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

namespace Quik.Stb
{
    public unsafe static partial class Stbtt
    {
        private delegate void FailedAssertProc(byte *expression, byte *file, int line, byte *function);

        private static readonly string[] LibraryNames = new string[] 
        {
            //FIXME: This is wrong on so many levels, but, i need to do this
            // in order to get a change of this running.
            "runtimes/win-x64/native/libstbtt.dll",
            "runtimes/win-x86/native/libstbtt.dll",
            "runtimes/linux-arm/native/libstbtt.so",
            "runtimes/linux-arm64/native/libstbtt.so",
            "runtimes/linux-x64/native/libstbtt.so",
            "runtimes/native/libstbtt.dylib",
            "libstbtt.dll",
            "libstbtt.so",
            "libstbtt.dylib",
        };

        static Stbtt()
        {
            NativeLibrary.SetDllImportResolver(Assembly.GetExecutingAssembly(), Resolver);

            quik_stbtt_failed_assert_store(Marshal.GetFunctionPointerForDelegate<FailedAssertProc>(FailedAssert));
        }

        private static IntPtr Resolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)
        {
            if (libraryName != "stbtt")
                return IntPtr.Zero;

            foreach (string name in LibraryNames)
            {
                if (NativeLibrary.TryLoad(name, assembly, searchPath, out IntPtr handle))
                {
                    return handle;
                }
            }

            return NativeLibrary.Load(libraryName);
        }

        private static void FailedAssert(byte *expression, byte *file, int line, byte *function)
        {
            string expr = expression == null ? string.Empty : Marshal.PtrToStringUTF8((IntPtr)expression);
            string f    = file       == null ? string.Empty : Marshal.PtrToStringUTF8((IntPtr)file);
            string func = function   == null ? string.Empty : Marshal.PtrToStringUTF8((IntPtr)function);

            Exception ex =
                new Exception($"Assert failed in native stbtt code. ({System.IO.Path.GetFileName(f)}:{line})");
                ex.Data.Add("Expression", expr);
                ex.Data.Add("File", f);
                ex.Data.Add("Line", line);
                ex.Data.Add("Function", func);
            throw ex;
        }
    }
}