using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

using System.IO;
using MTK;
using System.Diagnostics;
using WinISP.BaseClass;
using System.Management;

namespace WinISP
{
    public partial class frmISP : Form
    {
        public int ErrorCode = 0;
        string _DirExeStarUp = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location) + "\\";
        bool IsConnect = false;
#if AUTO_SEARCH
        int StressTest_Count = 1;
#endif
        int Transfer_Time = 0;
        ulong Total_Transfer_Time = 0;
        ulong Total_UpdateFW_Time = 0;
        GraphicCardI2CCtrl gci2cCtrl = null;
        public frmISP()
        {
            InitializeComponent();
        }

        private String _binFilePath = String.Empty;
        private String _eddFilePath = String.Empty;
        private string _u8ModelName = String.Empty;
        private string _u8PanelName = String.Empty;
        private string _panelTypeName = String.Empty;
        private string _chipName = String.Empty;
        private string _u8ChipName = String.Empty;
        private string _u8BoardName = String.Empty;
        private string _monitorModelName = String.Empty;
        private string _monitorChipName = String.Empty;
        private string _monitorPanelName = String.Empty;
        private string _monitorBoardName = String.Empty;
        private bool _IsRunISP = false;
        ISimI2CCtrl _simI2cCtrl;
        private bool _IsHDMIConnect = false;
        const int _flashSectorSize = 0x1000;
        private Byte _chipType = 0;
        private Byte _panelType = 0;
        private Byte _clientType = 0;
        private const int WM_DEVICECHANGE = 0x219;
        private const int DBT_DEVNODES_CHANGED = 0x07;
        private const byte MStarCommand = 0xCC;
        private const byte CmdGetModelName = 0x36;
        private const byte CmdGetChipName = 0x41;
        private const byte CmdGetPanelName = 0x42;
        private const byte CmdGetBoardName = 0x45;
        private const int MonitorModelNameLength = 20;
        private const int MonitorChipNameLength = 20;
        private const int MonitorPanelNameLength = 30;
        private const int MonitorBoardNameLength = 20;
        private const string PlaceholderMonitorModelName = "MST9U6_PanelCMIM236HGJ_L21";
        private const string LegacyMonitorModelName = "MAG 321UPD E14";
        object _lockObj = new object();
        int _numberOfMonitors;
        int _lastNumOfMonitors = 0;
        int defaultPktLen = 59;
        public void UpdateNumofConnectedMonitor()
        {
            lock (_lockObj)
            {
                string strQuery = "SELECT * FROM Win32_PnPEntity WHERE Service = \"monitor\"";
                ManagementObjectSearcher searcher = new ManagementObjectSearcher("\\root\\CIMV2", strQuery);
                _numberOfMonitors = searcher.Get().Count;
            }
        }

        protected override void WndProc(ref Message m)
        {
            base.WndProc(ref m);

            switch (m.Msg)
            {
                case WM_DEVICECHANGE:
                    switch ((int)m.WParam)
                    {
                        case DBT_DEVNODES_CHANGED:
                            BackgroundWorker bw = new BackgroundWorker();
                            bw.DoWork += (s, args) =>
                            {
                                this.Invoke(new MethodInvoker(() => UpdateNumofConnectedMonitor()));
                                Application.DoEvents();
                            };
                            bw.RunWorkerAsync();
                            break;
                    }
                    break;
            }

        }

        public void CheckSingleInstance()
        {
            Process[] thisnameprocesslist;
            string modulename, processname;
            Process p = Process.GetCurrentProcess();
            modulename = p.MainModule.ModuleName.ToString();
            processname = System.IO.Path.GetFileNameWithoutExtension(modulename);
            thisnameprocesslist = Process.GetProcessesByName(processname);
            if (thisnameprocesslist.Length > 1)
            {
                Application.Exit();
            }
        }

#if DEBUG
        string debugversion = "[Debug Version]";
#else
        string debugversion = "";
#endif
        private void frmISP_Load(object sender, EventArgs e)
        {
            UpdateNumofConnectedMonitor();
            lblDisplayNumber.Text = _numberOfMonitors.ToString();

            CenterToScreen();
            if (_simI2cCtrl == null)
                _simI2cCtrl = new GraphicCardI2CCtrl();
            MTKDebugCmd.InitI2CCtrl(_simI2cCtrl);

            CheckSingleInstance();

            SetStatusMsg("Ready");

            string betaString = "";
            System.Diagnostics.FileVersionInfo fvi = System.Diagnostics.FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetExecutingAssembly().Location);
            this.Text += " [Program Version] : " + fvi.ProductVersion + " " + betaString /*+ " (FW ver : " + ver + ") "*/ + debugversion;
            WriteLog("Program version: " + fvi.ProductVersion, "debug");
#if DEBUG
            {
                this.label21.Visible = true;
                this.lblPktDelayTime.Visible = true;
                this.txtPktDelayTime.Visible = true;
                this.label20.Visible = true;
                this.lblMTKChipType.Visible = true;
            }
#endif
            Width = 800;
            TopMost = true;
            Connect();
            _lastNumOfMonitors = _numberOfMonitors;
            Application.Idle += Application_Idle;

        }

        bool _IsAutoRunISP = true;

        private void Application_Idle(object sender, EventArgs e)
        {
            if (_numberOfMonitors != _lastNumOfMonitors)
                _lastNumOfMonitors = _numberOfMonitors;
            //_cf = AccessIni.ReadFwConfig();
#if AUTO_SEARCH
            //search bin file from current folder
            FileInfo fi = SearchFile("*.bin");
            if (fi != null)
            {
                _binFilePath = fi.FullName;
                lblBinName.Text = fi.Name;
            }
            else
            {
                WriteLog("No bin file in current folder.", "debug");
                Application.Idle -= Application_Idle;
                this.Close();
            }
            Application.Idle -= Application_Idle;
            btnRunDPISP_Click(this, EventArgs.Empty);
#else
#endif
        }

        private FileInfo SearchFile(String extName)
        {
            string currentDirName = Directory.GetCurrentDirectory();
            string[] files = Directory.GetFiles(currentDirName, extName);
            foreach (string s in files)
            {
                FileInfo fi = null;
                try
                {
                    fi = new FileInfo(s);
                    WriteLog("FileInfo=" + fi.Name, "debug");
                    return fi;
                }
                catch (FileNotFoundException e)
                {
                    continue;
                }
            }

            return null;
        }

        private int GetTickCount()
        {
            return Environment.TickCount & Int32.MaxValue;
        }

        private void OnUpdateProgress(int total, int done, String msg)
        {
            if (msg != String.Empty)
            {
                SetStatusMsg(msg);
            }

            if (toolStripProgressBar1.Maximum != total)
            {
                toolStripProgressBar1.Maximum = (int)total;
            }

            if (toolStripProgressBar1.Value != done)
            {
                toolStripProgressBar1.Value = done;
            }
            Application.DoEvents();
        }

        private void SetISPStatus(bool isRunning,string err = "")
        {
            if (!string.IsNullOrEmpty(err))
            {
                MessageBox.Show(err, "Tip", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            _IsRunISP = isRunning;
            btnRunISP.Enabled = !isRunning;
            btnOpenBin.Enabled = !isRunning;
            btnRunDPISP.Enabled = !isRunning;
            cboConnectedMonitor.Enabled = !isRunning;
        }

        private void SetStatusMsg(String msg,bool display = false)
        {
            toolStripStatusLabel1.Text = msg;
            Application.DoEvents();

            if(display) MessageBox.Show(msg, "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        private void frmISP_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (_IsRunISP)
            {
                SetStatusMsg("Firmware is updating, please do not turn off application.");
                e.Cancel = true;
            }
        }

        private static string NormalizeMonitorInfo(string value)
        {
            return String.IsNullOrWhiteSpace(value) ? String.Empty : value.Trim();
        }

        private string NormalizeMonitorModelName(string value)
        {
            string normalizedValue = NormalizeMonitorInfo(value);
            if (string.Equals(normalizedValue, PlaceholderMonitorModelName, StringComparison.OrdinalIgnoreCase))
                return LegacyMonitorModelName;

            return normalizedValue;
        }

        private string GetModelMismatchMessage(string binModelName)
        {
            return "Model不匹配: FW=" + NormalizeMonitorModelName(_monitorModelName) + " BIN=" + NormalizeMonitorInfo(binModelName);
        }

        private bool IsModelNameMatch(string u8Model_Name)
        {
            string monitorValue = NormalizeMonitorModelName(_monitorModelName);
            if (String.IsNullOrEmpty(monitorValue))
                return true;

            return string.Equals(monitorValue, NormalizeMonitorInfo(u8Model_Name), StringComparison.Ordinal);
        }
        private bool IsChipNameMatch(string u8Chi_pName)
        {
            string monitorValue = NormalizeMonitorInfo(_monitorChipName);
            if (String.IsNullOrEmpty(monitorValue))
                return true;

            return string.Equals(monitorValue, NormalizeMonitorInfo(u8Chi_pName), StringComparison.Ordinal);
        }
        private bool IsPanelNameMatch(string u8Panel_Name)
        {
            string monitorValue = NormalizeMonitorInfo(_monitorPanelName);
            if (String.IsNullOrEmpty(monitorValue))
                return true;

            return string.Equals(monitorValue, NormalizeMonitorInfo(u8Panel_Name), StringComparison.Ordinal);
        }
        private bool IsBoardNameMatch(string u8Board_Name)
        {
            string monitorValue = NormalizeMonitorInfo(_monitorBoardName);
            string binValue = NormalizeMonitorInfo(u8Board_Name);

            if (!String.IsNullOrEmpty(monitorValue))
                return string.Equals(monitorValue, binValue, StringComparison.Ordinal);

            if (binValue.Length < 4)
                return false;

            return binValue[0] == 'F' && binValue[1] == 'W' && binValue[2] == '.' && binValue[3] == '0';
        }
        private void btnOpenBin_Click(object sender, EventArgs e)
        {
            OpenFileDialog dlg = new OpenFileDialog();
            dlg.InitialDirectory = "c:\\";
            dlg.Filter = "bin files (*.bin)|*.bin";
            dlg.FilterIndex = 1;
            dlg.RestoreDirectory = true;

            if (dlg.ShowDialog() == DialogResult.OK)
            {
                try
                {
                    FileInfo fi = new FileInfo(dlg.FileName);
                    _binFilePath = fi.FullName;
                    lblBinName.Text = fi.Name;
                    lblFileSize.Text = InitBinaryFile(_binFilePath).Length.ToString();
                    
                    // 解析bin文件中的u8ModelName、u8PanelName、u8BoardName、u8ChipName
                    const long infoAddr=0x0041380;
                    const int modenameLen = 20; // 每个名字最大长度
                    const int chipnameLen = 20; 
                    const int panelnameLen = 30;
                    const int boardnameLen = 20;
                    const int totalLen = modenameLen + chipnameLen + panelnameLen + boardnameLen;
                    byte[] infoBytes = ReadBinSection(_binFilePath, infoAddr, totalLen);
                                        if (infoBytes.Length < totalLen)
                                                throw new InvalidDataException("Bin metadata section is shorter than expected.");
                    _u8ModelName = Encoding.ASCII.GetString(infoBytes, 0, modenameLen).TrimEnd('\0');
                    _u8ChipName = Encoding.ASCII.GetString(infoBytes, modenameLen, chipnameLen).TrimEnd('\0');
                    _u8PanelName = Encoding.ASCII.GetString(infoBytes, modenameLen + chipnameLen, panelnameLen).TrimEnd('\0');
                    _u8BoardName = Encoding.ASCII.GetString(infoBytes, modenameLen + chipnameLen + panelnameLen, boardnameLen).TrimEnd('\0');
                //     SetStatusMsg("Read Bin OK. Model: " + _u8ModelName + ", Chip: " + _u8ChipName + ", Panel: " + _u8PanelName + ", Board: " + _u8BoardName);
                SetStatusMsg("Please update the firmware.",true);
                }
                catch (Exception ex)
                {
                    SetStatusMsg("Could not read file from disk.",true);
                }

                if (_binFilePath == String.Empty)
                    return;
                    
                if (flashSectorInfo != null && flashSectorInfo.Count > 0)
                    flashSectorInfo.Clear();
                btnConnect.PerformClick();

                if (IsConnect)
                {
                    string verifyMsg = String.Empty;
                    bool canVerify = false;
                    var gpuType = ((GraphicCardI2CCtrl)_simI2cCtrl).GetGraphicCardType;

                    if (gpuType != GraphicCardI2CCtrl.GRAPHIC_CARD.INTEL && gpuType != GraphicCardI2CCtrl.GRAPHIC_CARD.INTEL_IGCL)
                    {
                        ClearConnectedMonitorInfo();
                        QueryConnectedMonitorInfo();
                        canVerify = !string.IsNullOrWhiteSpace(_monitorModelName)
                            || !string.IsNullOrWhiteSpace(_monitorChipName)
                            || !string.IsNullOrWhiteSpace(_monitorPanelName);
                    }

                    if (!canVerify)
                    {
                        verifyMsg = "BIN已加载，但当前显卡路径无法立即读取显示器身份信息，暂时不能立即判断是否一致。";
                    }
                    else if (!IsModelNameMatch(_u8ModelName))
                    {
                        verifyMsg = GetModelMismatchMessage(_u8ModelName);
                    }
                    else if (!IsChipNameMatch(_u8ChipName))
                    {
                        verifyMsg = Properties.Resources.ChipNameErr;
                    }
                    else if (!IsPanelNameMatch(_u8PanelName))
                    {
                        verifyMsg = Properties.Resources.PanelNameErr;
                    }
                    else
                    {
                        verifyMsg = "BIN与当前显示器匹配，可开始升级。";
                    }

                    SetStatusMsg(verifyMsg, true);
                }
            }
        }

        private void txtDelayUnit_TextChanged(object sender, EventArgs e)
        {
            WinIOLib._dftI2CDlyTime = Convert.ToInt16(txtDelayUnit.Text);
        }

        private void QueryConnectedMonitorInfo()
        {
            // 读取显示器端信息（芯片名、面板名、板卡名、型号名）
            try
            {
                bool legacyRemapped = false;

                // 型号名
                {
                    Byte[] cmd = { MStarCommand, CmdGetModelName };
                    Byte[] recv = MTKDebugCmd.DDC_Read(cmd, MonitorModelNameLength + 2, 500)
                               ?? MTKDebugCmd.DDC_Read(cmd, MonitorModelNameLength + 2, 2000)
                               ?? MTKDebugCmd.DDC_Read(cmd, MonitorModelNameLength + 2, 5000);
                    if (recv != null && recv.Length >= MonitorModelNameLength + 2)
                    {
                        string rawModelName = Encoding.ASCII.GetString(recv, 2, MonitorModelNameLength).TrimEnd('\0');
                        if (string.Equals(rawModelName.Trim(), PlaceholderMonitorModelName, StringComparison.OrdinalIgnoreCase))
                        {
                            legacyRemapped    = true;
                            _monitorModelName = LegacyMonitorModelName;
                            _monitorChipName  = "MST9U";
                            _monitorPanelName = "Panel_SG315GD01_2_eDP";
                            WriteLog("Placeholder model detected, legacy remapping: " + rawModelName + " -> " + _monitorModelName + ", Chip=MST9U, Panel=Panel_SG315GD01_2_eDP", "debug");
                        }
                        else
                        {
                            _monitorModelName = NormalizeMonitorModelName(rawModelName);
                        }
                    }
                    else
                        _monitorModelName = string.Empty;
                }

                // 芯片名
                if (!legacyRemapped)
                {
                    Byte[] cmd = { MStarCommand, CmdGetChipName };
                    Byte[] recv = MTKDebugCmd.DDC_Read(cmd, MonitorChipNameLength + 2, 500)
                               ?? MTKDebugCmd.DDC_Read(cmd, MonitorChipNameLength + 2, 2000)
                               ?? MTKDebugCmd.DDC_Read(cmd, MonitorChipNameLength + 2, 5000);
                    if (recv != null && recv.Length >= MonitorChipNameLength + 2)
                        _monitorChipName = Encoding.ASCII.GetString(recv, 2, MonitorChipNameLength).TrimEnd('\0');
                    else
                        _monitorChipName = string.Empty;
                }
                // 面板名
                if (!legacyRemapped)
                {
                    Byte[] cmd = { MStarCommand, CmdGetPanelName };
                    Byte[] recv = MTKDebugCmd.DDC_Read(cmd, MonitorPanelNameLength + 2, 500)
                               ?? MTKDebugCmd.DDC_Read(cmd, MonitorPanelNameLength + 2, 2000)
                               ?? MTKDebugCmd.DDC_Read(cmd, MonitorPanelNameLength + 2, 5000);
                    if (recv != null && recv.Length >= MonitorPanelNameLength + 2)
                        _monitorPanelName = Encoding.ASCII.GetString(recv, 2, MonitorPanelNameLength).TrimEnd('\0');
                    else
                        _monitorPanelName = string.Empty;
                }
                // 板卡名
                {
                    Byte[] cmd = { MStarCommand, CmdGetBoardName };
                    Byte[] recv = MTKDebugCmd.DDC_Read(cmd, MonitorBoardNameLength + 2, 500)
                               ?? MTKDebugCmd.DDC_Read(cmd, MonitorBoardNameLength + 2, 2000)
                               ?? MTKDebugCmd.DDC_Read(cmd, MonitorBoardNameLength + 2, 5000);
                    if (recv != null && recv.Length >= MonitorBoardNameLength + 2)
                        _monitorBoardName = Encoding.ASCII.GetString(recv, 2, MonitorBoardNameLength).TrimEnd('\0');
                    else
                        _monitorBoardName = string.Empty;
                }

                // 日志打印
                WriteLog("MonitorModelName=" + _monitorModelName, "debug");
                WriteLog("MonitorChipName=" + _monitorChipName, "debug");
                WriteLog("MonitorPanelName=" + _monitorPanelName, "debug");
                WriteLog("MonitorBoardName=" + _monitorBoardName, "debug");
            }
            catch (Exception ex)
            {
                WriteLog("QueryConnectedMonitorInfo Exception: " + ex.Message, "debug");
            }
        }

        private void ClearConnectedMonitorInfo()
        {
            _monitorModelName = string.Empty;
            _monitorChipName = string.Empty;
            _monitorPanelName = string.Empty;
            _monitorBoardName = string.Empty;
        }

        void Connect(int delayTime = 0)
        {
            SetStatusMsg("Connect MTK Monitor...");
            //Application.DoEvents();
            Byte[] buf = { 0xCC, 0x90, 0x00 ,0x00};

            WinIOLib.DelayMs(delayTime);

            if (_simI2cCtrl.Init() == true)
            {
                WriteLog("simI2cCtrl.Init() = true", "debug");
                switch (((GraphicCardI2CCtrl)_simI2cCtrl).GetGraphicCardType)
                {
                    case GraphicCardI2CCtrl.GRAPHIC_CARD.AMD: { lblGraphicCard.Text = "AMD"; } break;
                    case GraphicCardI2CCtrl.GRAPHIC_CARD.INTEL: { lblGraphicCard.Text = "INTEL"; } break;
                    case GraphicCardI2CCtrl.GRAPHIC_CARD.NVIDIA: { lblGraphicCard.Text = "NVIDIA"; } break;
                    case GraphicCardI2CCtrl.GRAPHIC_CARD.INTEL_IGCL: { lblGraphicCard.Text = "INTEL-IGCL"; } break;
                    case GraphicCardI2CCtrl.GRAPHIC_CARD.NA: { lblGraphicCard.Text = "UNKNOWN"; } break;
                }

                if (((GraphicCardI2CCtrl)_simI2cCtrl).GetConnectedPortType == GraphicCardI2CCtrl.PortTypes.HDMI)
                {
                    WriteLog("_IsHDMIConnect = true;", "debug");
                    _IsHDMIConnect = true;
                }
                else
                {
                    WriteLog("_IsHDMIConnect = false;", "debug");
                    _IsHDMIConnect = false;
                }

                MTKDebugCmd.InitI2CCtrl(_simI2cCtrl);
                DDC_SetToolFlag(true);
                WinIOLib.DelayMs(10);
           //     DDC2CI_SetR2Reset51Flag(0x01);
                Byte chipType = 0;
                IsConnect = false;
               MTKDebugCmd.DDC_Write(buf);
                //if (IsConnect)
                {
                    //WriteLog("IsConnect = true", "debug");
                    btnRunDPISP.Enabled = true;
                    pnlConnState.BackColor = Color.LightGreen;
                    lblDisplayName.Text = ((GraphicCardI2CCtrl)_simI2cCtrl).GetMonitorName;
                    if (String.IsNullOrEmpty(_binFilePath))
                        SetStatusMsg("Please select a F/W file.");
                    else
                        SetStatusMsg("BIN loaded. Ready to upgrade.");

                    cboConnectedMonitor.Items.Clear();
                    String nameFromGPU = "";
                    List<String> list = ((GraphicCardI2CCtrl)_simI2cCtrl).InitConnectedMonitor;
                    WriteLog("InitConnectedMonitor count = " + list.Count, "debug");

                    if (list.Count == 0 && ((GraphicCardI2CCtrl)_simI2cCtrl).GetGraphicCardType == GraphicCardI2CCtrl.GRAPHIC_CARD.AMD)
                    {
                        WriteLog("InitConnectedMonitor = 0 on AMD auto-detect, trying forced NVIDIA controller ...", "debug");
                        ((GraphicCardI2CCtrl)_simI2cCtrl).ReCreateGraphicCardCtrl(GraphicCardI2CCtrl.GPUTypes.NVIDIA);
                        WinIOLib.DelayMs(300);
                        if (_simI2cCtrl.Init())
                        {
                            list = ((GraphicCardI2CCtrl)_simI2cCtrl).InitConnectedMonitor;
                            WriteLog("InitConnectedMonitor after forced NVIDIA count = " + list.Count, "debug");
                        }

                        if (list.Count == 0)
                        {
                            WriteLog("Forced NVIDIA controller did not find monitors, restoring auto-detect controller ...", "debug");
                            ((GraphicCardI2CCtrl)_simI2cCtrl).ReCreateGraphicCardCtrl();
                            WinIOLib.DelayMs(300);
                            if (_simI2cCtrl.Init())
                                list = ((GraphicCardI2CCtrl)_simI2cCtrl).InitConnectedMonitor;
                        }
                    }

                    // NVIDIA DP AUX channel may not be ready on first launch – retry with delay.
                    {
                        int mntRetry = 0;
                        while (list.Count == 0 && mntRetry++ < 3)
                        {
                            WriteLog("InitConnectedMonitor = 0, waiting 1500ms before retry " + mntRetry + " ...", "debug");
                            WinIOLib.DelayMs(1500);
                            list = ((GraphicCardI2CCtrl)_simI2cCtrl).InitConnectedMonitor;
                            WriteLog("InitConnectedMonitor retry " + mntRetry + " count = " + list.Count, "debug");
                        }
                        // If all retries exhausted and still no monitor, recreate the GPU control and try once more.
                        if (list.Count == 0)
                        {
                            WriteLog("InitConnectedMonitor still 0 – calling ReCreateGraphicCardCtrl ...", "debug");
                            ((GraphicCardI2CCtrl)_simI2cCtrl).ReCreateGraphicCardCtrl();
                            WinIOLib.DelayMs(1500);
                            list = ((GraphicCardI2CCtrl)_simI2cCtrl).InitConnectedMonitor;
                            WriteLog("InitConnectedMonitor after ReCreate count = " + list.Count, "debug");
                        }
                    }

                    lblDisplayNumber.Text = list.Count.ToString();
                    if (list.Count > 0)
                    {
                        for (int idx = 0; idx < list.Count; idx++)
                        {
                            cboConnectedMonitor.Items.Add(list[idx].ToUpper());
                        }
                    }

                    for (int idxDisp = 0; idxDisp < list.Count; idxDisp++)
                    {
                        int retry = 0; // Reset retry counter for each monitor index
                        nameFromGPU = list[idxDisp].ToUpper();
                        WriteLog("nameFromGPU[" + idxDisp + "] = " + nameFromGPU, "debug");
                        cboConnectedMonitor.SelectedIndex = idxDisp;

                        ((GraphicCardI2CCtrl)_simI2cCtrl).SetConnectedMonitorIndex((Byte)idxDisp);
                        while ((IsConnect == false) && (retry++ < 30))
                        {
                            IsConnect = DDC_CheckMTKDevice(ref chipType);
                        }
                        WriteLog("idxDisp=" + idxDisp + " IsConnect=" + IsConnect + " chipType=" + chipType, "debug");
                        if (IsConnect == true)
                        {
                            pnlConnState.BackColor = Color.LightGreen;
                            lblConnectedPort.Text = ((GraphicCardI2CCtrl)_simI2cCtrl).GetConnectedPortTypeByIdx((Byte)idxDisp).ToString();
                            break;
                        }
                    }
                    // Intel IGCL and AMD can complete the FE handshake but still time out on optional
                    // identity queries during connect. Skip those reads here so connect stays usable.
                    if ((((GraphicCardI2CCtrl)_simI2cCtrl).GetGraphicCardType != GraphicCardI2CCtrl.GRAPHIC_CARD.INTEL)
                        && (((GraphicCardI2CCtrl)_simI2cCtrl).GetGraphicCardType != GraphicCardI2CCtrl.GRAPHIC_CARD.INTEL_IGCL)
                        && (((GraphicCardI2CCtrl)_simI2cCtrl).GetGraphicCardType != GraphicCardI2CCtrl.GRAPHIC_CARD.AMD))
                    {
                        QueryConnectedMonitorInfo();
                    }
                    else
                    {
                        ClearConnectedMonitorInfo();
                        WriteLog("Skip optional monitor identity query on AMD/Intel path.", "debug");
                    }
                    cboConnectedMonitor.SelectedIndexChanged += cboConnectedMonitor_SelectedIndexChanged;
                    cboConnectedMonitor.Enabled = true;
                }

                if (IsConnect == false)
                {
                    WriteLog("IsConnect = false", "debug");
                    cboConnectedMonitor.Enabled = false;
                    btnRunDPISP.Enabled = false;
                    pnlConnState.BackColor = Color.Red;
                ClearConnectedMonitorInfo();
                    SetStatusMsg("Current F/W version is not compatible with this tool.", true);
                }
                _chipType = chipType;
                switch (chipType)
                {
                    case 0: { lblMTKChipType.Text = "Not Defined"; } break;
                    case 1: { lblMTKChipType.Text = "TSUM"; } break;
                    case 2: { lblMTKChipType.Text = "MST9U"; } break;
                }
                Application.DoEvents();
                DDC_GetVersion();
                DDC_SetToolFlag(false);

                int pktDelayTime = 0;
                switch (((GraphicCardI2CCtrl)_simI2cCtrl).GetGraphicCardType)
                {
                    case GraphicCardI2CCtrl.GRAPHIC_CARD.AMD:
                        {
                            if (_chipType == 2)
                            {
                                //MST9U
                                pktDelayTime = 200;
                            }
                            else
                            {
                                //TSUMxxG : 當maxPktLen=59時，低於1000時SendDataToDram會失敗。
                                pktDelayTime = 1500;
                            }
                            WriteLog("GRAPHIC_CARD.AMD", "debug");
                        }
                        break;
                    case GraphicCardI2CCtrl.GRAPHIC_CARD.NVIDIA:
                        {
                            if (_chipType == 2)
                            {
                                //MST9U
                                pktDelayTime = 200;
                            }
                            else
                            {
                                //TSUMxxG : 當maxPktLen=59時，低於1000時SendDataToDram會失敗。
                                pktDelayTime = 1000;
                            }
                            WriteLog("GRAPHIC_CARD.NVIDIA", "debug");
                        }
                        break;
                    case GraphicCardI2CCtrl.GRAPHIC_CARD.INTEL:
                    case GraphicCardI2CCtrl.GRAPHIC_CARD.INTEL_IGCL:
                        {
                            //defaultPktLen = 10;
                            if (_IsHDMIConnect)
                            {
                                //INTEL HDMI 的DDC大部分只有量到10KHz, Bus已經很慢了 所以不需要delay time.
                                pktDelayTime = 0;
                            }
                            else
                            {
                                //INTEL DP
                                if (_chipType == 2)
                                    if (_chipType == 2)
                                    {
                                        //MST9U
                                        pktDelayTime = 0;
                                    }
                                    else
                                    {
                                        //TSUMxxG 
                                        pktDelayTime = 0;
                                    }
                            }
                            WriteLog("GRAPHIC_CARD.INTEL/INTEL_IGCL", "debug");
                        }
                        break;
                }
                lblPktDelayTime.Text = pktDelayTime.ToString();
                txtPktDelayTime.Text = pktDelayTime.ToString();
            }
            else
                pnlConnState.BackColor = Color.Red;

            if (IsConnect)
            {
                if (String.IsNullOrEmpty(_binFilePath))
                    SetStatusMsg("Please select a F/W file.");
                else
                    SetStatusMsg("BIN loaded. Ready to upgrade.");
            }
            else
                SetStatusMsg("Ready");
        }

        private void btnConnect_Click(object sender, EventArgs e)
        {
            Connect();
        }

        private Byte _ver0 = 0;
        private Byte _ver1 = 0;
        private Byte _ver2 = 0;

        private void DDC_GetVersion()
        {
            Byte[] cmd1 = { 0xCC, 0x3A };
            Byte[] recv = MTKDebugCmd.DDC_Read(cmd1, 7);
            if (recv != null)
            {
                Byte[] verData = new Byte[5];
                Array.Copy(recv, 2, verData, 0, 5);
                var str = System.Text.Encoding.Default.GetString(verData);
                lblVer.Text = str;
                if ((0x2E == verData[1]) && (0x2E == verData[3]))
                {
                    _ver0 = verData[0];
                    _ver1 = verData[2];
                    _ver2 = verData[4];
                }
            }
        }
        public static byte DDC2CI_GetToolFlag()
        {
            Byte[] buf = { 0xCC, 0xA9 };

            byte flag = 0;
            Byte[] recv = MTKDebugCmd.DDC_Read(buf, 3);
            if (recv != null)
                flag = recv[2];
            return flag;
        }
        private bool IsAmdOrIntelPath()
        {
            var gpuType = ((GraphicCardI2CCtrl)_simI2cCtrl).GetGraphicCardType;
            return gpuType == GraphicCardI2CCtrl.GRAPHIC_CARD.AMD
                || gpuType == GraphicCardI2CCtrl.GRAPHIC_CARD.INTEL
                || gpuType == GraphicCardI2CCtrl.GRAPHIC_CARD.INTEL_IGCL;
        }

        private void DDC_SetToolFlag(bool en)
        {
            // AMD and Intel DDC paths do not support the tool-flag handshake command.
            // Sending it causes repeated DDC timeout errors, so skip entirely.
            if (IsAmdOrIntelPath())
            {
                frmISP.WriteLog("DDC_SetToolFlag skipped on AMD/Intel path.", "debug");
                return;
            }

            Byte[] buf = { 0xCC, 0x90, 0x00 };
            if (en)
                buf[2] = 0x01;

            MTKDebugCmd.DDC_Write(buf);
            WinIOLib.DelayMs(10);

            int RetryCnt = 0;
            while (RetryCnt <= 6)
            {
                if (Convert.ToBoolean(DDC2CI_GetToolFlag()) == en)
                {
                    RetryCnt = 0;
                    break;
                }

                WinIOLib.DelayMs(30);
                RetryCnt++;

                if ((RetryCnt % 3) == 0)
                    MTKDebugCmd.DDC_Write(buf);
            }
            if (RetryCnt > 0)
            {
                WriteLog("DDC_SetToolFlag fail.", "debug");
            }
        }
        public static byte DDC2CI_GetR2Reset51Flag()
        {
            Byte[] buf = { 0xCC, 0x89, 0x00 };

            byte flag = 0;
            Byte[] recv = MTKDebugCmd.DDC_Read(buf, 3);
            if (recv != null)
                flag = recv[2];
            return flag;
        }
        public static void DDC2CI_SetR2Reset51Flag(byte en)
        {
            Byte[] buf = { 0xCC, 0x89, 0x01, 0x00 };

            if (en == 1)
                buf[3] = en;

            MTKDebugCmd.DDC_Write(buf);
            WinIOLib.DelayMs(10);

            int RetryCnt = 0;
            while ((DDC2CI_GetR2Reset51Flag() != en) && (RetryCnt <= 3))
            {
                WinIOLib.DelayMs(30);
                RetryCnt++;

                if ((RetryCnt % 3) == 0)
                    MTKDebugCmd.DDC_Write(buf);
            }
        }
        private void DDC_GetCRC(ref UInt32 checkSum)
        {
            Byte[] buf = { 0xCC, 0xF1 };

            //check point
            //NV HDMI 1000
            //NV DP 3000
            Byte[] recv = MTKDebugCmd.DDC_Read(buf, 6);
            if (recv != null)
            {
                checkSum = (UInt32)recv[2] + (((UInt32)recv[3]) << 8) + +(((UInt32)recv[4]) << 16) + (((UInt32)recv[5]) << 24);
            }
        }

        private void DDC_SetRecvInfo(int recvSize)
        {
            Byte[] buf = { 0xCC, 0xF2, 0x00, 0x00, 0x00, 0x00 };
            buf[2] = (byte)(recvSize & 0xFF);
            buf[3] = (byte)((recvSize >> 8) & 0xFF);
            buf[4] = (byte)((recvSize >> 16) & 0xFF);
            buf[5] = (byte)((recvSize >> 24) & 0xFF);
            MTKDebugCmd.DDC_Write(buf);
        }

        private void DDC_GetRecvInfo(ref int recvSize)
        {
            Byte[] buf = { 0xCC, 0xF3 };
            Byte[] recv = MTKDebugCmd.DDC_Read(buf, 6);
            recvSize = 0;
            if (recv != null)
            {
                recvSize = (int)recv[2] + (((int)recv[3]) << 8) + +(((int)recv[4]) << 16) + (((int)recv[5]) << 24);
            }
        }

        UInt32 errDDCWr = 0;
        private void DDC_SetData(Byte[] data)
        {

            Byte[] buf = new Byte[data.Length + 2];
            buf[0] = 0xCC;
            buf[1] = 0xF4;
            Array.Copy(data, 0, buf, 2, data.Length);
            if (MTKDebugCmd.DDC_Write(buf))
            {
            }
            else
            {
                errDDCWr++;
            }
        }

        private void DDC_GetSentInfo(ref int checkSum, ref int pktCnt, int delayTime)
        {
            Byte[] buf = { 0xCC, 0xF5 };
            Byte[] recv = MTKDebugCmd.DDC_Read(buf, 8, delayTime);
            checkSum = 0;
            pktCnt = 0;
            if (recv != null)
            {
                checkSum = (int)recv[2] + (((int)recv[3]) << 8);
                pktCnt = (int)recv[4] + (((int)recv[5]) << 8) + +(((int)recv[6]) << 16) + (((int)recv[7]) << 24);
            }
        }

        private void DDC_GetISPStatus(ref int status, int delayTime = 1000)
        {
            Byte[] buf = { 0xCC, 0xF8 };
            Byte[] recv = MTKDebugCmd.DDC_Read(buf, 3, delayTime);
            status = 0;
            if (recv != null)
            {
                status = (int)recv[2];// +(((int)recv[3]) << 8);
                //pktCnt = (int)recv[4] + (((int)recv[5]) << 8) + +(((int)recv[6]) << 16) + (((int)recv[7]) << 24);
            }
        }

        private void DDC_GetImgOneCheckSum(ref int checkSum)
        {
            Byte[] buf = { 0xCC, 0xFD };
            Byte[] recv = MTKDebugCmd.DDC_Read(buf, 4, 500);
            if (recv != null)
            {
                checkSum = (int)recv[2] + (((int)recv[3]) << 8);
            }
        }

        private void DDC_SetISPImgShadow(UInt16 checkSum)
        {
            Byte[] buf = { 0xCC, 0xFC, (Byte)(checkSum & 0xFF), (Byte)(checkSum >> 8) };
            MTKDebugCmd.DDC_Write(buf);
        }

        private void DDC_GetISPImgNum(ref int imgNum)
        {
            Byte[] buf = { 0xCC, 0xF9 };
            Byte[] recv = MTKDebugCmd.DDC_Read(buf, 3);
            imgNum = 0;
            if (recv != null)
            {
                imgNum = (int)recv[2];// +(((int)recv[3]) << 8);
                //pktCnt = (int)recv[4] + (((int)recv[5]) << 8) + +(((int)recv[6]) << 16) + (((int)recv[7]) << 24);
            }
        }

        private void DDC_SetISPImgOne()
        {
            Byte[] buf = { 0xCC, 0xFA };
            MTKDebugCmd.DDC_Write(buf);
        }

        private void DDC_SetISPReboot()
        {
            Byte[] buf = { 0xCC, 0xFB };
            MTKDebugCmd.DDC_Write(buf);
        }

        private void DDC_SetSentInfo(int pktCnt)
        {
            Byte[] buf = { 0xCC, 0xF6, 0x00, 0x00, 0x00, 0x00 };
            buf[2] = (byte)(pktCnt & 0xFF);
            buf[3] = (byte)((pktCnt >> 8) & 0xFF);
            buf[4] = (byte)((pktCnt >> 16) & 0xFF);
            buf[5] = (byte)((pktCnt >> 24) & 0xFF);
            MTKDebugCmd.DDC_Write(buf);
        }

        private bool DDC_CheckMTKDevice(ref Byte chipType, int delayTime = 1000)
        {
            Random rdn = new Random();
            Byte value1 = (Byte)rdn.Next();
            Byte value2 = (Byte)rdn.Next();
            Byte sum = (Byte)(value1 + value2);
            Byte[] buf = { 0xCC, 0xFE, value1, value2 };
            Byte[] recv = MTKDebugCmd.DDC_Read(buf, 4, delayTime);

            if ((recv != null) && (sum == recv[3]))
            {
                //chipType ==> 0 : Not Define, 1: TSUM, 2:MST9U 
                chipType = recv[2];
                if(chipType==2)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            return false;
        }
        private bool DDC_CheckClientInfo(ref Byte ClientInfo, int delayTime = 1000)
        {
            Random rdn = new Random();
            Byte value1 = (Byte)rdn.Next();
            Byte value2 = (Byte)rdn.Next();
            Byte sum = (Byte)(value1 + value2);
            Byte[] buf = { 0xCC, 0xE0, value1, value2 };
            Byte[] recv = MTKDebugCmd.DDC_Read(buf, 4, delayTime);

            if ((recv != null) && (sum == recv[3]))
            {
                //Client info ==> 0: MSI, 1 : ACER
                ClientInfo = recv[2];
                if(ClientInfo==0)
                {
                    return true;
                }
                else if(ClientInfo==1)
                {
                    return false;
                }
            }
            return false;
        }
        private bool DDC_CheckPanelInfo(ref Byte PanelInfo, int delayTime = 1000)
        {
            Random rdn = new Random();
            Byte value1 = (Byte)rdn.Next();
            Byte value2 = (Byte)rdn.Next();
            Byte sum = (Byte)(value1 + value2);
            Byte[] buf = { 0xCC, 0xE1, value1, value2 };
            Byte[] recv = MTKDebugCmd.DDC_Read(buf, 4, delayTime);

            if ((recv != null) && (sum == recv[3]))
            {
                //Panel info ==> 118: Panel_SG2701D02_1_eDP, 1 : ACER
                PanelInfo = recv[2];
                if(PanelInfo==118)
                {
                     return true;
                }   
                 else
                {
                    return false;       
                }
            }
            return false;
        }
        private void DDC_RunISP(UInt16 checkSum)
        {
            Byte[] buf = { 0xCC, 0xF7, (Byte)(checkSum & 0xFF), (Byte)(checkSum >> 8) };
            MTKDebugCmd.DDC_Write(buf);
        }

        class FlashSectorInfo
        {
            public byte[] data;
            public bool IsEmpty;

            public FlashSectorInfo()
            {
                data = new byte[_flashSectorSize];
                Array.Clear(data, 0, data.Length);
                IsEmpty = false;
            }

            public void Clear()
            {
                for (int idx = 0; idx < _flashSectorSize; idx++)
                {
                    data[idx] = 0xFF;
                }
            }

            public void CheckEmpty()
            {
                IsEmpty = true;
                for (int idx = 0; idx < _flashSectorSize; idx++)
                {
                    if (data[idx] != 0xFF)
                    {
                        IsEmpty = false;
                        break;
                    }
                }
            }

            public int CheckSum()
            {
                int checkSum = 0;
                for (int idx = 0; idx < _flashSectorSize; idx++)
                {
                    checkSum += data[idx];
                }
                return checkSum & 0xFFFF;
            }
        }

        private bool SetSentInfo(int sentLen)
        {
            const int MaxRetryCnt = 10;
            int retryCnt = 0;
            int rdCheckSum = 0;
            int rdPktCnt = 0;
            while (retryCnt++ < MaxRetryCnt)
            {
                DDC_SetSentInfo(sentLen);
                WinIOLib.DelayMs(1);
                DDC_GetSentInfo(ref rdCheckSum, ref rdPktCnt, 500);
                if (sentLen == rdPktCnt)
                {
                    return true;
                }
            }
            return false;
        }

        private void AdjustPacketParam(int defaultPktLen, ref int PktLen, int defaultPktDelayTime, ref int pktDelayTime, bool bOnlyAdjustDelaytime = false) //Decide PktLen first as it's limitation of GraphicsCard
        {
            Random rdn = new Random();
            int checkSum = 0;
            int rdCheckSum = 0, rdPktCnt = 0;
            int prePktLen = 0, maxPktLen = defaultPktLen, minPktLen = 10;
            int prePktDelayTime = defaultPktDelayTime, maxPktDelayTime = defaultPktDelayTime, minPktDelayTime = 0;
            PktLen = defaultPktLen;

            try
            {
                if (!bOnlyAdjustDelaytime)
                {
                    while (prePktLen != PktLen)
                    {
                        WriteLog("Adaptive PktLen: " + PktLen.ToString(), "debug");
                        prePktLen = PktLen;
                        checkSum = 0;
                        byte[] data = new byte[PktLen];
                        for (int i = 0; i < PktLen; i++)
                        {
                            data[i] = (Byte)rdn.Next();
                            checkSum += data[i];
                        }
                        DDC_SetData(data);
                        WinIOLib.DelayUs(maxPktDelayTime);
                        DDC_GetSentInfo(ref rdCheckSum, ref rdPktCnt, 500);
                        if ((PktLen != rdPktCnt) || (checkSum != rdCheckSum))
                        {
                            DDC_GetSentInfo(ref rdCheckSum, ref rdPktCnt, 500);
                        }
                        if ((PktLen != rdPktCnt) || (checkSum != rdCheckSum))
                        {
                            DDC_GetSentInfo(ref rdCheckSum, ref rdPktCnt, 500);
                        }
                        if ((PktLen != rdPktCnt) || (checkSum != rdCheckSum))
                        {
                            DDC_GetSentInfo(ref rdCheckSum, ref rdPktCnt, 500);
                        }
                        //reset sent info
                        if (SetSentInfo(0) == false)
                        {
                            SetStatusMsg("Adaptive PktLen fail.");
                            SetISPStatus(false, "Adaptive PktLen fail.");
                            return;
                        }

                        if ((PktLen != rdPktCnt) || (checkSum != rdCheckSum))
                        {
                            maxPktLen = PktLen;
                            PktLen = (PktLen + minPktLen) / 2;
                        }
                        else if (PktLen == defaultPktLen)
                            break;
                        else
                        {
                            minPktLen = PktLen;
                            PktLen = (PktLen + maxPktLen) / 2;
                        }
                    }
                    if ((PktLen != rdPktCnt) || (checkSum != rdCheckSum))
                    {
                        PktLen = 10;
                    }
                }

                int adjustCount = 10;
                pktDelayTime = 0;
                while (pktDelayTime < defaultPktDelayTime && (prePktDelayTime != pktDelayTime))
                {
                    prePktDelayTime = pktDelayTime;
                    checkSum = 0;

                    byte[] data = new byte[PktLen];
                    for (int i = 0; i < PktLen; i++)
                    {
                        data[i] = (Byte)rdn.Next();
                        checkSum += data[i];
                    }
                    for (int i = 0; i < adjustCount; i++)
                    {
                        DDC_SetData(data);
                        WinIOLib.DelayUs(pktDelayTime);
                    }
                    checkSum *= adjustCount;
                    checkSum = checkSum & 0xFFFF;

                    DDC_GetSentInfo(ref rdCheckSum, ref rdPktCnt, 500);
                    if ((PktLen * adjustCount != rdPktCnt) || (checkSum != rdCheckSum))
                    {
                        DDC_GetSentInfo(ref rdCheckSum, ref rdPktCnt, 500);
                    }
                    if ((PktLen * adjustCount != rdPktCnt) || (checkSum != rdCheckSum))
                    {
                        DDC_GetSentInfo(ref rdCheckSum, ref rdPktCnt, 500);
                    }
                    if ((PktLen * adjustCount != rdPktCnt) || (checkSum != rdCheckSum))
                    {
                        DDC_GetSentInfo(ref rdCheckSum, ref rdPktCnt, 500);
                    }
                    if ((PktLen * adjustCount != rdPktCnt) || (checkSum != rdCheckSum))
                    {
                        minPktDelayTime = pktDelayTime;
                        pktDelayTime = (pktDelayTime + maxPktDelayTime) / 2;
                    }
                    else
                    {
                        maxPktDelayTime = pktDelayTime;
                        pktDelayTime = (pktDelayTime + minPktDelayTime) / 2;
                    }
                    //reset sent info
                    if (SetSentInfo(0) == false)
                    {
                        SetStatusMsg("Adaptive pktDelayTime fail.");
                        SetISPStatus(false, "Adaptive pktDelayTime fail.");
                        return;
                    }
                    WriteLog("Adaptive pktDelayTime: " + pktDelayTime.ToString() + "us.", "debug");
                }
            }
            catch (Exception e)
            {
                frmISP.WriteLog("Exception: " + e.Message, "debug");
                ((GraphicCardI2CCtrl)_simI2cCtrl).ReCreateGraphicCardCtrl();
                defaultPktLen = 10;
                AdjustPacketParam(defaultPktLen, ref maxPktLen, 10000, ref pktDelayTime, true);
            }
        }

        private int SendSectorData(FlashSectorInfo si, int failCnt) //leave it for debug
        {
            int maxPktLen = 59;
            int pktDelayTime = 1000;
            bool canCheckIdleStatus = false;
            switch (((GraphicCardI2CCtrl)_simI2cCtrl).GetGraphicCardType)
            {
                case GraphicCardI2CCtrl.GRAPHIC_CARD.AMD:
                    {
                        if (_IsHDMIConnect)
                        {
                        }
                        else
                        {
                            maxPktLen = 11; //Need to check
                        }

                        if (_chipType == 2)
                        {
                            pktDelayTime = 200;
                        }
                        else
                        {
                            //當maxPktLen=59時，低於1000時SendDataToDram會失敗。
                            pktDelayTime = 1500;
                        }
                    }
                    break;
                case GraphicCardI2CCtrl.GRAPHIC_CARD.NVIDIA:
                    {
                        if (_chipType == 2)
                        {
                            pktDelayTime = 200;
                        }
                        else
                        {
                            //當maxPktLen=59時，低於1000時SendDataToDram會失敗。
                            pktDelayTime = 1000;
                        }
                    }
                    break;
                case GraphicCardI2CCtrl.GRAPHIC_CARD.INTEL_IGCL:
                case GraphicCardI2CCtrl.GRAPHIC_CARD.INTEL:
                    if (_IsHDMIConnect)
                    {
                        //INTEL HDMI 的DDC大部分只有量到10KHz, Bus已經很慢了 所以不需要delay time.
                        pktDelayTime = 0;
                        maxPktLen = 11;
                    }
                    else
                    {
                        maxPktLen = 11; //Need to check
                        //INTEL DP
                        if (_chipType == 2)
                        {
                            pktDelayTime = 0;
                        }
                        else
                        {
                            pktDelayTime = 0;
                        }
                    }
                    break;
            }

            //Adaptive packet lenght adjustment for different OS/GPU.
            if (failCnt > 0)
            {
                maxPktLen = maxPktLen - failCnt * 5;
            }

            if (maxPktLen < 10) //6E 51 Length CC F4 Data Chksum
            {
                maxPktLen = 10;
            }

            int pktLen = 0;
            int sentLen = 0;
            while (sentLen < si.data.Length)
            {
                if ((si.data.Length - sentLen) > maxPktLen)
                    pktLen = maxPktLen;
                else
                    pktLen = si.data.Length - sentLen;

                byte[] data = new byte[pktLen];

                Array.Copy(si.data, sentLen, data, 0, pktLen);
                DDC_SetData(data);
                if (canCheckIdleStatus)
                {
                    CheckISPIdleStatus();
                }
                else
                {
                    WinIOLib.DelayUs(pktDelayTime);
                }

                sentLen += pktLen;
            }
            return si.CheckSum();
        }
        private int SendSectorData(FlashSectorInfo si, int failCnt, int maxPktLen, int pktDelayTime)
        {
            bool canCheckIdleStatus = false;

            //Adaptive packet lenght adjustment for different OS/GPU.
            if (failCnt > 0)
            {
                if (pktDelayTime == 0)
                    pktDelayTime = 200;

                pktDelayTime = pktDelayTime + 100 * failCnt;
            }

            int pktLen = 0;
            int sentLen = 0;
            while (sentLen < si.data.Length)
            {
                if ((si.data.Length - sentLen) > maxPktLen)
                    pktLen = maxPktLen;
                else
                    pktLen = si.data.Length - sentLen;

                byte[] data = new byte[pktLen];

                Array.Copy(si.data, sentLen, data, 0, pktLen);
                DDC_SetData(data);
                if (canCheckIdleStatus)
                {
                    CheckISPIdleStatus();
                }
                else
                {
                    WinIOLib.DelayUs(pktDelayTime);
                }

                sentLen += pktLen;
            }
            return si.CheckSum();
        }

        List<FlashSectorInfo> flashSectorInfo = new List<FlashSectorInfo>();
        private void btnRunDPISP_Click(object sender, EventArgs e)
        {
            WriteLog("pktDelayTime: " + txtPktDelayTime.Text, "debug");
            DDC_SetToolFlag(true);
            if (_binFilePath == String.Empty)
            {
                WriteLog("binFilePath is NULL", "debug");
                return;
            }
            WriteLog("Start ISP", "debug");
            //int totalFailCnt1 = 0;
            int failCnt = 0;
            int checkSumFailCnt = 0;
            int lenFailCnt = 0;

            SetISPStatus(true);
            const int MaxRetryCnt = 30;
            MTKDebugCmd.InitI2CCtrl(_simI2cCtrl);
            Byte[] bin = InitBinaryFile(_binFilePath);
            int retryCnt = 0;
            int rdCheckSum = 0, rdPktCnt = 0;
            int checkSum = 0, sentLen = 0, pktLen = 0;
            int totalBytes = bin.Length;

            int start = GetTickCount();
            int end = 0;

            if (flashSectorInfo.Count == 0)
            {
                for (int idx = 0; idx < bin.Length; idx += _flashSectorSize)
                {
                    if ((bin.Length - idx) >= _flashSectorSize)
                        pktLen = _flashSectorSize;
                    else
                        pktLen = bin.Length - idx;

                    FlashSectorInfo sector = new FlashSectorInfo();
                    sector.Clear();
                    Array.Copy(bin, idx, sector.data, 0, pktLen);
                    sector.CheckEmpty();
                    flashSectorInfo.Add(sector);
                }
            }

            QueryConnectedMonitorInfo();

            toolStripProgressBar1.Maximum = flashSectorInfo.Count;
            toolStripProgressBar1.Value = 0;
            int imgNum = 0;
            retryCnt = 0;
            DDC_GetISPImgNum(ref imgNum);
            if (imgNum == 1)
            {
                MessageBox.Show(this, "Update firmware will continue without reboot at startup. Display will reboot after update success.", this.Text);
            }

            IsConnect = false;
            Byte chipType = 0;
            while ((IsConnect == false) && (retryCnt++ < MaxRetryCnt))
            {
                IsConnect = DDC_CheckMTKDevice(ref chipType);
                if (IsConnect)
                {
                    break;
                }
               IsConnect = DDC_CheckClientInfo(ref chipType);
                if (IsConnect)
                {
                    break;
                }
               IsConnect = DDC_CheckPanelInfo(ref chipType);
                if (IsConnect)
                {
                    break;
                }
                WinIOLib.DelayMs(1000);
            }

            if (IsConnect == false)
            {
                SetStatusMsg("Initialize ISP connection fail.");
                SetISPStatus(false, "Initialize ISP connection fail.");
                return;
            }

            _chipType = chipType;
            switch (chipType)
            {
                case 0: { lblMTKChipType.Text = "Not Defined"; } break;
                case 1: { lblMTKChipType.Text = "TSUM"; } break;
                case 2: { lblMTKChipType.Text = "MST9U"; } break;
            }

    //    private bool DDC_CheckClientInfo(ref Byte ClientInfo, int delayTime = 1000)

            if (!IsModelNameMatch(_u8ModelName))
            {
                string modelErr = GetModelMismatchMessage(_u8ModelName);
                SetStatusMsg(modelErr);
                SetISPStatus(false, modelErr);
                return;
            }
            if (!IsChipNameMatch(_u8ChipName))
            {
                SetStatusMsg(Properties.Resources.ChipNameErr);
                SetISPStatus(false, Properties.Resources.ChipNameErr);
                return;
            }
            if (!IsPanelNameMatch(_u8PanelName))
            {
                SetStatusMsg(Properties.Resources.PanelNameErr);
                SetISPStatus(false, Properties.Resources.PanelNameErr);
                return;
            }
            if (!IsBoardNameMatch(_u8BoardName))
            {
                SetStatusMsg(Properties.Resources.BoardNameErr);
                SetISPStatus(false, Properties.Resources.BoardNameErr);
                return;
            }

            Application.DoEvents();
            //init file info            
            int rdSize = 0;
            retryCnt = 0;
            while (retryCnt++ < MaxRetryCnt)
            {
                DDC_SetRecvInfo(bin.Length);
                WinIOLib.DelayMs(500);
                DDC_GetRecvInfo(ref rdSize);
                if (rdSize == bin.Length)
                    break;
            }

            if (rdSize != bin.Length)
            {
                SetStatusMsg("Initialize file Information fail.");
                SetISPStatus(false, "Initialize file Information fail.");
                return;
            }

            ///////Compare ISP//////////////////
            List<FlashSectorInfo> rootFlash = new List<FlashSectorInfo>();
            Boolean canExecuteCompareISP = false;
            UInt16 imgCheckSum = 0;
            String path = Directory.GetCurrentDirectory();
            path += "\\rootimage.bin";
            if (System.IO.File.Exists(path))
            {
                Byte[] rootBin = InitBinaryFile(path);
                for (int idx = 0; idx < rootBin.Length; idx += _flashSectorSize)
                {
                    if ((rootBin.Length - idx) >= _flashSectorSize)
                        pktLen = _flashSectorSize;
                    else
                        pktLen = rootBin.Length - idx;

                    for (int sidx = 0; sidx < pktLen; sidx++)
                    {
                        imgCheckSum += rootBin[idx + sidx];
                    }
                    FlashSectorInfo sector = new FlashSectorInfo();
                    sector.Clear();
                    Array.Copy(rootBin, idx, sector.data, 0, pktLen);
                    sector.CheckEmpty();
                    rootFlash.Add(sector);
                }
                

                UInt32 crc = 0, rdCrc = 0;
                int rdImgOneCheckSum = 0;
                for (int idx = 0; idx < 21; idx++)
                {
                    crc += rootFlash[255].data[0xFE0 + idx];
                }
                for (int retry = 0; retry < 4; retry++)
                {
                    DDC_GetCRC(ref rdCrc);
                    WinIOLib.DelayMs(500);
                    if (crc == rdCrc)
                    {
                        canExecuteCompareISP = true;
                        break;
                    }
                }

                if (canExecuteCompareISP)
                {
                    DDC_SetISPImgShadow(imgCheckSum);
                    SetStatusMsg("Initalize Fast ISP procedure...");
                    WinIOLib.DelayMs(17000);
                    for (int retry = 0; retry < 20; retry++)
                    {
                        DDC_GetImgOneCheckSum(ref rdImgOneCheckSum);
                        if (imgCheckSum == rdImgOneCheckSum)
                        {
                            canExecuteCompareISP = true;
                            break;
                        }
                        else
                        {
                            canExecuteCompareISP = false;
                            WinIOLib.DelayMs(1000);
                        }
                    }
                }
            }

       //     SetStatusMsg("Transfer Parameter adjusting ...");
            int maxPktLen = 0, pktDelayTime = 0;
            AdjustPacketParam(defaultPktLen, ref maxPktLen, 10000, ref pktDelayTime);
            WriteLog("maxPktLen = " + Convert.ToString(maxPktLen) + ", pktDelayTime = " + Convert.ToString(pktDelayTime) + "", "debug");
#if DEBUG
            if (Convert.ToInt32(txtPktDelayTime.Text) > 0)
                pktDelayTime = Convert.ToInt32(txtPktDelayTime.Text);
#endif
            retryCnt = 0;
            while (retryCnt++ < MaxRetryCnt)
            {
                DDC_SetRecvInfo(bin.Length);
                WinIOLib.DelayMs(500);
                DDC_GetRecvInfo(ref rdSize);
                if (rdSize == bin.Length)
                    break;
            }
            //Send data          
            FlashSectorInfo si;
            FlashSectorInfo rootSi;
            Boolean IsDiffSector = false;
            start = GetTickCount();
            int wroteSectorCnt = 0;
            retryCnt = 0;
            for (int cnt = 0; cnt < flashSectorInfo.Count; cnt++)
            {
                IsDiffSector = false;
                si = flashSectorInfo[cnt];

                if (canExecuteCompareISP)
                {
                    rootSi = rootFlash[cnt];
                    for (int sIdx = 0; sIdx < 4096; sIdx++)
                    {
                        if (si.data[sIdx] != rootSi.data[sIdx])
                        {
                            IsDiffSector = true;
                            break;
                        }
                    }

                    if (IsDiffSector)
                    {
                        checkSum += SendSectorData(si, failCnt, maxPktLen, pktDelayTime);
                        checkSum &= 0xFFFF;
                        wroteSectorCnt++;
                    }
                    else
                    {
                        //bypass empty sector
                        if (SetSentInfo(sentLen + _flashSectorSize) == false)
                        {
                            SetStatusMsg("Send file fail.");
                            SetISPStatus(false, "Send file fail.");
                            return;
                        }
                        checkSum = 0;
                        //WinIOLib.DelayMs(50);
                    }
                }
                else
                {
                    if (si.IsEmpty)
                    {
                        //bypass empty sector
                        if (SetSentInfo(sentLen + _flashSectorSize) == false)
                        {
                            SetStatusMsg("Send file fail.");
                            SetISPStatus(false, "Send file fail.");
                            return;
                        }
                        checkSum = 0;
                    }
                    else
                    {
                        checkSum += SendSectorData(si, failCnt, maxPktLen, pktDelayTime);
                        checkSum &= 0xFFFF;
                    }
                }

                sentLen += _flashSectorSize;

                DDC_GetSentInfo(ref rdCheckSum, ref rdPktCnt, 500);
                if ((sentLen != rdPktCnt) || (checkSum != rdCheckSum))
                {
                    DDC_GetSentInfo(ref rdCheckSum, ref rdPktCnt, 500);
                }
                if ((sentLen != rdPktCnt) || (checkSum != rdCheckSum))
                {
                    DDC_GetSentInfo(ref rdCheckSum, ref rdPktCnt, 500);
                }
                if ((sentLen != rdPktCnt) || (checkSum != rdCheckSum))
                {
                    DDC_GetSentInfo(ref rdCheckSum, ref rdPktCnt, 500);
                }
                if (sentLen != rdPktCnt)
                {
                    lenFailCnt++;
                }
                if (checkSum != rdCheckSum)
                {
                    checkSumFailCnt++;
                }
                if ((sentLen != rdPktCnt) || (checkSum != rdCheckSum))
                {
                    retryCnt++;
                    failCnt++;
                    sentLen -= _flashSectorSize;
                    checkSum = 0;

                    if (sentLen < 0)
                        sentLen = 0;

                    if (cnt >= 0)
                        cnt -= 1;

                    //reset sent info
                    if (SetSentInfo(sentLen) == false)
                    {
                        SetStatusMsg("Send file fail.");
                        SetISPStatus(false, "Send file fail.");
                        return;
                    }

                    if (retryCnt > MaxRetryCnt)
                    {
                        SetStatusMsg("Send file fail.");
                        SetISPStatus(false, "Send file fail.");
                        return;
                    }
                }
                else
                {
                    retryCnt = 0;
                }

                if (cnt < 0)
                    toolStripProgressBar1.Value = 0;
                else
                    toolStripProgressBar1.Value = cnt;
                end = GetTickCount() - start;

                if (sentLen >= bin.Length)
                    sentLen = bin.Length;


                if ((cnt == 0) && (end > 4000)) //terminate ISP when 4K bytes per sec large than 4 sec 
                {
                    //SetStatusMsg("Procedure is terminated, because data transfer speed is abnormal.");
                    //btnUploadFlash.Enabled = true;
                    //SetISPStatus(false);
                    //return;
                }

           //     SetStatusMsg("Sent Bytes : " + sentLen.ToString() + ", " + "Elapsed Time : " + end.ToString() + " ms");
     //      _u8ModelName = "mmmmmmmmmmmmm";
     //      SetStatusMsg("Sent Bytes : " + sentLen.ToString() + ", " + "Elapsed Time : " + end.ToString() + " ms. Model: " + _u8ModelName);
                Application.DoEvents();
            }

            UInt16 fileCheckSum = 0;
            lblTransferTime.Text = end.ToString();
#if AUTO_SEARCH
            Transfer_Time = end;
            Total_Transfer_Time += (ulong)end;
#endif
            for (int idx = 0; idx < bin.Length; idx++)
            {
                fileCheckSum += bin[idx];
            }


            //Execute ISP   
            DDC_RunISP(fileCheckSum);
            WinIOLib.DelayMs(1000);

            toolStripProgressBar1.Value = flashSectorInfo.Count;
            Application.DoEvents();
            end = GetTickCount() - start;
   //         SetStatusMsg("Sent Bytes : " + sentLen.ToString() + ", " + "Elapsed Time : " + end.ToString() + " ms");
       //SetStatusMsg("Sent Bytes : " + sentLen.ToString() + ", " + "Elapsed Time : " + end.ToString() + " ms");
     // SetStatusMsg("Read Bin OK. Model: " + _u8ModelName);
            Application.DoEvents();

            //check ddc is alive until system update flash finish
            retryCnt = 0;
            UInt32 tmpCRC = 0x89ABCDEF;
            int status = 0;
            int binLen = bin.Length / 1024;
            int waitUpdateFWCnt = 120;
            if (binLen <= 512)
            {
                waitUpdateFWCnt = 120;
            }
            else if (binLen <= 1024)
            {
                waitUpdateFWCnt = 240;
            }
            else if (binLen <= 2048)
            {
                waitUpdateFWCnt = 480;
            }
            else if (binLen <= 4096)
            {
                waitUpdateFWCnt = 960;
            }

            while (retryCnt++ < waitUpdateFWCnt)
            {
                WinIOLib.DelayMs(1000);

                SetStatusMsg("Updating firmware..., " + "Elapsed Time : " + end.ToString() + " ms");
                Application.DoEvents();
                //int pktDelayTime = Convert.ToInt32(txtPktDelayTime.Text);
                IsConnect = DDC_CheckMTKDevice(ref chipType/*, pktDelayTime*/);
                if (IsConnect == false)
                    continue;
                //DDC_GetSentInfo(ref rdCheckSum, ref rdPktCnt);
                DDC_GetISPStatus(ref status/*, pktDelayTime*/);
                if ((status == 2) || (status == 1))//0:Init, 1: fail, 2: success
                    break;
            }

            //how to check fw is update success? 
            end = GetTickCount() - start;
            if (status == 2)
            {
                Connect();
                SetStatusMsg("Update firmware success, " + "Elapsed Time : " + end.ToString() + " ms");
                //MessageBox.Show(this, "Update firmware success. Please reset the power of display.", this.Text);
            }
            else
            {
                SetStatusMsg("Update firmware fail, " + "Elapsed Time : " + end.ToString() + " ms",true);
            }

            SetISPStatus(false);

            if (status == 2)
            {
                MessageBox.Show(this, "Update fimware is success, Display will reboot. .", this.Text);
                lblUpdatingFWmsg.Visible = false;
                DDC_SetISPReboot();
            }
        }

        Boolean CheckISPIdleStatus()
        {
            int status = 0;
            int cnt = 0;
            while ((status != 0x99) && (cnt++ < 10))
            {
                DDC_GetISPStatus(ref status, 100);
            }

            if (status == 0x99)
                return true;
            else
                return false;
        }

        private static string resultFolder = System.IO.Directory.GetCurrentDirectory() + "\\";
        private static System.IO.StreamWriter _logWriter = null;
        private static string _logPath = null;
        private static readonly object _logLock = new object();
        [Conditional("DEBUG")]
        public static void WriteLog(string str, string level)
        {
            try
            {
                if (!System.IO.Directory.Exists(resultFolder))
                    System.IO.Directory.CreateDirectory(resultFolder);

                string path = resultFolder + (level == "" ? "" : level + "_") + DateTime.Now.ToString("yyyy-MM-dd") + ".log";

                lock (_logLock)   // >>> 新增：同进程内串行化，避免自家多线程争用
                {
                    if (_logWriter == null || _logPath != path)
                    {
                        if (_logWriter != null)
                        {
                            try { _logWriter.Flush(); _logWriter.Close(); } catch { }
                        }
                        _logPath = path;
                        var stream = new FileStream(path, FileMode.Append, FileAccess.Write, FileShare.ReadWrite);
                        _logWriter = new System.IO.StreamWriter(stream);
                    }
                    _logWriter.WriteLine(DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss.fff: ") + str);
                    _logWriter.Flush();
                }
            }
            catch (Exception)   // >>> 改动：IOException → Exception，吞掉一切日志异常
            {
                // 日志写入失败绝不能影响主流程。可选：降级写到 Debug 输出
                try { System.Diagnostics.Debug.WriteLine("[LOG-FALLBACK] " + str); } catch { }
            }
        }

        private void cboConnectedMonitor_SelectedIndexChanged(object sender, EventArgs e)
        {
            WriteLog("[cboConnectedMonitor_SelectedIndexChanged] cboConnectedMonitor.SelectedIndex = " + cboConnectedMonitor.SelectedIndex, "debug");
            ((GraphicCardI2CCtrl)_simI2cCtrl).SetConnectedMonitorIndex((Byte)cboConnectedMonitor.SelectedIndex);
            DDC_SetToolFlag(true);
            Byte chipType = 0;
            IsConnect = DDC_CheckMTKDevice(ref chipType);
            int retry = 0;
            while ((IsConnect == false) && (retry++ < 30))
            {
                IsConnect = DDC_CheckMTKDevice(ref chipType);
            }
            if (IsConnect == true)
            {
                pnlConnState.BackColor = Color.LightGreen;
                if ((((GraphicCardI2CCtrl)_simI2cCtrl).GetGraphicCardType != GraphicCardI2CCtrl.GRAPHIC_CARD.INTEL)
                    && (((GraphicCardI2CCtrl)_simI2cCtrl).GetGraphicCardType != GraphicCardI2CCtrl.GRAPHIC_CARD.INTEL_IGCL))
                {
                    QueryConnectedMonitorInfo();
                }
                else
                {
                    ClearConnectedMonitorInfo();
                    WriteLog("Skip optional monitor identity query on Intel path.", "debug");
                }
            }
            else
            {
                pnlConnState.BackColor = Color.Red;
                _chipType = 0;
                ClearConnectedMonitorInfo();
            }

            _chipType = chipType;
            switch (chipType)
            {
                case 0: { lblMTKChipType.Text = "Not Defined"; } break;
                case 1: { lblMTKChipType.Text = "TSUM"; } break;
                case 2: { lblMTKChipType.Text = "MST9U"; } break;
            }
            Application.DoEvents();
            if ((((GraphicCardI2CCtrl)_simI2cCtrl).GetGraphicCardType != GraphicCardI2CCtrl.GRAPHIC_CARD.INTEL)
                && (((GraphicCardI2CCtrl)_simI2cCtrl).GetGraphicCardType != GraphicCardI2CCtrl.GRAPHIC_CARD.INTEL_IGCL))
            {
                DDC_GetVersion();
            }
            else
            {
                WriteLog("Skip optional firmware version query on Intel path.", "debug");
            }
            Application.DoEvents();
            DDC_SetToolFlag(false);
            lblConnectedPort.Text = ((GraphicCardI2CCtrl)_simI2cCtrl).GetConnectedPortTypeByIdx((Byte)cboConnectedMonitor.SelectedIndex).ToString();
            Application.DoEvents();
        }
        public static Byte[] InitBinaryFile(String filepath)
        {
            Byte[] rdBuf = null;

            if (File.Exists(filepath) == false)
                return null;

            UInt32 lastDataPosition = 0;
            using (BinaryReader reader = new BinaryReader(File.Open(filepath, FileMode.Open)))
            {
                rdBuf = reader.ReadBytes((int)reader.BaseStream.Length);
            }

            UInt32 cnt = 0;
            UInt16 checkSum = 0;
            Byte data = 0;
            while (cnt < rdBuf.Length)
            {
                data = rdBuf[cnt];
                checkSum += data;
                if (data != 0xFF)
                {
                    lastDataPosition = cnt;
                }
                cnt++;
            }

            if ((lastDataPosition & 0xFFF) > 0)
                lastDataPosition = (lastDataPosition + 0x1000) & 0xFFF000;
            UInt32 _progSize = 0;
            _progSize = lastDataPosition;
            Byte[] wrBuf = new Byte[_progSize];
            for (int idx = 0; idx < wrBuf.Length; idx++)
            {
                wrBuf[idx] = 0xFF;
            }
            int copyLen = rdBuf.Length;
            if (wrBuf.Length < rdBuf.Length)
            {
                copyLen = wrBuf.Length;
                System.Windows.Forms.MessageBox.Show("rd len : " + rdBuf.Length.ToString() + ", wr len" + wrBuf.Length.ToString());
            }
            Array.Copy(rdBuf, wrBuf, copyLen);
            return wrBuf;
        }

        public static byte[] ReadBinSection(string filePath, long offset, int length)
        {
            if (!File.Exists(filePath))
                throw new FileNotFoundException("Bin file not found.", filePath);


            byte[] buffer = new byte[length];
            using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
            {
                if (offset > fs.Length - 1)
                    throw new ArgumentOutOfRangeException("offset", "Offset is beyond file length.");

                fs.Seek(offset, SeekOrigin.Begin);
                int bytesRead = fs.Read(buffer, 0, length);
                if (bytesRead < length)
                {
                    // If file is shorter than expected, resize buffer
                    Array.Resize(ref buffer, bytesRead);
                }
            }
            return buffer;
        }
    }

}

