using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using Decal.Interop.Core;
using Microsoft.Win32;

namespace Decal.Core
{
    [ComVisible(true)]
    [Guid("6DE65A82-C451-46E6-A82D-92137BE851AD")]
    [ClassInterface(ClassInterfaceType.None)]
    [ProgId("Decal.DecalEnum")]
    public class DecalEnumImpl : IDecalEnum
    {
        private class EnumEntry
        {
            public string FriendlyName = "";
            public Guid ComClass;
            public bool Enabled = true;
            public bool Restricted;
            public Guid SurrogateClass;
            public string ResourcePath = "";
            public string FilePath = "";
            public string Version = "";
            public int Order;
        }

        private readonly List<EnumEntry> _entries = new List<EnumEntry>();
        private int _index = -1;
        private string _group = "";

        private EnumEntry Current => (_index >= 0 && _index < _entries.Count) ? _entries[_index] : null;

        public string FriendlyName => Current?.FriendlyName ?? "";
        public Guid ComClass => Current?.ComClass ?? Guid.Empty;

        public bool Enabled
        {
            get => Current?.Enabled ?? false;
            set
            {
                var c = Current;
                if (c == null) return;
                c.Enabled = value;
                try
                {
                    using (var key = Registry.LocalMachine.OpenSubKey(
                        $@"SOFTWARE\Decal\{_group}\{c.ComClass:B}", true))
                    {
                        key?.SetValue("Enabled", value ? 1 : 0, RegistryValueKind.DWord);
                    }
                }
                catch { }
            }
        }

        public bool Restricted => Current?.Restricted ?? false;

        public object CreateInstance(ref Guid riid)
        {
            var entry = Current;
            if (entry == null) return null;

            // If there's a surrogate, use it
            if (entry.SurrogateClass != Guid.Empty)
            {
                var surrogateType = Type.GetTypeFromCLSID(entry.SurrogateClass);
                if (surrogateType != null)
                {
                    var surrogate = Activator.CreateInstance(surrogateType) as IDecalSurrogate;
                    if (surrogate != null)
                        return surrogate.CreateInstance((DecalEnum)(object)this, ref riid);
                }
            }

            // Direct COM creation
            var type = Type.GetTypeFromCLSID(entry.ComClass);
            return type != null ? Activator.CreateInstance(type) : null;
        }

        public void Next()
        {
            _index++;
            if (_index >= _entries.Count)
                Marshal.ThrowExceptionForHR(1); // S_FALSE
        }

        public Guid SurrogateClass => Current?.SurrogateClass ?? Guid.Empty;
        public string ResourcePath => Current?.ResourcePath ?? "";
        public object Property => null; // Parameterized — returns registry values by name
        public string Group => _group;

        public void Skip(ref Guid clsid)
        {
            for (int i = 0; i < _entries.Count; i++)
            {
                if (_entries[i].ComClass == clsid)
                {
                    _index = i;
                    return;
                }
            }
        }

        public void MoveBefore(ref Guid clsidBefore)
        {
            if (_index < 0 || _index >= _entries.Count) return;
            var current = _entries[_index];
            _entries.RemoveAt(_index);

            int targetIdx = _entries.Count;
            for (int i = 0; i < _entries.Count; i++)
            {
                if (_entries[i].ComClass == clsidBefore)
                {
                    targetIdx = i;
                    break;
                }
            }
            _entries.Insert(targetIdx, current);
            _index = targetIdx;
        }

        public string Version => Current?.Version ?? "";

        public bool FileExists
        {
            get
            {
                var path = Current?.FilePath;
                return !string.IsNullOrEmpty(path) && File.Exists(path);
            }
        }

        public string FilePath => Current?.FilePath ?? "";

        internal void Initialize(string group)
        {
            _group = group;
            _entries.Clear();
            _index = -1;

            try
            {
                using (var baseKey = Registry.LocalMachine.OpenSubKey($@"SOFTWARE\Decal\{group}"))
                {
                    if (baseKey == null) return;

                    string orderStr = baseKey.GetValue("Order") as string ?? "";

                    foreach (string subKeyName in baseKey.GetSubKeyNames())
                    {
                        if (!Guid.TryParse(subKeyName, out var clsid))
                            continue;

                        using (var subKey = baseKey.OpenSubKey(subKeyName))
                        {
                            if (subKey == null) continue;

                            var entry = new EnumEntry
                            {
                                FriendlyName = subKey.GetValue(null) as string ?? subKeyName,
                                ComClass = clsid,
                                Enabled = ((int)(subKey.GetValue("Enabled") ?? 1)) != 0,
                                Restricted = ((int)(subKey.GetValue("Restricted") ?? 0)) != 0,
                                ResourcePath = subKey.GetValue("ResourcePath") as string ?? "",
                                FilePath = subKey.GetValue("File") as string ?? "",
                                Version = subKey.GetValue("Version") as string ?? "",
                            };

                            var surrogateStr = subKey.GetValue("Surrogate") as string;
                            if (!string.IsNullOrEmpty(surrogateStr) && Guid.TryParse(surrogateStr, out var surr))
                                entry.SurrogateClass = surr;

                            var orderVal = subKey.GetValue("Order") as string;
                            if (!string.IsNullOrEmpty(orderVal) && orderVal.Length > 0)
                            {
                                int pos = orderStr.IndexOf(orderVal[0]);
                                entry.Order = pos >= 0 ? pos : int.MaxValue;
                            }
                            else
                            {
                                entry.Order = int.MaxValue;
                            }

                            _entries.Add(entry);
                        }
                    }
                }

                _entries.Sort((a, b) => a.Order.CompareTo(b.Order));
            }
            catch { }
        }

        internal int Count => _entries.Count;
    }
}
