using System.Collections;
using System.Net;
using Common;
using DotNext;
using WebProtocol;

namespace Peripherals.LogicAnalyzerClient;

static class AnalyzerAddr
{
    const UInt32 BASE = 0x9000_0000;
    const UInt32 DMA1_BASE = 0x7000_0000;
    const UInt32 DDR_BASE = 0x0000_0000;

    /// <summary>
    /// 0x0000_0000    R/W [ 0]      capture on:    置1开始等待捕获，0停止捕获。捕获到信号后该位自动清零。  <br/>
    ///                    [ 8]      capture force: 置1则强制捕获信号，自动置0。 <br/>
    ///                    [16]      capture busy:  1为逻辑分析仪正在捕获信号。 <br/>
    ///                    [24]      capture done:  1为逻辑分析仪内存完整存储了此次捕获的信号。 <br/>
    /// 配置顺序：若[0]为0，则将其置1，随后不断获取[0]，若其变为0则表示触发成功。随后不断获取[24]，若其为1则表示捕获完成。 <br/>
    /// </summary>
    public const UInt32 CAPTURE_MODE = BASE + 0x0000_0000;

    /// <summary>
    /// 0x0000_0001    R/W [1:0] global trig mode:  00: 全局与  (&amp;) <br/>
    ///                                             01: 全局或  (&#124;) <br/>
    ///                                             10: 全局非与(~&amp;) <br/>
    ///                                             11: 全局非或(~&#124;) <br/>
    /// </summary>
    public const UInt32 GLOBAL_TRIG_MODE = BASE + 0x0000_0001;

    /// <summary>
    /// 0x0000_0010 - 0x0000_0017 R/W [5:0] 信号M的触发操作符，共8路 <br/>
    ///                               [5:3] M's Operator: 000 == <br/>
    ///                                                   001 != <br/>
    ///                                                   010 &lt; <br/>
    ///                                                   011 &lt;= <br/>
    ///                                                   100 &gt; <br/>
    ///                                                   101 &gt;= <br/>
    ///                               [2:0] M's Value:    000 LOGIC 0 <br/>
    ///                                                   001 LOGIC 1 <br/>
    ///                                                   010 X(not care) <br/>
    ///                                                   011 RISE <br/>
    ///                                                   100 FALL <br/>
    ///                                                   101 RISE OR FALL <br/>
    ///                                                   110 NOCHANGE <br/>
    ///                                                   111 SOME NUMBER <br/>
    /// </summary>
    public static readonly UInt32[] SIGNAL_TRIG_MODE = {
        BASE + 0x0000_0010, BASE + 0x0000_0011,
        BASE + 0x0000_0012, BASE + 0x0000_0013,
        BASE + 0x0000_0014, BASE + 0x0000_0015,
        BASE + 0x0000_0016, BASE + 0x0000_0017,
        BASE + 0x0000_0018, BASE + 0x0000_0019,
        BASE + 0x0000_001A, BASE + 0x0000_001B,
        BASE + 0x0000_001C, BASE + 0x0000_001D,
        BASE + 0x0000_001E, BASE + 0x0000_001F,
        BASE + 0x0000_0020, BASE + 0x0000_0021,
        BASE + 0x0000_0022, BASE + 0x0000_0023,
        BASE + 0x0000_0024, BASE + 0x0000_0025,
        BASE + 0x0000_0026, BASE + 0x0000_0027,
        BASE + 0x0000_0028, BASE + 0x0000_0029,
        BASE + 0x0000_002A, BASE + 0x0000_002B,
        BASE + 0x0000_002C, BASE + 0x0000_002D,
        BASE + 0x0000_002E, BASE + 0x0000_002F
    };
    public const UInt32 LOAD_NUM_ADDR = BASE + 0x0000_0002;
    public const UInt32 PRE_LOAD_NUM_ADDR = BASE + 0x0000_0003;
    public const UInt32 CAHNNEL_DIV_ADDR = BASE + 0x0000_0004;
    public const UInt32 CLOCK_DIV_ADDR = BASE + 0x0000_0005;
    public const UInt32 DMA1_START_WRITE_ADDR = DMA1_BASE + 0x0000_0012;
    public const UInt32 DMA1_END_WRITE_ADDR = DMA1_BASE + 0x0000_0013;
    public const UInt32 DMA1_CAPTURE_CTRL_ADDR = DMA1_BASE + 0x0000_0014;
    public const UInt32 STORE_OFFSET_ADDR = DDR_BASE + 0x0100_0000;

    /// <summary>
    /// 0x0100_0000 - 0x0100_03FF 只读 32位波形存储，得到的32位数据中低八位最先捕获，高八位最后捕获。<br/>
    ///                                共1024个地址，每个地址存储4组，深度为4096。<br/>
    /// </summary>
    public const Int32 CAPTURE_DATA_LENGTH = 1024;
    public const Int32 CAPTURE_DATA_PRELOAD = 512;
}

/// <summary>
/// 逻辑分析仪运行状态枚举
/// </summary>
[Flags]
public enum CaptureStatus
{
    /// <summary>
    /// 无状态标志
    /// </summary>
    None = 0,

    /// <summary>
    /// 捕获使能位，置1开始等待捕获，0停止捕获。捕获到信号后该位自动清零
    /// </summary>
    CaptureOn = 1 << 0,      // [0] 捕获使能

    /// <summary>
    /// 强制捕获位，置1则强制捕获信号，自动置0
    /// </summary>
    CaptureForce = 1 << 8,   // [8] 强制捕获

    /// <summary>
    /// 捕获忙碌位，1为逻辑分析仪正在捕获信号
    /// </summary>
    CaptureBusy = 1 << 16,   // [16] 捕获进行中

    /// <summary>
    /// 捕获完成位，1为逻辑分析仪内存完整存储了此次捕获的信号
    /// </summary>
    CaptureDone = 1 << 24    // [24] 捕获完成
}

/// <summary>
/// 全局触发模式枚举，定义多路信号触发条件的逻辑组合方式
/// </summary>
public enum GlobalCaptureMode
{
    /// <summary>
    /// 全局与模式，所有触发条件都必须满足
    /// </summary>
    AND = 0b00,

    /// <summary>
    /// 全局或模式，任一触发条件满足即可
    /// </summary>
    OR = 0b01,

    /// <summary>
    /// 全局非与模式，不是所有触发条件都满足
    /// </summary>
    NAND = 0b10,

    /// <summary>
    /// 全局非或模式，所有触发条件都不满足
    /// </summary>
    NOR = 0b11
}

/// <summary>
/// 逻辑分析仪采样时钟分频系数
/// </summary>
public enum AnalyzerClockDiv
{
    /// <summary>
    /// 1分频
    /// </summary>
    DIV1 = 0x0000_0000,

    /// <summary>
    /// 2分频
    /// </summary>
    DIV2 = 0x0000_0001,

    /// <summary>
    /// 4分频
    /// </summary>
    DIV4 = 0x0000_0002,

    /// <summary>
    /// 8分频
    /// </summary>
    DIV8 = 0x0000_0003,

    /// <summary>
    /// 16分频
    /// </summary>
    DIV16 = 0x0000_0004,

    /// <summary>
    /// 32分频
    /// </summary>
    DIV32 = 0x0000_0005,

    /// <summary>
    /// 64分频
    /// </summary>
    DIV64 = 0x0000_0006,

    /// <summary>
    /// 128分频
    /// </summary>
    DIV128 = 0x0000_0007
}

/// <summary>
/// 信号M的操作符枚举
/// </summary>
public enum SignalOperator : byte
{
    /// <summary>
    /// 等于操作符
    /// </summary>
    Equal = 0b000,        // ==
    /// <summary>
    /// 不等于操作符
    /// </summary>
    NotEqual = 0b001,     // !=
    /// <summary>
    /// 小于操作符
    /// </summary>
    LessThan = 0b010,     // <
    /// <summary>
    /// 小于等于操作符
    /// </summary>
    LessThanOrEqual = 0b011, // <=
    /// <summary>
    /// 大于操作符
    /// </summary>
    GreaterThan = 0b100,      // >
    /// <summary>
    /// 大于等于操作符
    /// </summary>
    GreaterThanOrEqual = 0b101 // >=
}

/// <summary>
/// 信号M的值枚举
/// </summary>
public enum SignalValue : byte
{
    /// <summary>
    /// 逻辑0电平
    /// </summary>
    Logic0 = 0b000,        // LOGIC 0
    /// <summary>
    /// 逻辑1电平
    /// </summary>
    Logic1 = 0b001,        // LOGIC 1
    /// <summary>
    /// 不关心该信号状态
    /// </summary>
    NotCare = 0b010,       // X(not care)
    /// <summary>
    /// 上升沿触发
    /// </summary>
    Rise = 0b011,          // RISE
    /// <summary>
    /// 下降沿触发
    /// </summary>
    Fall = 0b100,          // FALL
    /// <summary>
    /// 上升沿或下降沿触发
    /// </summary>
    RiseOrFall = 0b101,    // RISE OR FALL
    /// <summary>
    /// 信号无变化
    /// </summary>
    NoChange = 0b110,      // NOCHANGE
    /// <summary>
    /// 特定数值
    /// </summary>
    SomeNumber = 0b111     // SOME NUMBER
}

/// <summary>
/// 逻辑分析仪有效通道数
/// </summary>
public enum AnalyzerChannelDiv
{
    /// <summary>
    /// 1路
    /// </summary>
    ONE = 0x0000_0000,
    /// <summary>
    /// 2路
    /// </summary>
    TWO = 0x0000_0001,
    /// <summary>
    /// 4路
    /// </summary>
    FOUR = 0x0000_0002,
    /// <summary>
    /// 8路
    /// </summary>
    EIGHT = 0x0000_0003,
    /// <summary>
    /// 16路
    /// </summary>
    XVI = 0x0000_0004,
    /// <summary>
    /// 32路
    /// </summary>
    XXXII = 0x0000_0005
}

/// <summary>
/// FPGA逻辑分析仪客户端，用于控制FPGA上的逻辑分析仪模块进行信号捕获和分析
/// </summary>
public class Analyzer
{

    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>
    /// 初始化逻辑分析仪客户端
    /// </summary>
    /// <param name="address">FPGA设备的IP地址</param>
    /// <param name="port">通信端口号</param>
    /// <param name="taskID">任务标识符</param>
    /// <param name="timeout">通信超时时间（毫秒），默认2000ms</param>
    /// <exception cref="ArgumentException">当timeout为负数时抛出</exception>
    public Analyzer(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>
    /// 控制逻辑分析仪的捕获模式
    /// </summary>
    /// <param name="captureOn">是否开始捕获</param>
    /// <param name="force">是否强制捕获</param>
    /// <returns>操作结果，成功返回true，否则返回异常信息</returns>
    public async ValueTask<Result<bool>> SetCaptureMode(bool captureOn, bool force)
    {
        // 构造寄存器值
        UInt32 value = 0;
        if (captureOn) value |= 1 << 0;
        {
            var ret = await UDPClientPool.WriteAddr(this.ep, this.taskID, AnalyzerAddr.DMA1_CAPTURE_CTRL_ADDR, value, this.timeout);
            if (!ret.IsSuccessful)
            {
                logger.Error($"Failed to set DMA1_CAPTURE_CTRL_ADDR: {ret.Error}");
                return new(ret.Error);
            }
            if (!ret.Value)
            {
                logger.Error("WriteAddr to DMA1_CAPTURE_CTRL_ADDR returned false");
                return new(new Exception("Failed to set DMA1_CAPTURE_CTRL_ADDR"));
            }
        }
        if (force) value |= 1 << 8;
        {
            var ret = await UDPClientPool.WriteAddr(this.ep, this.taskID, AnalyzerAddr.CAPTURE_MODE, value, this.timeout);
            if (!ret.IsSuccessful)
            {
                logger.Error($"Failed to set capture mode: {ret.Error}");
                return new(ret.Error);
            }
            if (!ret.Value)
            {
                logger.Error("WriteAddr to CAPTURE_MODE returned false");
                return new(new Exception("Failed to set capture mode"));
            }
        }
        return true;
    }

    /// <summary>
    /// 读取逻辑分析仪捕获运行状态
    /// </summary>
    /// <returns>操作结果，成功返回寄存器值，否则返回异常信息</returns>
    public async ValueTask<Result<CaptureStatus>> ReadCaptureStatus()
    {
        var ret = await UDPClientPool.ReadAddrByte(this.ep, this.taskID, AnalyzerAddr.CAPTURE_MODE, this.timeout);
        if (!ret.IsSuccessful)
        {
            logger.Error($"Failed to read capture status: {ret.Error}");
            return new(ret.Error);
        }
        if (ret.Value.Options.Data == null || ret.Value.Options.Data.Length < 4)
        {
            logger.Error("ReadAddr returned invalid data for capture status");
            return new(new Exception("Failed to read capture status"));
        }
        UInt32 status = Number.BytesToUInt32(ret.Value.Options.Data).Value;
        return (CaptureStatus)status;
    }

    /// <summary>
    /// 设置全局触发模式
    /// </summary>
    /// <param name="mode">全局触发模式（0:与, 1:或, 2:非与, 3:非或）</param>
    /// <returns>操作结果，成功返回true，否则返回异常信息</returns>
    public async ValueTask<Result<bool>> SetGlobalTrigMode(GlobalCaptureMode mode)
    {
        var ret = await UDPClientPool.WriteAddr(
            this.ep, this.taskID, AnalyzerAddr.GLOBAL_TRIG_MODE, (byte)mode, this.timeout);
        if (!ret.IsSuccessful)
        {
            logger.Error($"Failed to set global trigger mode: {ret.Error}");
            return new(ret.Error);
        }
        if (!ret.Value)
        {
            logger.Error("WriteAddr to GLOBAL_TRIG_MODE returned false");
            return new(new Exception("Failed to set global trigger mode"));
        }
        return true;
    }

    /// <summary>
    /// 设置指定信号通道的触发模式
    /// </summary>
    /// <param name="signalIndex">信号通道索引（0-7）</param>
    /// <param name="op">触发操作符</param>
    /// <param name="val">触发信号值</param>
    /// <returns>操作结果，成功返回true，否则返回异常信息</returns>
    public async ValueTask<Result<bool>> SetSignalTrigMode(int signalIndex, SignalOperator op, SignalValue val)
    {
        if (signalIndex < 0 || signalIndex >= AnalyzerAddr.SIGNAL_TRIG_MODE.Length)
            return new(new ArgumentException($"Signal index must be 0~{AnalyzerAddr.SIGNAL_TRIG_MODE.Length}"));

        // 计算模式值: [2:0] 信号值, [5:3] 操作符
        UInt32 mode = ((UInt32)op << 3) | (UInt32)val;

        var addr = AnalyzerAddr.SIGNAL_TRIG_MODE[signalIndex];
        var ret = await UDPClientPool.WriteAddr(this.ep, this.taskID, addr, mode, this.timeout);
        if (!ret.IsSuccessful)
        {
            logger.Error($"Failed to set signal trigger mode: {ret.Error}");
            return new(ret.Error);
        }
        if (!ret.Value)
        {
            logger.Error("WriteAddr to SIGNAL_TRIG_MODE returned false");
            return new(new Exception("Failed to set signal trigger mode"));
        }
        return true;
    }

    /// <summary>
    /// 设置逻辑分析仪的深度、预采样深度、有效通道、分频系数
    /// </summary>
    /// <param name="capture_length">深度</param>
    /// <param name="pre_capture_length">预采样深度</param>
    /// <param name="channel_div">有效通道(0-[1],1-[2],2-[4],3-[8],4-[16],5-[32])</param>
    /// <param name="clock_div">采样时钟分频系数</param>
    /// <returns>操作结果，成功返回true，否则返回异常信息</returns>
    public async ValueTask<Result<bool>> SetCaptureParams(int capture_length, int pre_capture_length, AnalyzerChannelDiv channel_div, AnalyzerClockDiv clock_div)
    {
        if (capture_length == 0) capture_length = 1;
        if (pre_capture_length == 0) pre_capture_length = 1;
        {
            var ret = await UDPClientPool.WriteAddr(this.ep, this.taskID, AnalyzerAddr.LOAD_NUM_ADDR, (UInt32)(capture_length - 1), this.timeout);
            if (!ret.IsSuccessful)
            {
                logger.Error($"Failed to set LOAD_NUM_ADDR: {ret.Error}");
                return new(ret.Error);
            }
            if (!ret.Value)
            {
                logger.Error("WriteAddr to LOAD_NUM_ADDR returned false");
                return new(new Exception("Failed to set LOAD_NUM_ADDR"));
            }
        }
        {
            var ret = await UDPClientPool.WriteAddr(this.ep, this.taskID, AnalyzerAddr.PRE_LOAD_NUM_ADDR, (UInt32)(pre_capture_length - 1), this.timeout);
            if (!ret.IsSuccessful)
            {
                logger.Error($"Failed to set PRE_LOAD_NUM_ADDR: {ret.Error}");
                return new(ret.Error);
            }
            if (!ret.Value)
            {
                logger.Error("WriteAddr to PRE_LOAD_NUM_ADDR returned false");
                return new(new Exception("Failed to set PRE_LOAD_NUM_ADDR"));
            }
        }
        {
            var ret = await UDPClientPool.WriteAddr(this.ep, this.taskID, AnalyzerAddr.DMA1_START_WRITE_ADDR, AnalyzerAddr.STORE_OFFSET_ADDR, this.timeout);
            if (!ret.IsSuccessful)
            {
                logger.Error($"Failed to set DMA1_START_WRITE_ADDR: {ret.Error}");
                return new(ret.Error);
            }
            if (!ret.Value)
            {
                logger.Error("WriteAddr to DMA1_START_WRITE_ADDR returned false");
                return new(new Exception("Failed to set DMA1_START_WRITE_ADDR"));
            }
        }
        {
            var ret = await UDPClientPool.WriteAddr(this.ep, this.taskID, AnalyzerAddr.DMA1_END_WRITE_ADDR, AnalyzerAddr.STORE_OFFSET_ADDR + (UInt32)(capture_length - 1), this.timeout);
            if (!ret.IsSuccessful)
            {
                logger.Error($"Failed to set DMA1_END_WRITE_ADDR: {ret.Error}");
                return new(ret.Error);
            }
            if (!ret.Value)
            {
                logger.Error("WriteAddr to DMA1_END_WRITE_ADDR returned false");
                return new(new Exception("Failed to set DMA1_END_WRITE_ADDR"));
            }
        }
        {
            var ret = await UDPClientPool.WriteAddr(this.ep, this.taskID, AnalyzerAddr.CAHNNEL_DIV_ADDR, (UInt32)channel_div, this.timeout);
            if (!ret.IsSuccessful)
            {
                logger.Error($"Failed to set CAHNNEL_DIV_ADDR: {ret.Error}");
                return new(ret.Error);
            }
            if (!ret.Value)
            {
                logger.Error("WriteAddr to CAHNNEL_DIV_ADDR returned false");
                return new(new Exception("Failed to set CAHNNEL_DIV_ADDR"));
            }
        }
        {
            var ret = await UDPClientPool.WriteAddr(this.ep, this.taskID, AnalyzerAddr.CLOCK_DIV_ADDR, (UInt32)clock_div, this.timeout);
            if (!ret.IsSuccessful)
            {
                logger.Error($"Failed to set CLOCK_DIV_ADDR: {ret.Error}");
                return new(ret.Error);
            }
            if (!ret.Value)
            {
                logger.Error("WriteAddr to CLOCK_DIV_ADDR returned false");
                return new(new Exception("Failed to set CLOCK_DIV_ADDR"));
            }
        }
        return true;
    }

    /// <summary>
    /// 读取捕获的波形数据
    /// </summary>
    /// <returns>操作结果，成功返回byte[]，否则返回异常信息</returns>
    public async ValueTask<Result<byte[]>> ReadCaptureData(int capture_length = 2048 * 32)
    {
        var ret = await UDPClientPool.ReadAddr4BytesAsync(
            this.ep,
            this.taskID,
            AnalyzerAddr.STORE_OFFSET_ADDR,
            capture_length,
            BurstType.ExtendBurst, // 使用扩展突发读取
            this.timeout
        );
        if (!ret.IsSuccessful)
        {
            logger.Error($"Failed to read capture data: {ret.Error}");
            return new(ret.Error);
        }
        var data = ret.Value;
        if (data == null || data.Length != capture_length * 4)
        {
            logger.Error($"Capture data length mismatch: {data?.Length}");
            return new(new Exception("Capture data length mismatch"));
        }
        var reversed = Common.Number.ReverseBytes(data, 4).Value;
        return reversed;
    }
}
