using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;

namespace hive
{

#if UNITY_STANDALONE_WIN
    /// <summary>디렉터리 목록에서 DLL을 찾아 절대 경로를 돌려주는 Finder.</summary>
    internal static class HiveDllFinder
    {
        /// <param name="dllName">예: "MyPlugin.dll"</param>
        /// <param name="searchDirs">탐색할 폴더 집합(순서 중요)</param>
        /// <returns>존재 시 절대 경로, 없으면 <c>null</c></returns>
        public static string Find(string relativePath, IEnumerable<string> roots)
        {
            foreach (var root in roots.Where(Directory.Exists))
            {
                var full = Path.Combine(root, relativePath);
                if (File.Exists(full))
                    return Path.GetFullPath(full);
            }
            return null;
        }
    }

    internal static class HiveDllLoader
    {
        const uint LOAD_LIBRARY_SEARCH_USER_DIRS = 0x00000400;
		const uint LOAD_LIBRARY_SEARCH_DEFAULT_DIRS = 0x00001000;


        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        static extern IntPtr LoadLibraryExW(string lpFileName, IntPtr hFile, uint flags);

        [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
        static extern IntPtr GetModuleHandleW(string lpModuleName);

        [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
        static extern IntPtr AddDllDirectory(string newDirectory);

        public static IntPtr Load(string relativePath, IEnumerable<string> searchDirs)
        {
            // alreadt loaded?
            var dllFile = Path.GetFileName(relativePath);
            IntPtr h = GetModuleHandleW(dllFile);
            if (h != IntPtr.Zero) return h;

            // find absolute path
            string abs = HiveDllFinder.Find(relativePath, searchDirs)
                     ?? throw new DllNotFoundException(relativePath);

            // add directories to search path
            AddDllDirectory(Path.GetDirectoryName(abs)!);

            h = LoadLibraryExW(abs, IntPtr.Zero, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS | LOAD_LIBRARY_SEARCH_USER_DIRS);
            if (h == IntPtr.Zero)
                throw new DllNotFoundException(
                    $"{abs} (Win32Err {Marshal.GetLastWin32Error()})");
            return h;
        }
    }
#endif
}
