#nullable enable
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using T3.Core.Logging;
namespace T3.Core.Resource.Assets;
///
/// Helper class to convert extensions and file filters into unique ids for faster matching
///
///
/// then uses these IDs to store a list of supported extensions as int ids.
/// Maybe similar to a dynamic enum type. The unique Ids are generated while counting the
/// keys in a dictionary.
///
public static class FileExtensionRegistry
{
public static int GetUniqueId(string ext)
{
lock (_registryLock)
{
if (_map.TryGetValue(ext, out var id))
return id;
var newId = _next++;
_map[ext] = newId;
return newId;
}
}
public static bool TryGetExtensionIdForFilePath(string filepath, out int id)
{
id = -1;
try
{
var extension = Path.GetExtension(filepath);
if (extension.Length < 2)
return false;
// Dictionary reads are generally thread-safe if no writes occur,
// but since we write during startup, we lock for safety
lock (_registryLock)
{
return _map.TryGetValue(extension[1..], out id);
}
}
catch (Exception e)
{
Log.Warning($"Can't get extension from {filepath}: " + e.Message);
return false;
}
}
public static List IdsFromFileFilter(string filter)
{
var list = new List();
IdsFromFileFilter(filter, ref list);
return list;
}
public static bool TryGetExtensionForId(int id, out string foundExtension)
{
foreach (var (e, i) in _map)
{
if (i != id)
continue;
foundExtension = e;
return true;
}
foundExtension = string.Empty;
return false;
}
public static void IdsFromFileFilter(string filter, ref List ids)
{
ids.Clear();
var tokens = filter.Split('|'); // desc|pat|desc|pat|...
for (int i = 1; i < tokens.Length; i += 2)
{
var patterns = tokens[i].Split(';'); // *.png;*.jpg;*.tar.gz
foreach (var pat in patterns)
{
var ext = ExtractExtensionFromPattern(pat);
if (ext is null) continue;
ids.Add(GetUniqueId(ext));
}
}
}
// Returns normalized extension without leading dot, keeps composite (e.g. "tar.gz"), or null to skip.
private static string? ExtractExtensionFromPattern(string pattern)
{
if (string.IsNullOrWhiteSpace(pattern)) return null;
var s = pattern.Trim();
// Skip all-files patterns
if (s == "*" || s == "*.*") return null;
// "*.ext" or "*.tar.gz" → "ext" / "tar.gz"
if (s.Length >= 2 && s[0] == '*' && s[1] == '.')
return s[2..].Trim().TrimStart('.').ToLowerInvariant();
// Plain ".ext" or "ext" → "ext"
if (!s.Contains('*'))
return s.TrimStart('.').ToLowerInvariant();
// Anything with wildcards beyond the leading "*." is unsupported → skip
return null;
}
///
/// A helper method that generates a list of filter ids from an
/// extension set string (e.g. "mp4, mov, m4v")
///
public static List GetExtensionIdsFromExtensionSetString(string? filter)
{
if (string.IsNullOrWhiteSpace(filter)) return [];
var ids = new List();
var extensions = filter.Split(',', StringSplitOptions.RemoveEmptyEntries);
foreach (var ext in extensions)
{
var cleanExt = ext.Trim();
if (string.IsNullOrEmpty(cleanExt))
continue;
ids.Add(GetUniqueId(cleanExt));
}
return ids;
}
private static readonly Lock _registryLock = new();
private static readonly Dictionary _map = new(StringComparer.OrdinalIgnoreCase);
private static int _next;
}