using System;
using System.Linq;
using WinISP;

namespace MTK
{
    public static class MTKDebugCmd
    {
        private const Byte _BIT0 = 0x01;
        private const Byte _BIT1 = 0x02;
        private const Byte _BIT2 = 0x04;
        private const Byte _BIT3 = 0x08;
        private const Byte _BIT4 = 0x10;
        private const Byte _BIT5 = 0x20;
        private const Byte _BIT6 = 0x40;
        private const Byte _BIT7 = 0x80;

        private static ISimI2CCtrl _i2cCtrl;

        public static void InitI2CCtrl(ISimI2CCtrl i2cCtrl)
        {
            _i2cCtrl = i2cCtrl;
        }

        public static bool DDC_Write(Byte[] data)
        {
            //6E-51-Len-Data-chkSum
            const Byte dest = 0x6E;
            const Byte src = 0x51;
            Byte len = (Byte)(data.Length | _BIT7);
            Byte chkSum = 0;
            Byte[] wrBuf = new Byte[data.Length + 4];
            wrBuf[0] = dest;
            wrBuf[1] = src;
            wrBuf[2] = len;
            Array.Copy(data, 0, wrBuf, 3, data.Length);
            
            for (int idx = 0; idx < (wrBuf.Length - 1); idx++)
            {
                chkSum ^= wrBuf[idx];
            }
            wrBuf[wrBuf.Length - 1] = chkSum;


            bool ret = false;
            ret = _i2cCtrl.WriteData(wrBuf);

            string strData = "";
            for (int i = 3; i < wrBuf.Length; ++i)
                strData += wrBuf[i].ToString("X2") + " ";

            if (!ret)
                WinISP.frmISP.WriteLog("[Write DDC Fail]" + strData, "debug");

            return ret;
        }
          
        public static Byte[] DDC_Read(Byte[] data, int recvLen, int delayTime = 1000)
        {
            string strData = "";
            for (int i = 0; i < data.Length; ++i)
                strData += data[i].ToString("X2") + " ";
            const int maxNullRetryCnt = 3;
            for (int retry = 0; retry <= maxNullRetryCnt; retry++)
            {
                if (!DDC_Write(data))
                    return null;

                WinIOLib.DelayUs(delayTime * (retry + 1));

                const Byte dest = 0x6F;
                Byte[] wrCmds = { dest };
                Byte[] recvBuf = new Byte[recvLen];
                strData = "";
                if (_i2cCtrl.ReadData(recvBuf, wrCmds) == true)
                {
                    frmISP.WriteLog(
                            $"[DDC_Read RAW] cmd=0x{data[1]:X2} recvLen={recvLen} " +
                            $"raw=[{string.Join(" ", recvBuf.Select(b => b.ToString("X2")))}]",
                            "debug");

                    // ────────
                    for (int i = 0; i < recvBuf.Length; ++i)
                        strData += recvBuf[i].ToString("X2") + " ";

                    if ((recvBuf[0] == 0x6E) && (recvBuf[1] == 0x80))
                    {
                        if (retry < maxNullRetryCnt)
                            continue;

                        WinISP.frmISP.WriteLog("[Read DDC Fail]" + strData + "  DDC null response timeout.", "debug");
                        return null;
                    }

                    if ((recvBuf[0] != 0x6E) || (recvBuf[1] < 0x80))
                    {
                        WinISP.frmISP.WriteLog("[Read DDC Fail]" + strData + "  I2C Format Error!", "debug");
                        return null;
                    }

                    return recvBuf;
                }
                else
                {
                    WinISP.frmISP.WriteLog("[Read DDC Fail]", "debug");
                    return null;
                }
            }

            return null;
        }

        /// <summary>
        /// 从 DDC 0x6F 继续读取数据（不重新发送命令）。
        /// 用于 N卡 DP AUX 通道 16 字节限制导致截断时的分段续读。
        /// 在 DDC_Read 成功返回后立即调用，尝试读取响应缓冲区中尚未消费的剩余字节。
        /// </summary>
        /// <param name="recvLen">要读取的字节数</param>
        /// <returns>读取到的字节数组，失败返回 null</returns>
        public static Byte[] DDC_ReadContinue(int recvLen)
        {
            const Byte dest = 0x6F;
            Byte[] wrCmds = { dest };
            Byte[] recvBuf = new Byte[recvLen];
            if (_i2cCtrl.ReadData(recvBuf, wrCmds))
            {
                frmISP.WriteLog(
                    $"[DDC_ReadContinue] rdLen={recvLen} raw=[{string.Join(" ", recvBuf.Select(b => b.ToString("X2")))}]",
                    "debug");
                return recvBuf;
            }
            frmISP.WriteLog($"[DDC_ReadContinue] rdLen={recvLen} read failed", "debug");
            return null;
        }

        private static Byte CalCheckSum(Byte[] data, int startIdx = 0)
        {
            Byte chkSum = 0;
            for (int idx = startIdx; idx < data.Length; idx++)
            {
                chkSum ^= data[idx];
            }
            return chkSum;
        }

        //Write EDID 128 bytes
        public static bool DDC_WriteEDID(Byte[] data)
        {
            //DDC Wr EDID Cmd : CC-20-Idx-data
            const int maxWrLen = 32;
            const int maxRetryCnt = 5;
            int retryCnt = 0;
            int wrCnt = 0;
            Byte wrChkSum = 0, rdChkSum = 0;
            Byte[] cmds = new Byte[maxWrLen + 3];
            const Byte MTKCmd = 0xCC;
            const Byte wrEdidCmd = 0x20;            
            cmds[0] = MTKCmd;
            cmds[1] = wrEdidCmd;

            while ((wrCnt < data.Length) && (retryCnt < maxRetryCnt))
            {
                cmds[2] = (Byte)wrCnt;
                Array.Copy(data, wrCnt, cmds, 3, maxWrLen);
                wrChkSum = CalCheckSum(cmds, 3);
                DDC_Write(cmds);
                WinIOLib.DelayMs(100);
                rdChkSum = DDC_ReadChkSum();
                if (wrChkSum != rdChkSum)
                {
                    retryCnt++;
                    continue;
                }else{
                    retryCnt = 0;
                    wrCnt += maxWrLen;
                }
            }

            if ((retryCnt == 0) && (wrCnt == data.Length))
                return true;
            else
                return false;
        }

        public static Byte DDC_ReadChkSum()
        {
            Byte[] cmds = new Byte[2];
            const Byte MTKCmd = 0xCC;
            const Byte wrRdChkSumCmd = 0x8E;
            cmds[0] = MTKCmd;
            cmds[1] = wrRdChkSumCmd;

            //Recv procedure : 6E-Len-Data0-Data1-ChkSum
            const int recvLen = 5;
            Byte[] recvBuf = DDC_Read(cmds, recvLen);            

            if (recvBuf == null)
                return 0;

            if ((recvBuf[1] & 0x7F) == (recvLen - 3))
                return recvBuf[2];
            else
                return 0;
        }

        public static bool IsDDCFormatOK(byte[] ddc, ref int slaveAddrIndex, ref int lengthIndex, ref int checkSumIndex)
        {
            int _slaveAddrIndex = -1, _lengthIndex = -1, _checkSumIndex = -1;
            for (int i = 0; i < ddc.Length; ++i)
            {
                if (ddc[i] == 0x6E)
                {
                    _slaveAddrIndex = i;
                    if (_slaveAddrIndex + 1 < ddc.Length)
                        if (ddc[_slaveAddrIndex + 1] > 0x80)
                        {
                            _lengthIndex = _slaveAddrIndex + 1;
                            if ((_lengthIndex + (ddc[_slaveAddrIndex + 1] - 0x80) + 1) < ddc.Length)
                            {
                                _checkSumIndex = _lengthIndex + (ddc[_slaveAddrIndex + 1] - 0x80) + 1;
                                break;
                            }
                        }
                }
            }
            WinISP.frmISP.WriteLog("DDCFormat check - _slaveAddrIndex: " + _slaveAddrIndex.ToString(), "debug");
            WinISP.frmISP.WriteLog("DDCFormat check - _lengthIndex: " + _lengthIndex.ToString(), "debug");
            WinISP.frmISP.WriteLog("DDCFormat check - _checkSumIndex: " + _checkSumIndex.ToString(), "debug");
            if ((_slaveAddrIndex == -1) || (_lengthIndex == -1) || (_checkSumIndex == -1))
            {
                WinISP.frmISP.WriteLog("DDCFormat check fail - Index: -1", "debug");
                return false;
            }
            if ((_checkSumIndex - _lengthIndex <= 0) || (_lengthIndex - _slaveAddrIndex <= 0) || (_checkSumIndex - _slaveAddrIndex <= 0))
            {
                WinISP.frmISP.WriteLog("DDCFormat check fail - Index fail", "debug");
                return false;
            }
            byte checkSum = 0;

            for (int i = _slaveAddrIndex; i < _checkSumIndex; ++i)
                checkSum = (byte)(checkSum ^ ddc[i]);

            checkSum = (byte)(checkSum ^ 0x50);

            WinISP.frmISP.WriteLog("DDCFormat check - Calculated Checksum: " + checkSum.ToString("X2"), "debug");
            WinISP.frmISP.WriteLog("DDCFormat check - Received Checksum: " + ddc[_checkSumIndex].ToString("X2"), "debug");

            if (checkSum == ddc[_checkSumIndex])
            {
                slaveAddrIndex = _slaveAddrIndex;
                lengthIndex = _lengthIndex;
                checkSumIndex = _checkSumIndex;
                return true;
            }
            else
            {
                WinISP.frmISP.WriteLog("DDCFormat check fail - Checksum fail", "debug");
                return false;
            }
        }
    }
}
