using System;
using System.Collections.Generic; // Required for List<T>
using System.IO;                // For File operations
using System.Runtime.InteropServices; // Required for StructLayout and Marshal

namespace Zorro 
{
    /// <summary>
    /// Provides generic methods for loading and saving arrays of structures
    /// from/to binary files using marshalling.
    /// This class is intended for internal use within this assembly.
    /// </summary>
    internal static class BinaryFileHandler 
    {
        public static void SaveArrayAsBlock<T>(string filePath, T[] data) where T : struct
        {
            if (data == null || data.Length == 0)
            {
                File.WriteAllBytes(filePath, Array.Empty<byte>());
                return;
            }

            int structSize = Marshal.SizeOf(typeof(T));
            byte[] byteArray = new byte[data.Length * structSize];
            IntPtr buffer = Marshal.AllocHGlobal(structSize);
            try
            {
                for (int i = 0; i < data.Length; i++)
                {
                    Marshal.StructureToPtr(data[i], buffer, false);
                    Marshal.Copy(buffer, byteArray, i * structSize, structSize);
                }
                File.WriteAllBytes(filePath, byteArray);
            }
            finally
            {
                Marshal.FreeHGlobal(buffer);
            }
        }

        public static T[]? LoadArrayAsBlock<T>(string filePath) where T : struct 
        {
            if (!File.Exists(filePath))
            {
                return null; 
            }

            byte[] byteArray = File.ReadAllBytes(filePath);
            if (byteArray.Length == 0)
            {
                return Array.Empty<T>();
            }

            int structSize = Marshal.SizeOf(typeof(T));

            if (byteArray.Length % structSize != 0)
            {
                throw new ArgumentException($"File size ({byteArray.Length} bytes) is not a valid multiple of the structure size ({structSize} bytes) for type {typeof(T).Name}. The file may be corrupted or not in the expected format.");
            }

            int itemCount = byteArray.Length / structSize;
            T[] dataArray = new T[itemCount];
            IntPtr buffer = Marshal.AllocHGlobal(structSize);
            try
            {
                for (int i = 0; i < itemCount; i++)
                {
                    Marshal.Copy(byteArray, i * structSize, buffer, structSize);
                    dataArray[i] = Marshal.PtrToStructure<T>(buffer);
                }
            }
            finally
            {
                Marshal.FreeHGlobal(buffer);
            }
            return dataArray;
        }
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
    public struct ZorroT1
    {
        public double OADate; 
        public float Value;   

        /// <summary>
        /// Returns the time as a UTC DateTime object.
        /// Note: DateTime.FromOADate returns a DateTime with Kind=Unspecified.
        /// We specify it as UTC as Zorro DATEs are GMT/UTC.
        /// </summary>
        public DateTime TimeUtc => DateTime.SpecifyKind(DateTime.FromOADate(OADate), DateTimeKind.Utc);

        public ZorroT1(double oaDate, float value)
        {
            OADate = oaDate;
            Value = value;
        }
        public ZorroT1(DateTime dateTime, float value)
        {
            // Ensure the DateTime is converted to its UTC equivalent for OADate
            OADate = dateTime.ToUniversalTime().ToOADate();
            Value = value;
        }

        public static void SaveToFile(string filePath, ZorroT1[] data) => BinaryFileHandler.SaveArrayAsBlock<ZorroT1>(filePath, data); 
        public static ZorroT1[]? LoadFromFile(string filePath) => BinaryFileHandler.LoadArrayAsBlock<ZorroT1>(filePath); 
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
    public struct ZorroT2
    {
        public double OADate; 
        public float Price;  
        public float Volume; 

        public DateTime TimeUtc => DateTime.SpecifyKind(DateTime.FromOADate(OADate), DateTimeKind.Utc);

        public ZorroT2(double oaDate, float price, float volume)
        {
            OADate = oaDate;
            Price = price;
            Volume = volume;
        }
        public ZorroT2(DateTime dateTime, float price, float volume)
        {
            OADate = dateTime.ToUniversalTime().ToOADate();
            Price = price;
            Volume = volume;
        }

        public static void SaveToFile(string filePath, ZorroT2[] data) => BinaryFileHandler.SaveArrayAsBlock<ZorroT2>(filePath, data); 
        public static ZorroT2[]? LoadFromFile(string filePath) => BinaryFileHandler.LoadArrayAsBlock<ZorroT2>(filePath); 
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
    public struct ZorroT6
    {
        public double OADate;           
        public float High;
        public float Low;
        public float Open;
        public float Close;
        public float AdditionalValue1; 
        public float AdditionalValue2; 

        public DateTime TimeUtc => DateTime.SpecifyKind(DateTime.FromOADate(OADate), DateTimeKind.Utc);
        
        public ZorroT6(double oaDate, float high, float low, float open, float close, float additionalValue1, float additionalValue2)
        {
            OADate = oaDate;
            High = high;
            Low = low;
            Open = open;
            Close = close;
            AdditionalValue1 = additionalValue1;
            AdditionalValue2 = additionalValue2;
        }
        public ZorroT6(DateTime dateTime, float high, float low, float open, float close, float additionalValue1, float additionalValue2)
        {
            OADate = dateTime.ToUniversalTime().ToOADate();
            High = high;
            Low = low;
            Open = open;
            Close = close;
            AdditionalValue1 = additionalValue1;
            AdditionalValue2 = additionalValue2;
        }

        public static void SaveToFile(string filePath, ZorroT6[] data) => BinaryFileHandler.SaveArrayAsBlock<ZorroT6>(filePath, data); 
        public static ZorroT6[]? LoadFromFile(string filePath) => BinaryFileHandler.LoadArrayAsBlock<ZorroT6>(filePath); 
    }

    // --- Builder Classes for Incremental Array Construction ---

    public class ZorroT1ArrayBuilder
    {
        private readonly List<ZorroT1> _items;

        public ZorroT1ArrayBuilder(int initialCapacity = 0)
        {
            _items = initialCapacity > 0 ? new List<ZorroT1>(initialCapacity) : new List<ZorroT1>();
        }

        public ZorroT1ArrayBuilder Add(DateTime time, float value)
        {
            // Constructor of ZorroT1 now handles UTC conversion
            _items.Add(new ZorroT1(time, value));
            return this; 
        }

        public ZorroT1ArrayBuilder Add(double oaDate, float value)
        {
            _items.Add(new ZorroT1(oaDate, value));
            return this;
        }
        
        public ZorroT1ArrayBuilder Add(ZorroT1 item)
        {
            _items.Add(item);
            return this;
        }

        public ZorroT1[] ToArray() => _items.ToArray();
        public void Clear() => _items.Clear();
        public int Count => _items.Count;

        public void SaveToFile(string filePath)
        {
            ZorroT1.SaveToFile(filePath, _items.ToArray());
        }
    }

    public class ZorroT2ArrayBuilder
    {
        private readonly List<ZorroT2> _items;
        
        public ZorroT2ArrayBuilder(int initialCapacity = 0)
        {
             _items = initialCapacity > 0 ? new List<ZorroT2>(initialCapacity) : new List<ZorroT2>();
        }

        public ZorroT2ArrayBuilder Add(DateTime time, float price, float volume)
        {
            _items.Add(new ZorroT2(time, price, volume));
            return this;
        }

        public ZorroT2ArrayBuilder Add(double oaDate, float price, float volume)
        {
            _items.Add(new ZorroT2(oaDate, price, volume));
            return this;
        }

        public ZorroT2ArrayBuilder Add(ZorroT2 item)
        {
            _items.Add(item);
            return this;
        }

        public ZorroT2[] ToArray() => _items.ToArray();
        public void Clear() => _items.Clear();
        public int Count => _items.Count;

        public void SaveToFile(string filePath)
        {
            ZorroT2.SaveToFile(filePath, _items.ToArray());
        }
    }

    public class ZorroT6ArrayBuilder
    {
        private readonly List<ZorroT6> _items;

        public ZorroT6ArrayBuilder(int initialCapacity = 0)
        {
            _items = initialCapacity > 0 ? new List<ZorroT6>(initialCapacity) : new List<ZorroT6>();
        }

        public ZorroT6ArrayBuilder Add(DateTime time, float high, float low, float open, float close, float additionalValue1, float additionalValue2)
        {
            _items.Add(new ZorroT6(time, high, low, open, close, additionalValue1, additionalValue2));
            return this;
        }

        public ZorroT6ArrayBuilder Add(double oaDate, float high, float low, float open, float close, float additionalValue1, float additionalValue2)
        {
            _items.Add(new ZorroT6(oaDate, high, low, open, close, additionalValue1, additionalValue2));
            return this;
        }

        public ZorroT6ArrayBuilder Add(ZorroT6 item)
        {
            _items.Add(item);
            return this;
        }

        public ZorroT6[] ToArray() => _items.ToArray();
        public void Clear() => _items.Clear();
        public int Count => _items.Count;

        public void SaveToFile(string filePath)
        {
            ZorroT6.SaveToFile(filePath, _items.ToArray());
        }
    }
}