using System.Reflection;
using System.Runtime.InteropServices;

using CustomAttributes;

static void ShowPluginInformation(string pluginFolder)
{
    var dir = new DirectoryInfo(pluginFolder);
    foreach (FileInfo file in dir.GetFiles("*.dll"))
    {
        Assembly pluginAssembly = Assembly.LoadFrom(file.FullName);
        var plugins =
             from type in pluginAssembly.ExportedTypes
             let info = type.GetCustomAttribute<PluginInformationAttribute>()
             where info != null
             select new { type, info };

        foreach (var plugin in plugins)
        {
            Console.WriteLine($"Typ wtyczki: {plugin.type.Name}");
            Console.WriteLine(
                $"Nazwa: {plugin.info.Name}, autor {plugin.info.Author}");
            Console.WriteLine("Opis: {plugin.info.Description}");            
        }
    }
}

// Dodajemy tu dwa różne rodzaje danych, więc by lepiej pokazać co się dziej
// czynności są wykonywane w odrębnych krokach
static void ShowPluginInformationMetadataLoadContext(FileInfo file)
{
    Type pluginAttributeType = typeof(PluginInformationAttribute);

    // Tworzymy listę ścieżek do podzespołów zawierającą podzespoły używane podczas wykonywania
    // oraz możliwe do sprawdzenia 
    string[] runtimeAssemblies = Directory.GetFiles(RuntimeEnvironment.GetRuntimeDirectory(), "*.dll");
    var paths = new List<string>(runtimeAssemblies);
    paths.Add(file.FullName);

    var resolver = new PathAssemblyResolver(paths);
    var mlc = new MetadataLoadContext(resolver);

    Assembly pluginAssembly = mlc.LoadFromAssemblyPath(file.FullName);
    var plugins =
         from type in pluginAssembly.ExportedTypes
         let info = type.GetCustomAttributesData().SingleOrDefault(attrData =>
               attrData.AttributeType.FullName == pluginAttributeType.FullName)
         where info != null
         let description = (CustomAttributeNamedArgument?)info.NamedArguments
                               .SingleOrDefault(a => a.MemberName == "Description")
         select new
         {
             type,
             Name = (string)info.ConstructorArguments[0].Value!,
             Author = (string)info.ConstructorArguments[1].Value!,
             Description = description?.TypedValue.Value
         };

    foreach (var plugin in plugins)
    {
        Console.WriteLine($"Typ wtyczki: {plugin.type.Name}");
        Console.WriteLine($"Nazwa: {plugin.Name}, autor {plugin.Author}");
        Console.WriteLine($"Opis: {plugin.Description}");
    }
}

Console.WriteLine("Normalne odzwierciedlanie");
ShowPluginInformation(Path.GetDirectoryName(typeof(Program).Assembly.Location)!);

Console.WriteLine();
Console.WriteLine("Odzwierciedlanie z użyciem MetadataLoadContext");
ShowPluginInformationMetadataLoadContext(new FileInfo(typeof(Program).Assembly.Location));

// Ten typ jest zdefiniowany w .NET. Pokazuję go tu tylko ze względów demonstracyjnych,
// dlatego został zapisany w bloku #if false
#if false
public interface ICustomAttributeProvider
{
    object[] GetCustomAttributes(bool inherit);
    object[] GetCustomAttributes(Type attributeType, bool inherit);
    bool IsDefined(Type attributeType, bool inherit);
}
#endif
