using System.Net;
using DotNext;

namespace Peripherals.I2cClient;

static class I2cAddr
{

    const UInt32 Base = 0x6000_0000;

    /// <summary>
    /// 0x0000_0000:
    /// [7:0] 本次传输的i2c地址(最高位总为0);
    /// [8] 1为读，0为写;
    /// [16] 1为SCCB协议，0为I2C协议;
    /// [24] 1为开启本次传输，自动置零
    /// </summary>
    public const UInt32 BaseConfig = Base + 0x0000_0000;

    /// <summary>
    /// 0x0000_0001:
    /// [15:0] 本次传输的数据量（以字节为单位，0为传1个字节）;
    /// [31:16] 若本次传输为读的DUMMY数据量（字节为单位，0为传1个字节）
    /// </summary>
    public const UInt32 TranConfig = Base + 0x0000_0001;

    /// <summary>
    /// 0x0000_0002: [0] cmd_done; [8] cmd_error;
    /// </summary>
    public const UInt32 Flag = Base + 0x0000_0002;

    /// <summary>
    /// 0x0000_0003: FIFO写入口，仅低8位有效，只写
    /// </summary>
    public const UInt32 Write = Base + 0x0000_0003;

    /// <summary>
    /// 0x0000_0004: FIFO读出口，仅低8位有效，只读
    /// </summary>
    public const UInt32 Read = Base + 0x0000_0004;

    /// <summary>
    /// 0x0000_0005: [0] FIFO写入口清空；[8] FIFO读出口清空；
    /// </summary>
    public const UInt32 Clear = Base + 0x0000_0005;
}

/// <summary>
/// [TODO:Enum]
/// </summary>
public enum I2cProtocol
{
    /// <summary>
    /// [TODO:Enum]
    /// </summary>
    I2c = 0,

    /// <summary>
    /// [TODO:Enum]
    /// </summary>
    SCCB = 1
}

/// <summary>
/// [TODO:description]
/// </summary>
public class I2c
{
    private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();

    readonly int timeout = 2000;
    readonly int taskID;
    readonly int port;
    readonly string address;
    private IPEndPoint ep;

    /// <summary>
    /// [TODO:description]
    /// </summary>
    /// <param name="address">[TODO:parameter]</param>
    /// <param name="port">[TODO:parameter]</param>
    /// <param name="taskID">[TODO:parameter]</param>
    /// <param name="timeout">[TODO:parameter]</param>
    /// <returns>[TODO:return]</returns>
    public I2c(string address, int port, int taskID, int timeout = 2000)
    {
        if (timeout < 0)
            throw new ArgumentException("Timeout couldn't be negative", nameof(timeout));
        this.address = address;
        this.taskID = taskID;
        this.port = port;
        this.ep = new IPEndPoint(IPAddress.Parse(address), port);
        this.timeout = timeout;
    }

    /// <summary>
    /// 向指定I2C设备写入数据
    /// </summary>
    /// <param name="devAddr">I2C设备地址</param>
    /// <param name="data">要写入的数据</param>
    /// <param name="proto">I2C协议类型</param>
    /// <returns>操作结果，成功返回true，否则返回异常信息</returns>
    public async ValueTask<Result<bool>> WriteData(UInt32 devAddr, byte[] data, I2cProtocol proto)
    {
        if (data.Length > 0x0000_FFFF)
        {
            logger.Error($"Data length {data.Length} exceeds maximum allowed 0x0000_FFFF");
            return new(new ArgumentException($"Data length {data.Length} exceeds maximum allowed 0x0000_FFFF"));
        }

        // 清除UDP服务器接收缓冲区
        MsgBus.UDPServer.ClearUDPData(this.address, this.taskID);

        logger.Trace($"Clear up udp server {this.address} receive data");

        // 写入数据到I2C FIFO写入口
        {
            var i2cData = new byte[data.Length * 4];
            int i = 0;
            foreach (var item in data)
            {
                i2cData[i++] = 0x00;
                i2cData[i++] = 0x00;
                i2cData[i++] = 0x00;
                i2cData[i++] = item;
            }

            var ret = await UDPClientPool.WriteAddr(this.ep, this.taskID, I2cAddr.Write, i2cData);
            if (!ret.IsSuccessful)
            {
                logger.Error($"Failed to write data to I2C FIFO: {ret.Error}");
                return new(ret.Error);
            }

            if (!ret.Value)
            {
                logger.Error("WriteAddr to I2C FIFO returned false");
                return new(new Exception("Failed to write data to I2C FIFO"));
            }
        }

        // 配置本次传输数据量
        {
            var ret = await UDPClientPool.WriteAddr(this.ep, this.taskID, I2cAddr.TranConfig, ((uint)(data.Length - 1)));
            if (!ret.IsSuccessful)
            {
                logger.Error($"Failed to configure transfer length: {ret.Error}");
                return new(ret.Error);
            }

            if (!ret.Value)
            {
                logger.Error("WriteAddr to TranConfig returned false");
                return new(new Exception("Failed to configure transfer length"));
            }
        }

        // 配置I2C地址、协议及启动传输
        {
            var ret = await UDPClientPool.WriteAddr(
                this.ep, this.taskID, I2cAddr.BaseConfig, (devAddr) | (((uint)proto) << 16) | (1 << 24));
            if (!ret.IsSuccessful)
            {
                logger.Error($"Failed to configure I2C address/protocol/start: {ret.Error}");
                return new(ret.Error);
            }

            if (!ret.Value)
            {
                logger.Error("WriteAddr to BaseConfig returned false");
                return new(new Exception("Failed to configure I2C address/protocol/start"));
            }
        }

        // 等待I2C命令完成
        {
            var ret = await UDPClientPool.ReadAddrWithWait(this.ep, this.taskID, I2cAddr.Flag, 0x0000_0001, 0xFFFF_FFFF, 10);
            if (!ret.IsSuccessful)
            {
                logger.Error($"Failed to wait for I2C command completion: {ret.Error}");
                return new(ret.Error);
            }

            if (!ret.Value)
            {
                logger.Error("ReadAddrWithWait for I2C command completion returned false");
                return new(new Exception("I2C command did not complete successfully"));
            }
        }

        return true;
    }

    /// <summary>
    /// 从指定I2C设备读取数据
    /// </summary>
    /// <param name="devAddr">I2C设备地址</param>
    /// <param name="data">要写入的数据（dummy数据）</param>
    /// <param name="dataReadLength">要读取的数据长度</param>
    /// <param name="proto">I2C协议类型</param>
    /// <returns>操作结果，成功返回读取到的数据，否则返回异常信息</returns>
    public async ValueTask<Result<byte[]>> ReadData(UInt32 devAddr, byte[] data, int dataReadLength, I2cProtocol proto)
    {
        if (dataReadLength < 1 || dataReadLength > 0x0000_FFFF)
        {
            logger.Error($"Read length {dataReadLength} is invalid or exceeds maximum allowed 0x0000_FFFF");
            return new(new ArgumentException($"Read length {dataReadLength} is invalid or exceeds maximum allowed 0x0000_FFFF"));
        }

        if (data.Length > 0x0000_FFFF)
        {
            logger.Error($"Data length {data.Length} exceeds maximum allowed 0x0000_FFFF");
            return new(new ArgumentException($"Data length {data.Length} exceeds maximum allowed 0x0000_FFFF"));
        }

        // 清除UDP服务器接收缓冲区
        MsgBus.UDPServer.ClearUDPData(this.address, this.taskID);

        logger.Trace($"Clear up udp server {this.address} receive data");

        // 配置写FIFO内容，内容为data[]
        {
            var i2cData = new byte[data.Length * 4];
            int i = 0;
            foreach (var item in data)
            {
                i2cData[i++] = 0x00;
                i2cData[i++] = 0x00;
                i2cData[i++] = 0x00;
                i2cData[i++] = item;
            }

            var ret = await UDPClientPool.WriteAddr(this.ep, this.taskID, I2cAddr.Write, i2cData);
            if (!ret.IsSuccessful)
            {
                logger.Error($"Failed to write data to I2C FIFO: {ret.Error}");
                return new(ret.Error);
            }

            if (!ret.Value)
            {
                logger.Error("WriteAddr to I2C FIFO returned false");
                return new(new Exception("Failed to write data to I2C FIFO"));
            }
        }

        // 配置本次传输数据量：[15:0]为读长度（length-1），[31:16]为dummy长度（data.Length-1）
        {
            uint tranConfig = ((uint)(dataReadLength - 1)) | (((uint)(data.Length - 1)) << 16);
            var ret = await UDPClientPool.WriteAddr(this.ep, this.taskID, I2cAddr.TranConfig, tranConfig);
            if (!ret.IsSuccessful)
            {
                logger.Error($"Failed to configure transfer length: {ret.Error}");
                return new(ret.Error);
            }

            if (!ret.Value)
            {
                logger.Error("WriteAddr to TranConfig returned false");
                return new(new Exception("Failed to configure transfer length"));
            }
        }

        // 配置I2C地址、协议及启动传输（读操作）
        {
            var ret = await UDPClientPool.WriteAddr(
                this.ep, this.taskID, I2cAddr.BaseConfig, (devAddr) | (1 << 8) | (((uint)proto) << 16) | (1 << 24));
            if (!ret.IsSuccessful)
            {
                logger.Error($"Failed to configure I2C address/protocol/start: {ret.Error}");
                return new(ret.Error);
            }

            if (!ret.Value)
            {
                logger.Error("WriteAddr to BaseConfig returned false");
                return new(new Exception("Failed to configure I2C address/protocol/start"));
            }
        }

        // 等待I2C命令完成
        {
            var ret = await UDPClientPool.ReadAddrWithWait(this.ep, this.taskID, I2cAddr.Flag, 0x0000_0001, 0xFFFF_FFFF, 10);
            if (!ret.IsSuccessful)
            {
                logger.Error($"Failed to wait for I2C command completion: {ret.Error}");
                return new(ret.Error);
            }

            if (!ret.Value)
            {
                logger.Error("ReadAddrWithWait for I2C command completion returned false");
                return new(new Exception("I2C command did not complete successfully"));
            }
        }

        // 读取数据
        {
            var ret = await UDPClientPool.ReadAddrByte(this.ep, this.taskID, I2cAddr.Read);
            if (!ret.IsSuccessful)
            {
                logger.Error($"Failed to read data from I2C FIFO: {ret.Error}");
                return new(ret.Error);
            }

            if (ret.Value.Options.Data == null)
            {
                logger.Error($"ReadAddr returned unexpected data length: {ret.Value.Options.Data?.Length ?? 0}");
                return new(new Exception("Failed to read expected amount of data from I2C FIFO"));
            }

            return ret.Value.Options.Data[3..]; // 返回读取到的数据，跳过前3个字节
        }
    }
}
