using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PSO2SERVER.Models
{
    /// <summary>
    /// Revealed minimap regions
    /// </summary>
    public class RevealedRegions
    {
        // 内部实现:
        // 该结构体由一个大小为[10]的字节数组组成，表示一个 8x10 的已揭示区域网格
        // （即 10 列 8 行）。每一位（bit）表示一个区域是否已揭示。
        // 位是从右到左索引的（即从 LSB 到 MSB），字节是从左到右索引的（arr[0]表示区域A1-8）。
        // 获取该数组中的某个值可以通过如下方式：
        // 
        // 示例代码：
        // bool GetBit(byte[] arr, int row, int col)
        // {
        //     // 确保行和列的范围有效
        //     if (row < 0 || row >= 8 || col < 0 || col >= 10)
        //         throw new ArgumentOutOfRangeException();
        //     
        //     // 计算对应位置的位偏移
        //     int offset = row * 10 + col;
        //     int byteOffset = offset / 8;
        //     int bitOffset = offset % 8;
        //     
        //     // 获取相应的位值
        //     return (arr[byteOffset] >> bitOffset & 1) == 1;
        // }

        // 使用一个长度为 10 的字节数组，表示 8 行 10 列的区域位图。
        // Lsb0 表示最低有效位在字节的最右侧。
        public byte[] zones = new byte[10];

        // 默认构造函数
        public RevealedRegions()
        {
        }

        // 构造函数 - 用指定数据初始化
        public RevealedRegions(byte[] data)
        {
            // 如果传入的 unk2 长度小于 10，则填充剩余部分为 0
            if (data.Length <= 10)
            {
                zones = data.Concat(new byte[10 - data.Length]).ToArray();
            }
            else
            {
                // 如果传入的 unk2 长度大于 10，则截取前 10 字节
                zones = data.Take(10).ToArray();
            }
        }

        /// <summary>
        /// 获取指定行列位置的位值
        /// </summary>
        /// <param name="row">行索引（0 到 7）</param>
        /// <param name="col">列索引（0 到 9）</param>
        /// <returns>该位置的位值（true = 已揭示，false = 未揭示）</returns>
        public bool GetBit(int row, int col)
        {
            // 确保行列值在有效范围内
            if (row < 0 || row >= 8 || col < 0 || col >= 10)
            {
                throw new ArgumentOutOfRangeException();
            }

            // 计算对应位置的偏移
            int offset = row * 10 + col;
            int byteOffset = offset / 8;
            int bitOffset = offset % 8;

            // 获取并返回该位置的位值
            return (zones[byteOffset] >> bitOffset & 1) == 1;
        }

        /// <summary>
        /// 设置指定行列位置的位值
        /// </summary>
        /// <param name="row">行索引（0 到 7）</param>
        /// <param name="col">列索引（0 到 9）</param>
        /// <param name="value">设置的值（true = 设置为已揭示，false = 设置为未揭示）</param>
        public void SetBit(int row, int col, bool value)
        {
            // 确保行列值在有效范围内
            if (row < 0 || row >= 8 || col < 0 || col >= 10)
            {
                throw new ArgumentOutOfRangeException();
            }

            // 计算对应位置的偏移
            int offset = row * 10 + col;
            int byteOffset = offset / 8;
            int bitOffset = offset % 8;

            // 设置对应位置的位值
            if (value)
            {
                // 设置为1
                zones[byteOffset] |= (byte)(1 << bitOffset);
            }
            else
            {
                // 设置为0
                zones[byteOffset] &= (byte)~(1 << bitOffset);
            }
        }
        // 读取数据
        public static RevealedRegions Read(BinaryReader reader)
        {
            try
            {
                byte[] data = reader.ReadBytes(10);
                if (data.Length != 10)
                    throw new InvalidDataException("Failed to read 10 bytes for zones data.");

                return new RevealedRegions(data);
            }
            catch (Exception ex)
            {
                throw new PacketError("RevealedRegions", "zone_bits", ex);
            }
        }

        // 写入数据
        public void Write(BinaryWriter writer)
        {
            try
            {
                writer.Write(zones);
            }
            catch (Exception ex)
            {
                throw new PacketError("RevealedRegions", "zone_bits", ex);
            }
        }

        // 索引操作，获取指定行的 10 位数据
        public BitSlice GetRow(int index)
        {
            if (index < 0 || index >= 8)
                throw new ArgumentOutOfRangeException();

            byte[] row = new byte[10];
            Array.Copy(zones, index * 10, row, 0, 10);
            return new BitSlice(row);
        }

        // 用于调试输出
        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("[");
            for (int i = 0; i < 8; i++)
            {
                sb.Append(GetRow(i).ToString());
                if (i < 7) sb.Append(", ");
            }
            sb.Append("]");
            return sb.ToString();
        }

        // Helper class to represent a slice of bits
        public class BitSlice
        {
            private byte[] data;

            public BitSlice(byte[] data)
            {
                this.data = data;
            }

            public override string ToString()
            {
                StringBuilder sb = new StringBuilder();
                foreach (byte b in data)
                {
                    sb.Append(Convert.ToString(b, 2).PadLeft(8, '0'));
                }
                return sb.ToString();
            }
        }

        // Error handling class for packet-related errors
        public class PacketError : Exception
        {
            public string PacketName { get; }
            public string FieldName { get; }
            public new Exception InnerException { get; }

            public PacketError(string packetName, string fieldName, Exception innerException)
                : base($"Error in packet '{packetName}', field '{fieldName}'", innerException)
            {
                PacketName = packetName;
                FieldName = fieldName;
                InnerException = innerException;
            }
        }
    }
}
