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

namespace Peripherals.OscilloscopeClient;

static class OscilloscopeAddr
{
    const UInt32 BASE = 0x8000_0000;

    /// <summary>
    /// 0x0000_0000:R/W[0] wave_run 启动捕获/关闭
    /// </summary>
    public const UInt32 START_CAPTURE = BASE + 0x0000_0000;

    /// <summary>
    /// 0x0000_0001: R/W[7:0] trig_level 触发电平
    /// </summary>
    public const UInt32 TRIG_LEVEL = BASE + 0x0000_0001;

    /// <summary>
    /// 0x0000_0002:R/W[0] trig_edge 触发边沿，0-下降沿，1-上升沿
    /// </summary>
    public const UInt32 TRIG_EDGE = BASE + 0x0000_0002;

    /// <summary>
    /// 0x0000_0003: R/W[9:0] h shift 水平偏移量
    /// </summary>
    public const UInt32 H_SHIFT = BASE + 0x0000_0003;

    /// <summary>
    /// 0x0000_0004: R/W[9:0] deci rate 抽样率，0—1023
    /// </summary>
    public const UInt32 DECI_RATE = BASE + 0x0000_0004;

    /// <summary>
    /// 0x0000_0005:R/W[0] ram refresh RAM刷新
    /// </summary>
    public const UInt32 RAM_FRESH = BASE + 0x0000_0005;

    /// <summary>
    /// 0x0000 0006:R[19: 0] ad_freq AD采样频率
    /// </summary>
    public const UInt32 AD_FREQ = BASE + 0x0000_0006;

    /// <summary>
    /// Ox0000_0007: R[7:0] ad_vpp AD采样幅度
    /// </summary>
    public const UInt32 AD_VPP = BASE + 0x0000_0007;

    /// <summary>
    /// 0x0000_0008: R[7:0] ad max AD采样最大值
    /// </summary>
    public const UInt32 AD_MAX = BASE + 0x0000_0008;

    /// <summary>
    /// 0x0000_0009: R[7:0] ad_min AD采样最小值
    /// </summary>
    public const UInt32 AD_MIN = BASE + 0x0000_0009;

    /// <summary>
    /// 0x0000_1000-0x0000_13FF:R[7:0] wave_rd_data 共1024个字节
    /// </summary>
    public const UInt32 RD_DATA_ADDR = BASE + 0x0000_1000;
    public const UInt32 RD_DATA_LENGTH = 0x0000_0400;
}

class Oscilloscope
{
    private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();

    readonly int timeout = 2000;
    readonly int taskID = 0;

    readonly int port;
    readonly string address;
    private IPEndPoint ep;

    /// <summary>
    /// 初始化示波器客户端
    /// </summary>
    /// <param name="address">示波器设备IP地址</param>
    /// <param name="port">示波器设备端口</param>
    /// <param name="timeout">超时时间（毫秒）</param>
    public Oscilloscope(string address, int port, int timeout = 2000)
    {
        if (timeout < 0)
            throw new ArgumentException("Timeout couldn't be negative", nameof(timeout));
        this.address = address;
        this.port = port;
        this.ep = new IPEndPoint(IPAddress.Parse(address), port);
        this.timeout = timeout;
    }

    /// <summary>
    /// 控制示波器的捕获开关
    /// </summary>
    /// <param name="enable">是否启动捕获</param>
    /// <returns>操作结果，成功返回true，否则返回异常信息</returns>
    public async ValueTask<Result<bool>> SetCaptureEnable(bool enable)
    {
        UInt32 value = enable ? 1u : 0u;
        var ret = await UDPClientPool.WriteAddr(this.ep, this.taskID, OscilloscopeAddr.START_CAPTURE, value, this.timeout);
        if (!ret.IsSuccessful)
        {
            logger.Error($"Failed to set capture enable: {ret.Error}");
            return new(ret.Error);
        }
        if (!ret.Value)
        {
            logger.Error("WriteAddr to START_CAPTURE returned false");
            return new(new Exception("Failed to set capture enable"));
        }
        return true;
    }

    /// <summary>
    /// 设置触发电平
    /// </summary>
    /// <param name="level">触发电平值（0-255）</param>
    /// <returns>操作结果，成功返回true，否则返回异常信息</returns>
    public async ValueTask<Result<bool>> SetTriggerLevel(byte level)
    {
        var ret = await UDPClientPool.WriteAddr(this.ep, this.taskID, OscilloscopeAddr.TRIG_LEVEL, level, this.timeout);
        if (!ret.IsSuccessful)
        {
            logger.Error($"Failed to set trigger level: {ret.Error}");
            return new(ret.Error);
        }
        if (!ret.Value)
        {
            logger.Error("WriteAddr to TRIG_LEVEL returned false");
            return new(new Exception("Failed to set trigger level"));
        }
        return true;
    }

    /// <summary>
    /// 设置触发边沿
    /// </summary>
    /// <param name="risingEdge">true为上升沿，false为下降沿</param>
    /// <returns>操作结果，成功返回true，否则返回异常信息</returns>
    public async ValueTask<Result<bool>> SetTriggerEdge(bool risingEdge)
    {
        UInt32 value = risingEdge ? 1u : 0u;
        var ret = await UDPClientPool.WriteAddr(this.ep, this.taskID, OscilloscopeAddr.TRIG_EDGE, value, this.timeout);
        if (!ret.IsSuccessful)
        {
            logger.Error($"Failed to set trigger edge: {ret.Error}");
            return new(ret.Error);
        }
        if (!ret.Value)
        {
            logger.Error("WriteAddr to TRIG_EDGE returned false");
            return new(new Exception("Failed to set trigger edge"));
        }
        return true;
    }

    /// <summary>
    /// 设置水平偏移量
    /// </summary>
    /// <param name="shift">水平偏移量值（0-1023）</param>
    /// <returns>操作结果，成功返回true，否则返回异常信息</returns>
    public async ValueTask<Result<bool>> SetHorizontalShift(UInt16 shift)
    {
        if (shift > 1023)
            return new(new ArgumentException("Horizontal shift must be 0-1023", nameof(shift)));

        var ret = await UDPClientPool.WriteAddr(this.ep, this.taskID, OscilloscopeAddr.H_SHIFT, shift, this.timeout);
        if (!ret.IsSuccessful)
        {
            logger.Error($"Failed to set horizontal shift: {ret.Error}");
            return new(ret.Error);
        }
        if (!ret.Value)
        {
            logger.Error("WriteAddr to H_SHIFT returned false");
            return new(new Exception("Failed to set horizontal shift"));
        }
        return true;
    }

    /// <summary>
    /// 设置抽样率
    /// </summary>
    /// <param name="rate">抽样率值（0-1023）</param>
    /// <returns>操作结果，成功返回true，否则返回异常信息</returns>
    public async ValueTask<Result<bool>> SetDecimationRate(UInt16 rate)
    {
        if (rate > 1023)
            return new(new ArgumentException("Decimation rate must be 0-1023", nameof(rate)));

        var ret = await UDPClientPool.WriteAddr(this.ep, this.taskID, OscilloscopeAddr.DECI_RATE, rate, this.timeout);
        if (!ret.IsSuccessful)
        {
            logger.Error($"Failed to set decimation rate: {ret.Error}");
            return new(ret.Error);
        }
        if (!ret.Value)
        {
            logger.Error("WriteAddr to DECI_RATE returned false");
            return new(new Exception("Failed to set decimation rate"));
        }
        return true;
    }

    /// <summary>
    /// 刷新RAM
    /// </summary>
    /// <returns>操作结果，成功返回true，否则返回异常信息</returns>
    public async ValueTask<Result<bool>> RefreshRAM()
    {
        var ret = await UDPClientPool.WriteAddr(this.ep, this.taskID, OscilloscopeAddr.RAM_FRESH, 1u, this.timeout);
        if (!ret.IsSuccessful)
        {
            logger.Error($"Failed to refresh RAM: {ret.Error}");
            return new(ret.Error);
        }
        if (!ret.Value)
        {
            logger.Error("WriteAddr to RAM_FRESH returned false");
            return new(new Exception("Failed to refresh RAM"));
        }
        return true;
    }

    /// <summary>
    /// 获取AD采样频率
    /// </summary>
    /// <returns>操作结果，成功返回采样频率值，否则返回异常信息</returns>
    public async ValueTask<Result<UInt32>> GetADFrequency()
    {
        var ret = await UDPClientPool.ReadAddrByte(this.ep, this.taskID, OscilloscopeAddr.AD_FREQ, this.timeout);
        if (!ret.IsSuccessful)
        {
            logger.Error($"Failed to read AD frequency: {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 AD frequency");
            return new(new Exception("Failed to read AD frequency"));
        }
        UInt32 freq = Number.BytesToUInt32(ret.Value.Options.Data).Value;
        // 取低20位 [19:0]
        freq &= 0xFFFFF;
        return freq;
    }

    /// <summary>
    /// 获取AD采样幅度
    /// </summary>
    /// <returns>操作结果，成功返回采样幅度值，否则返回异常信息</returns>
    public async ValueTask<Result<byte>> GetADVpp()
    {
        var ret = await UDPClientPool.ReadAddrByte(this.ep, this.taskID, OscilloscopeAddr.AD_VPP, this.timeout);
        if (!ret.IsSuccessful)
        {
            logger.Error($"Failed to read AD VPP: {ret.Error}");
            return new(ret.Error);
        }
        if (ret.Value.Options.Data == null || ret.Value.Options.Data.Length < 1)
        {
            logger.Error("ReadAddr returned invalid data for AD VPP");
            return new(new Exception("Failed to read AD VPP"));
        }
        return ret.Value.Options.Data[3];
    }

    /// <summary>
    /// 获取AD采样最大值
    /// </summary>
    /// <returns>操作结果，成功返回采样最大值，否则返回异常信息</returns>
    public async ValueTask<Result<byte>> GetADMax()
    {
        var ret = await UDPClientPool.ReadAddrByte(this.ep, this.taskID, OscilloscopeAddr.AD_MAX, this.timeout);
        if (!ret.IsSuccessful)
        {
            logger.Error($"Failed to read AD max: {ret.Error}");
            return new(ret.Error);
        }
        if (ret.Value.Options.Data == null || ret.Value.Options.Data.Length < 1)
        {
            logger.Error("ReadAddr returned invalid data for AD max");
            return new(new Exception("Failed to read AD max"));
        }
        return ret.Value.Options.Data[3];
    }

    /// <summary>
    /// 获取AD采样最小值
    /// </summary>
    /// <returns>操作结果，成功返回采样最小值，否则返回异常信息</returns>
    public async ValueTask<Result<byte>> GetADMin()
    {
        var ret = await UDPClientPool.ReadAddrByte(this.ep, this.taskID, OscilloscopeAddr.AD_MIN, this.timeout);
        if (!ret.IsSuccessful)
        {
            logger.Error($"Failed to read AD min: {ret.Error}");
            return new(ret.Error);
        }
        if (ret.Value.Options.Data == null || ret.Value.Options.Data.Length < 1)
        {
            logger.Error("ReadAddr returned invalid data for AD min");
            return new(new Exception("Failed to read AD min"));
        }
        return ret.Value.Options.Data[3];
    }

    /// <summary>
    /// 获取波形采样数据
    /// </summary>
    /// <returns>操作结果，成功返回采样数据数组，否则返回异常信息</returns>
    public async ValueTask<Result<byte[]>> GetWaveformData()
    {
        var ret = await UDPClientPool.ReadAddr4BytesAsync(
            this.ep,
            this.taskID,
            OscilloscopeAddr.RD_DATA_ADDR,
            (int)OscilloscopeAddr.RD_DATA_LENGTH / 32,
            BurstType.ExtendBurst, // 使用扩展突发读取
            this.timeout
        );
        if (!ret.IsSuccessful)
        {
            logger.Error($"Failed to read waveform data: {ret.Error}");
            return new(ret.Error);
        }
        var data = ret.Value;
        if (data == null || data.Length != OscilloscopeAddr.RD_DATA_LENGTH / 8)
        {
            logger.Error($"Waveform data length mismatch: {data?.Length}");
            return new(new Exception("Waveform data length mismatch"));
        }

        // 处理波形数据：从每4个字节中提取第4个字节（索引3）作为有效数据
        // 数据格式：低八位有效，即[4*i + 3]才是有效数据
        int sampleCount = data.Length / 4;
        byte[] waveformData = new byte[sampleCount];

        for (int i = 0; i < sampleCount; i++)
        {
            waveformData[i] = data[4 * i + 3];
        }

        return waveformData;
    }
}
