using System;
using System.Globalization;
using System.Runtime.InteropServices;
using Decal.Interop.Core;
using Decal.Interop.Dat;

namespace Decal.DecalDat
{
    internal enum LibraryType
    {
        Cell,
        Portal
    }

    [ComVisible(true)]
    [Guid("6FA05FDA-B4B5-4386-AB45-92D7E6A5D698")]
    [ClassInterface(ClassInterfaceType.None)]
    [ProgId("DecalDat.DatLibrary")]
    public class DatLibraryImpl : IDatLibrary, IDecalDirectory
    {
        private DatFile _datFile;
        private LibraryType _libraryType;
        private DatServiceImpl _service;

        internal void Load(DatServiceImpl service, string filename, LibraryType libraryType, int sectorSize)
        {
            _service = service;
            _libraryType = libraryType;
            try
            {
                _datFile = new DatFile(filename, sectorSize);
            }
            catch
            {
                // Silent failure matching original C++ behavior
                _datFile = null;
            }
        }

        /// <summary>
        /// IDatLibrary.Stream - parameterized property returning raw stream for a file ID.
        /// The COM interface declares this as a parameterized property (DispId 1) taking a DWORD File parameter.
        /// In the C++ implementation, get_Stream(DWORD dwFile, LPUNKNOWN *pVal) creates a DatStream directly.
        /// </summary>
        public object Stream
        {
            get
            {
                // This parameterized property is called via IDispatch with the file ID argument.
                // When called without parameters from managed code, return null.
                return null;
            }
        }

        internal object GetStream(uint fileId)
        {
            if (_datFile == null) return null;

            try
            {
                var fileEntry = _datFile.GetFile(fileId);
                var stream = new DatStreamImpl();
                stream.Load(fileEntry);
                return stream;
            }
            catch
            {
                return null;
            }
        }

        public object Open(string Protocol, uint File)
        {
            if (_datFile == null) return null;

            // Look up filter by protocol name
            var filter = _service.GetFilter(Protocol);

            if (filter != null)
            {
                // Check cache first
                var cached = _service.FindInCache(filter, _libraryType, File);
                if (cached != null) return cached;

                // Create raw stream
                DatFileEntry fileEntry;
                try
                {
                    fileEntry = _datFile.GetFile(File);
                }
                catch
                {
                    return null;
                }

                var stream = new DatStreamImpl();
                stream.Load(fileEntry);

                // Create and initialize filter
                var filterObj = _service.CreateFilter(filter);
                if (filterObj == null) return null;

                var fileFilter = filterObj as IFileFilter;
                if (fileFilter != null)
                {
                    fileFilter.Initialize((DatStream)(object)stream);
                }

                // Cache if applicable
                if (filter.Cache)
                {
                    _service.AddToCache(filter, _libraryType, File, filterObj);
                }

                return filterObj;
            }

            // No filter - return raw stream
            return GetStream(File);
        }

        /// <summary>
        /// IDecalDirectory.Lookup - parses "PROTOCOL:HEXID" or just "HEXID" format.
        /// </summary>
        public object Lookup(string strName)
        {
            if (string.IsNullOrEmpty(strName)) return null;

            int colonIdx = strName.IndexOf(':');
            if (colonIdx >= 0)
            {
                // Format: "protocol:0xHEXID"
                string protocol = strName.Substring(0, colonIdx);
                string hexPart = strName.Substring(colonIdx + 1).Trim();
                uint fileId = ParseHex(hexPart);
                return Open(protocol, fileId);
            }
            else
            {
                // Format: "0xHEXID" - raw stream
                uint fileId = ParseHex(strName.Trim());
                return GetStream(fileId);
            }
        }

        private static uint ParseHex(string hex)
        {
            if (hex.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
                hex = hex.Substring(2);
            return uint.Parse(hex, NumberStyles.HexNumber);
        }

        internal void Dispose()
        {
            _datFile?.Dispose();
            _datFile = null;
        }
    }
}
