using System.Diagnostics; using System.Drawing; using System.Drawing.Imaging; using System.Globalization; using System.Runtime.InteropServices; using System.Text; namespace View_by_Distance.Shared.Models.Stateless.Methods; internal abstract class Property { internal static (int Season, string seasonName) GetSeason(int dayOfYear) { (int Season, string seasonName) result = dayOfYear switch { < 78 => new(0, "Winter"), < 171 => new(1, "Spring"), < 264 => new(2, "Summer"), < 354 => new(3, "Fall"), _ => new(4, "Winter") }; return result; } internal static int GetDeterministicHashCode(byte[] value) { int result; unchecked { int hash1 = (5381 << 16) + 5381; int hash2 = hash1; for (int i = 0; i < value.Length; i += 2) { hash1 = ((hash1 << 5) + hash1) ^ value[i]; if (i == value.Length - 1) break; hash2 = ((hash2 << 5) + hash2) ^ value[i + 1]; } result = hash1 + (hash2 * 1566083941); } return result; } internal static int GetDeterministicHashCode(string value) { int result; unchecked { int hash1 = (5381 << 16) + 5381; int hash2 = hash1; for (int i = 0; i < value.Length; i += 2) { hash1 = ((hash1 << 5) + hash1) ^ value[i]; if (i == value.Length - 1) break; hash2 = ((hash2 << 5) + hash2) ^ value[i + 1]; } result = hash1 + (hash2 * 1566083941); } return result; } internal static (bool?, string[]) IsWrongYear(string[] segments, string year) { bool? result; string[] results = ( from l in segments where l?.Length > 2 && ( l[..2] is "18" or "19" or "20" || (l.Length == 5 && l.Substring(1, 2) is "18" or "19" or "20" && (l[0] is '~' or '=' or '-' or '^' or '#')) || (l.Length == 6 && l[..2] is "18" or "19" or "20" && l[4] == '.') || (l.Length == 7 && l.Substring(1, 2) is "18" or "19" or "20" && l[5] == '.') ) select l ).ToArray(); string[] matches = ( from l in results where l == year || (l.Length == 5 && l.Substring(1, 4) == year && (l[0] is '~' or '=' or '-' or '^' or '#')) || (l.Length == 6 && l[..4] == year && l[4] == '.') || (l.Length == 7 && l.Substring(1, 4) == year && l[5] == '.') select l ).ToArray(); if (!results.Any()) result = null; else result = !matches.Any(); return new(result, results); } internal static DateTime? GetDateTimeFromName(Models.FileHolder fileHolder) { DateTime? result = null; int length; string format; string fullFormat; StringBuilder value = new(); const string ticksExample = "##################"; string[][] dateFormats = new string[][] { new string[] { string.Empty, "yyyyMMdd_HHmmss", string.Empty }, new string[] { string.Empty, "yyyyMMddHHmmssfff", string.Empty }, new string[] { string.Empty, "yyyyMMdd_", ticksExample }, new string[] { string.Empty, "yyyy-MM-dd_", ticksExample }, new string[] { string.Empty, "yyyy-MM-dd.", ticksExample }, new string[] { string.Empty, "yyyy-MM-dd.", $"{ticksExample}.{fileHolder.Length}" }, new string[] { string.Empty, "yyyy-MM-dd HH.mm.ss", string.Empty }, new string[] { string.Empty, "yyyyMMdd_HHmmss", "_LLS" }, new string[] { string.Empty, "yyyyMMdd_HHmmss", "_HDR" }, new string[] { "WIN_", "yyyyMMdd_HH_mm_ss", "_Pro" }, new string[] { "IMG_", "yyyyMMdd_HHmmss", string.Empty }, new string[] { "IMG#####-", "yyyyMMdd-HHmm", string.Empty }, new string[] { "CameraZOOM-", "yyyyMMddHHmmss", string.Empty }, new string[] { "VideoCapture_", "yyyyMMdd-HHmmss ", string.Empty } }; foreach (string[] dateFormat in dateFormats) { _ = value.Clear(); if (dateFormat.Length != 3) throw new Exception(); fullFormat = string.Join(string.Empty, dateFormat); if (fileHolder.NameWithoutExtension.Length != fullFormat.Length) continue; format = dateFormat[1]; length = dateFormat[0].Length + dateFormat[1].Length; for (int i = dateFormat[0].Length; i < length; i++) _ = value.Append(fileHolder.NameWithoutExtension[i]); if (value.Length != format.Length) continue; if (DateTime.TryParseExact(value.ToString(), format, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime checkDateTime)) { if (fileHolder.NameWithoutExtension.Length < ticksExample.Length || !long.TryParse(fileHolder.NameWithoutExtension[^ticksExample.Length..], out long ticks)) result = checkDateTime; else result = new DateTime(ticks); break; } } return result; } internal static List GetDateTimes(DateTime creationTime, DateTime lastWriteTime, DateTime? dateTime, DateTime? dateTimeDigitized, DateTime? dateTimeFromName, DateTime? dateTimeOriginal, DateTime? gpsDateStamp) { List results = new() { creationTime, lastWriteTime }; if (dateTime.HasValue) results.Add(dateTime.Value); if (dateTimeDigitized.HasValue) results.Add(dateTimeDigitized.Value); if (dateTimeFromName.HasValue) results.Add(dateTimeFromName.Value); if (dateTimeOriginal.HasValue) results.Add(dateTimeOriginal.Value); if (gpsDateStamp.HasValue) results.Add(gpsDateStamp.Value); return results; } internal static DateTime GetDateTime(Models.Property? property) { DateTime result; if (property is null) result = DateTime.MinValue; else { List dateTimes = new() { property.CreationTime, property.LastWriteTime }; if (property.DateTime.HasValue) dateTimes.Add(property.DateTime.Value); if (property.DateTimeDigitized.HasValue) dateTimes.Add(property.DateTimeDigitized.Value); if (property.DateTimeFromName.HasValue) dateTimes.Add(property.DateTimeFromName.Value); if (property.DateTimeOriginal.HasValue) dateTimes.Add(property.DateTimeOriginal.Value); if (property.GPSDateStamp.HasValue) dateTimes.Add(property.GPSDateStamp.Value); result = dateTimes.Min(); } return result; } internal static DateTime GetMinimumDateTime(Models.Property? property) { DateTime result; List dateTimes; if (property is null) result = DateTime.MinValue; else { dateTimes = IProperty.GetDateTimes(property); result = dateTimes.Min(); } return result; } internal static string GetDiffRootDirectory(string diffPropertyDirectory) { string result = string.Empty; string results = "-Results"; string? checkDirectory = diffPropertyDirectory; for (int i = 0; i < int.MaxValue; i++) { checkDirectory = Path.GetDirectoryName(checkDirectory); if (string.IsNullOrEmpty(checkDirectory)) break; if (checkDirectory.EndsWith(results)) { result = checkDirectory[..^results.Length]; break; } } return result; } internal static double GetStandardDeviation(IEnumerable values, double average) { double result = 0; if (!values.Any()) throw new Exception("Collection must have at least one value!"); double sum = values.Sum(l => (l - average) * (l - average)); result = Math.Sqrt(sum / values.Count()); return result; } private static long GetThreeStandardDeviationHigh(ref List ticksCollection, long min) { long result; ticksCollection = (from l in ticksCollection select l - min).ToList(); double sum = ticksCollection.Sum(); double average = sum / ticksCollection.Count; double standardDeviation = GetStandardDeviation(ticksCollection, average); result = (long)Math.Ceiling(average + min + (standardDeviation * 3)); return result; } internal static TimeSpan GetThreeStandardDeviationHigh(int minimum, Models.Container container) { TimeSpan result; DateTime? minimumDateTime; List ticksCollection = new(); foreach (Models.Item item in container.Items) { if (item.Property is null) continue; minimumDateTime = GetMinimumDateTime(item.Property); if (minimumDateTime is null) continue; ticksCollection.Add(minimumDateTime.Value.Ticks); } long threeStandardDeviationHigh; long min; if (!ticksCollection.Any()) min = 0; else min = ticksCollection.Min(); if (ticksCollection.Count < minimum) threeStandardDeviationHigh = long.MaxValue; else threeStandardDeviationHigh = GetThreeStandardDeviationHigh(ref ticksCollection, min); result = new TimeSpan(threeStandardDeviationHigh - min); return result; } internal static (int, List, List) Get(Models.Container container, TimeSpan threeStandardDeviationHigh, int i) { List results = new(); int j = i; long? ticks; Models.Item item; TimeSpan timeSpan; Models.Item nextItem; DateTime? minimumDateTime; DateTime? nextMinimumDateTime; List dateTimes = new(); for (; j < container.Items.Count; j++) { ticks = null; item = container.Items[j]; if (item.Property is null) continue; minimumDateTime = GetMinimumDateTime(item.Property); if (minimumDateTime is null) continue; for (int k = j + 1; k < container.Items.Count; k++) { nextItem = container.Items[k]; if (nextItem.Property is null) continue; nextMinimumDateTime = GetMinimumDateTime(nextItem.Property); if (nextMinimumDateTime is null) continue; ticks = nextMinimumDateTime.Value.Ticks; break; } results.Add(item); dateTimes.Add(minimumDateTime.Value); if (ticks.HasValue) { timeSpan = new(ticks.Value - minimumDateTime.Value.Ticks); if (timeSpan > threeStandardDeviationHigh) break; } } return new(j, dateTimes, results); } internal static bool Any(Models.Container[] containers) { bool result = false; foreach (Models.Container container in containers) { if (!container.Items.Any()) continue; if ((from l in container.Items where l.Any() select true).Any()) { result = true; break; } } return result; } internal static bool NameWithoutExtensionIsIdFormat(Models.FileHolder fileHolder) { bool result; if (fileHolder.NameWithoutExtension.Length < 5) result = false; else { bool skipOneAllAreNumbers = fileHolder.NameWithoutExtension[1..].All(l => char.IsNumber(l)); result = (skipOneAllAreNumbers && fileHolder.NameWithoutExtension[0] == '-') || (skipOneAllAreNumbers && char.IsNumber(fileHolder.NameWithoutExtension[0])); } return result; } #pragma warning disable CA1416 internal static (DateTime?[], int?, string?) Get(Models.FileHolder fileHolder, bool isIgnoreExtension, bool isValidImageFormatExtension) { int? id = null; string? message = null; DateTime? dateTime = null; DateTime? gpsDateStamp = null; DateTime? dateTimeOriginal = null; DateTime? dateTimeDigitized = null; if (!isIgnoreExtension && isValidImageFormatExtension) { try { byte[] bytes; string value; DateTime checkDateTime; PropertyItem? propertyItem; ASCIIEncoding asciiEncoding = new(); string dateTimeFormat = IProperty.DateTimeFormat(); using Image image = Image.FromFile(fileHolder.FullName); using Bitmap bitmap = new(image); Rectangle rectangle = new(0, 0, image.Width, image.Height); BitmapData bitmapData = bitmap.LockBits(rectangle, ImageLockMode.ReadOnly, bitmap.PixelFormat); IntPtr intPtr = bitmapData.Scan0; int length = bitmapData.Stride * bitmap.Height; bytes = new byte[length]; Marshal.Copy(intPtr, bytes, 0, length); bitmap.UnlockBits(bitmapData); id = IProperty.GetDeterministicHashCode(bytes); bitmap.Dispose(); if (image.PropertyIdList.Contains((int)IExif.Tags.DateTime)) { propertyItem = image.GetPropertyItem((int)IExif.Tags.DateTime); if (propertyItem?.Value is not null) { value = asciiEncoding.GetString(propertyItem.Value, 0, propertyItem.Len - 1); if (value.Length > dateTimeFormat.Length) value = value[..dateTimeFormat.Length]; if (value.Length == dateTimeFormat.Length && DateTime.TryParseExact(value, dateTimeFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out checkDateTime)) dateTime = checkDateTime; } } if (image.PropertyIdList.Contains((int)IExif.Tags.DateTimeDigitized)) { propertyItem = image.GetPropertyItem((int)IExif.Tags.DateTimeDigitized); if (propertyItem?.Value is not null) { value = asciiEncoding.GetString(propertyItem.Value, 0, propertyItem.Len - 1); if (value.Length > dateTimeFormat.Length) value = value[..dateTimeFormat.Length]; if (value.Length == dateTimeFormat.Length && DateTime.TryParseExact(value, dateTimeFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out checkDateTime)) dateTimeDigitized = checkDateTime; } } if (image.PropertyIdList.Contains((int)IExif.Tags.DateTimeOriginal)) { propertyItem = image.GetPropertyItem((int)IExif.Tags.DateTimeOriginal); if (propertyItem?.Value is not null) { value = asciiEncoding.GetString(propertyItem.Value, 0, propertyItem.Len - 1); if (value.Length > dateTimeFormat.Length) value = value[..dateTimeFormat.Length]; if (value.Length == dateTimeFormat.Length && DateTime.TryParseExact(value, dateTimeFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out checkDateTime)) dateTimeOriginal = checkDateTime; } } if (image.PropertyIdList.Contains((int)IExif.Tags.GPSDateStamp)) { propertyItem = image.GetPropertyItem((int)IExif.Tags.GPSDateStamp); if (propertyItem?.Value is not null) { value = asciiEncoding.GetString(propertyItem.Value, 0, propertyItem.Len - 1); if (value.Length > dateTimeFormat.Length) value = value[..dateTimeFormat.Length]; if (value.Length == dateTimeFormat.Length && DateTime.TryParseExact(value, dateTimeFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out checkDateTime)) gpsDateStamp = checkDateTime; } } image.Dispose(); } catch (Exception) { message = string.Concat(new StackFrame().GetMethod()?.Name, " <", fileHolder.FullName, ">"); } } DateTime?[] dateTimes = new DateTime?[] { fileHolder.LastWriteTime, fileHolder.CreationTime, dateTime, dateTimeDigitized, dateTimeOriginal, gpsDateStamp }; return new(dateTimes, id, message); } #pragma warning restore CA1416 }