using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO.Ports;
using System.IO;
using AR;

namespace Project
{

    public partial class Form1 : Form
    {


        public Form1()
        {
            InitializeComponent();

            PUB.init();

            // 현재 설치되어있는 시리얼포트목록을 조회해서 cmbPort... 컨트롤에 미리 입력한다.
            RefreshPortList();

            // 기본 BaudRate 설정
            tbBaudAGV.Text = "115200";
            tbBaudBMS.Text = "9600";
        }

        /// <summary>
        /// 시리얼 포트 목록을 새로고침하여 콤보박스에 추가
        /// </summary>
        private void RefreshPortList()
        {
            try
            {
                string[] ports = SerialPort.GetPortNames();

                cmbPortAGV.Items.Clear();
                cmbPortBMS.Items.Clear();

                foreach (string port in ports)
                {
                    cmbPortAGV.Items.Add(port);
                    cmbPortBMS.Items.Add(port);
                }



                addLog($"포트 목록 새로고침 완료: {ports.Length}개 포트 발견");
            }
            catch (Exception ex)
            {
                addLog($"포트 목록 조회 오류: {ex.Message}");
            }
        }

        /// <summary>
        /// 설정파일에서 데이터를 읽어서 컨트롤에 설정한다
        /// </summary>
        void LoadSetting()
        {
            try
            {

                // AGV 설정
                if (!string.IsNullOrEmpty(PUB.setting.Port_AGV))
                {
                    this.cmbPortAGV.Text = PUB.setting.Port_AGV;
                }
                tbBaudAGV.Text = PUB.setting.Baud_AGV.ToString();

                // BMS 설정
                if (!string.IsNullOrEmpty(PUB.setting.Port_BAT))
                {
                    cmbPortBMS.Text = PUB.setting.Port_BAT;
                }
                tbBaudBMS.Text = PUB.setting.Baud_BAT.ToString();

                addLog("설정 파일 로드 완료");
            }
            catch (Exception ex)
            {
                addLog($"설정 로드 오류: {ex.Message}");
            }
        }

        /// <summary>
        /// 현재 설정된 값을 확인하고 셋팅파일에 기록한다
        /// </summary>
        void SaveSetting()
        {
            try
            {
                // 현재 UI 값을 설정 객체에 반영
                PUB.setting.Port_AGV = cmbPortAGV.SelectedItem?.ToString() ?? "";
                PUB.setting.Baud_AGV = int.TryParse(tbBaudAGV.Text, out int agvBaud) ? agvBaud : 115200;
                PUB.setting.Port_BAT = cmbPortBMS.SelectedItem?.ToString() ?? "";
                PUB.setting.Baud_BAT = int.TryParse(tbBaudBMS.Text, out int bmsBaud) ? bmsBaud : 9600;

                // 파일에 저장
                PUB.setting.Save();

                addLog("설정 파일 저장 완료");
            }
            catch (Exception ex)
            {
                addLog($"설정 저장 오류: {ex.Message}");
            }
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            LoadSetting();
        }

        private void btLoadSetting_Click(object sender, EventArgs e)
        {
            LoadSetting();
        }

        private void btSaveSetting_Click(object sender, EventArgs e)
        {
            SaveSetting();
        }

        private void btTestAGV_Click(object sender, EventArgs e)
        {
            // AGV 용 프로토콜을 전송해서 올바르게 피드백이 오는지 확인한다
            // AGV Protocol: STX(0x02) + Data + ETX(0x03)

            if (cmbPortAGV.SelectedItem == null)
            {
                addLog("[AGV] 포트를 선택해주세요");
                return;
            }

            if (!int.TryParse(tbBaudAGV.Text, out int baudRate))
            {
                addLog("[AGV] BaudRate가 올바르지 않습니다");
                return;
            }

            try
            {
                addLog($"[AGV] 테스트 시작: {cmbPortAGV.SelectedItem} @ {baudRate}bps");


                using (arDev.Narumi port = new arDev.Narumi())
                {
                    port.PortName = cmbPortAGV.SelectedItem.ToString();
                    port.BaudRate = baudRate;
                    port.Message += (s1, e1) =>{
                        addLog($"[AGV] 응답 수신: {e1.Message}");
                    };
                    port.Open();
                    addLog($"[AGV] 포트 열기 성공");

                    DateTime sendtime = DateTime.Now;
                    port.AGVMoveStop("test");//
                    addLog($"[AGV] 테스트 명령 전송:");

                    // 응답 대기
                    System.Threading.Thread.Sleep(1200);


                    if (port.lastRecvTime > sendtime)
                    {

                        // STX, ETX 확인
                        //if (response.Length >= 2 && response[0] == 0x02 && response[response.Length - 1] == 0x03)
                        //{
                        addLog($"[AGV] ✓ 통신 성공 - 올바른 프로토콜 응답");
                        //}
                        //else
                        //{
                        //  addLog($"[AGV] ⚠ 응답은 받았으나 프로토콜 형식이 다름");
                        //}
                    }
                    else
                    {
                        addLog($"[AGV] ⚠ 응답 없음 - 포트는 열렸으나 장치 응답 없음");
                    }

                    port.Close();
                    addLog($"[AGV] 포트 닫기 완료");
                }
            }
            catch (Exception ex)
            {
                addLog($"[AGV] ✗ 오류 발생: {ex.Message}");
            }
        }

        

        private void btTestBMS_Click(object sender, EventArgs e)
        {
            // BMS 용 프로토콜을 전송해서 올바르게 피드백이 오는지 확인한다
            // BMS Protocol: STX(0xDD) + Data(32 bytes) + ETX(0x77)

            if (cmbPortBMS.SelectedItem == null)
            {
                addLog("[BMS] 포트를 선택해주세요");
                return;
            }

            if (!int.TryParse(tbBaudBMS.Text, out int baudRate))
            {
                addLog("[BMS] BaudRate가 올바르지 않습니다");
                return;
            }

            try
            {
                addLog($"[BMS] 테스트 시작: {cmbPortBMS.SelectedItem} @ {baudRate}bps");

                using (SerialPort port = new SerialPort())
                {
                    port.PortName = cmbPortBMS.SelectedItem.ToString();
                    port.BaudRate = baudRate;
                    port.DataBits = 8;
                    port.Parity = Parity.None;
                    port.StopBits = StopBits.One;
                    port.ReadTimeout = 3000;
                    port.WriteTimeout = 3000;

                    port.Open();
                    addLog($"[BMS] 포트 열기 성공");

                    // 간단한 테스트 명령 전송
                    var testCmd = new List<byte>();
                    testCmd.Add(0xDD);
                    testCmd.Add(0xA5);
                    testCmd.Add(0x03);
                    testCmd.Add(0x00);
                    testCmd.Add(0xFF);
                    testCmd.Add(0xFD);
                    testCmd.Add(0x77);
                    testCmd.Add(0x0D);

                    port.Write(testCmd.ToArray(), 0, testCmd.Count);//.Length);
                    addLog($"[BMS] 테스트 명령 전송: {BitConverter.ToString(testCmd.ToArray())}");

                    // 응답 대기
                    System.Threading.Thread.Sleep(1200);

                    if (port.BytesToRead > 0)
                    {
                        byte[] response = new byte[port.BytesToRead];
                        port.Read(response, 0, response.Length);
                        addLog($"[BMS] 응답 수신 ({response.Length} bytes): {BitConverter.ToString(response)}");

                        // STX, ETX 확인
                        if (response.Length >= 2 && response[0] == 0xDD && response[response.Length - 1] == 0x77)
                        {
                            addLog($"[BMS] ✓ 통신 성공 - 올바른 프로토콜 응답");
                            if (response.Length == 34 || response.Length == 23)
                            {
                                addLog($"[BMS] ✓ 데이터 길이 확인 ({response.Length} bytes)");
                            }
                        }
                        else
                        {
                            addLog($"[BMS] ⚠ 응답은 받았으나 프로토콜 형식이 다름");
                        }
                    }
                    else
                    {
                        addLog($"[BMS] ⚠ 응답 없음 - 포트는 열렸으나 장치 응답 없음");
                    }

                    port.Close();
                    addLog($"[BMS] 포트 닫기 완료");
                }
            }
            catch (Exception ex)
            {
                addLog($"[BMS] ✗ 오류 발생: {ex.Message}");
            }
        }

        void addLog(string msg)
        {
            // rtLog 컨트롤에 메세지를 추가하고, 추가된 메세지에 맞게 커서가 자동이동되게 한다
            if (rtLog.InvokeRequired)
            {
                rtLog.Invoke(new Action(() => addLog(msg)));
            }
            else
            {
                string timestamp = DateTime.Now.ToString("HH:mm:ss.fff");
                rtLog.AppendText($"[{timestamp}] {msg}\r\n");
                rtLog.SelectionStart = rtLog.Text.Length;
                rtLog.ScrollToCaret();
            }
        }

        private async void btFindPort_Click(object sender, EventArgs e)
        {
            // 버튼 비활성화 (중복 실행 방지)
            btFindPort.Enabled = false;

            try
            {
                await Task.Run(() => FindPortsAsync());
            }
            finally
            {
                // 버튼 재활성화
                btFindPort.Enabled = true;
            }
        }

        private void FindPortsAsync()
        {
            // AGV와 BMS의 응답 포트를 병렬로 동시에 찾는다
            addLog("===== 포트 자동 검색 시작 (병렬 모드) =====");

            string[] ports = SerialPort.GetPortNames();
            if (ports.Length == 0)
            {
                addLog("사용 가능한 포트가 없습니다");
                return;
            }

            addLog($"총 {ports.Length}개 포트 검색 중...");

            // 병렬 검색을 위한 변수
            object lockObj = new object();
            string foundAGVPort = null;
            string foundBMSPort = null;
            int agvBaud = 115200;
            int bmsBaud = 9600;
            bool bothFound = false;

            // 현재 설정된 포트 (우선순위 표시용)
            string currentAGVPort = null;
            string currentBMSPort = null;

            this.Invoke(new Action(() =>
            {
                currentAGVPort = cmbPortAGV.SelectedItem?.ToString();
                currentBMSPort = cmbPortBMS.SelectedItem?.ToString();
            }));

            // 현재 설정 확인 로그
            addLog($"현재 AGV 설정: {(string.IsNullOrEmpty(currentAGVPort) ? "(선택 안됨)" : currentAGVPort)}");
            addLog($"현재 BMS 설정: {(string.IsNullOrEmpty(currentBMSPort) ? "(선택 안됨)" : currentBMSPort)}");

            // === 1단계: 현재 설정된 포트 우선 테스트 ===
            addLog("--- 1단계: 현재 설정 포트 우선 테스트 ---");

            // AGV 현재 포트 우선 테스트
            if (!string.IsNullOrEmpty(currentAGVPort) && ports.Contains(currentAGVPort))
            {
                addLog($"[우선] AGV 현재 포트 테스트: {currentAGVPort}");
                for (int retry = 1; retry <= 3; retry++)
                {
                    if (TestAGVPort(currentAGVPort, agvBaud))
                    {
                        foundAGVPort = currentAGVPort;
                        addLog($"✓ 현재 설정 포트에서 AGV 발견: {currentAGVPort}");
                        break;
                    }
                    if (retry < 3) System.Threading.Thread.Sleep(500);
                }
            }

            // BMS 현재 포트 우선 테스트
            if (!string.IsNullOrEmpty(currentBMSPort) && ports.Contains(currentBMSPort))
            {
                addLog($"[우선] BMS 현재 포트 테스트: {currentBMSPort}");
                for (int retry = 1; retry <= 3; retry++)
                {
                    if (TestBMSPort(currentBMSPort, bmsBaud))
                    {
                        foundBMSPort = currentBMSPort;
                        addLog($"✓ 현재 설정 포트에서 BMS 발견: {currentBMSPort}");
                        break;
                    }
                    if (retry < 3) System.Threading.Thread.Sleep(500);
                }
            }

            // 둘 다 찾았으면 즉시 종료
            if (foundAGVPort != null && foundBMSPort != null)
            {
                addLog("현재 설정 포트에서 모두 발견! 검색 종료");
                ApplyFoundPorts(foundAGVPort, foundBMSPort, agvBaud, bmsBaud);
                addLog("===== 포트 자동 검색 완료 =====");
                return;
            }

            // === 2단계: 나머지 포트 병렬 검색 ===
            if (foundAGVPort == null || foundBMSPort == null)
            {
                addLog("--- 2단계: 나머지 포트 병렬 검색 ---");
            }

            // 모든 포트를 병렬로 테스트 (단, 이미 테스트한 포트 제외)
            List<Task> tasks = new List<Task>();

            foreach (string portName in ports)
            {
                string port = portName; // 클로저 캡처용

                // 이미 우선 테스트에서 체크한 포트는 제외
                if (port == currentAGVPort || port == currentBMSPort)
                    continue;

                Task task = Task.Run(() =>
                {
                    // 이미 둘 다 찾았으면 즉시 종료
                    if (bothFound) return;

                    addLog($"[{port}] 검사 시작...");

                    // AGV 테스트 (3회 재시도)
                    for (int retry = 1; retry <= 3; retry++)
                    {
                        // 첫 번째 시도에서는 조기 종료하지 않고 전체 대기 시간 보장
                        if (retry > 1 && bothFound) return;

                        try
                        {
                            if (foundAGVPort == null && TestAGVPort(port, agvBaud))
                            {
                                lock (lockObj)
                                {
                                    if (foundAGVPort == null)
                                    {
                                        foundAGVPort = port;
                                        addLog($"[{port}] ✓ AGV 발견! (재시도: {retry}/3)");

                                        // 둘 다 찾았는지 확인
                                        if (foundBMSPort != null)
                                        {
                                            bothFound = true;
                                            addLog("AGV와 BMS 모두 발견! 검색 종료");
                                        }
                                    }
                                }
                                break;
                            }
                        }
                        catch { }

                        if (retry < 3 && foundAGVPort == null && !bothFound)
                        {
                            System.Threading.Thread.Sleep(500);
                        }
                    }

                    // BMS 테스트 (3회 재시도)
                    for (int retry = 1; retry <= 3; retry++)
                    {
                        // 첫 번째 시도에서는 조기 종료하지 않고 전체 대기 시간 보장
                        if (retry > 1 && bothFound) return;

                        try
                        {
                            if (foundBMSPort == null && TestBMSPort(port, bmsBaud))
                            {
                                lock (lockObj)
                                {
                                    if (foundBMSPort == null)
                                    {
                                        foundBMSPort = port;
                                        addLog($"[{port}] ✓ BMS 발견! (재시도: {retry}/3)");

                                        // 둘 다 찾았는지 확인
                                        if (foundAGVPort != null)
                                        {
                                            bothFound = true;
                                            addLog("AGV와 BMS 모두 발견! 검색 종료");
                                        }
                                    }
                                }
                                break;
                            }
                        }
                        catch { }

                        if (retry < 3 && foundBMSPort == null && !bothFound)
                        {
                            System.Threading.Thread.Sleep(500);
                        }
                    }

                    // 둘 다 실패한 경우에만 로그
                    if (!bothFound && foundAGVPort != port && foundBMSPort != port)
                    {
                        addLog($"[{port}] ✗ 응답 없음");
                    }
                });

                tasks.Add(task);
            }

            // 나머지 포트가 있을 경우만 대기
            if (tasks.Count > 0)
            {
                addLog("나머지 포트 테스트 진행 중...");

                // 주기적으로 체크하면서 둘 다 찾았으면 즉시 종료
                while (!Task.WaitAll(tasks.ToArray(), 100))
                {
                    if (bothFound)
                    {
                        addLog("검색 조기 종료 - 모든 포트 발견");
                        break;
                    }
                }
            }

            // 결과 적용
            ApplyFoundPorts(foundAGVPort, foundBMSPort, agvBaud, bmsBaud);
            addLog("===== 포트 자동 검색 완료 =====");
        }

        /// <summary>
        /// 발견된 포트를 UI에 적용
        /// </summary>
        private void ApplyFoundPorts(string agvPort, string bmsPort, int agvBaud, int bmsBaud)
        {
            if (agvPort != null)
            {
                this.Invoke(new Action(() =>
                {
                    int idx = cmbPortAGV.Items.IndexOf(agvPort);
                    if (idx >= 0)
                    {
                        cmbPortAGV.SelectedIndex = idx;
                        tbBaudAGV.Text = agvBaud.ToString();
                    }
                }));
                addLog($"✓ AGV 포트: {agvPort} (Baud: {agvBaud})");
            }
            else
            {
                addLog("⚠ AGV 포트를 찾지 못했습니다");
            }

            if (bmsPort != null)
            {
                this.Invoke(new Action(() =>
                {
                    int idx = cmbPortBMS.Items.IndexOf(bmsPort);
                    if (idx >= 0)
                    {
                        cmbPortBMS.SelectedIndex = idx;
                        tbBaudBMS.Text = bmsBaud.ToString();
                    }
                }));
                addLog($"✓ BMS 포트: {bmsPort} (Baud: {bmsBaud})");
            }
            else
            {
                addLog("⚠ BMS 포트를 찾지 못했습니다");
            }
        }

        /// <summary>
        /// AGV 포트인지 테스트 (btTestAGV_Click 코드 사용)
        /// </summary>
        private bool TestAGVPort(string portName, int baudRate)
        {
            try
            {
                using (arDev.Narumi port = new arDev.Narumi())
                {
                    port.PortName = portName;
                    port.BaudRate = baudRate;

                    addLog($"  >> AGV 테스트 시작: {portName} @ {baudRate}bps");

                    port.Open();
                    addLog($"  >> AGV 포트 열기 성공: {portName}");

                    DateTime sendtime = DateTime.Now;
                    port.AGVMoveStop("test");
                    addLog($"  >> AGV 명령 전송: {portName} (시간: {sendtime:HH:mm:ss.fff})");

                    // 응답 대기 (더 길게, 주기적으로 체크)
                    int maxWait = 2000; // 최대 2초 대기
                    int waitStep = 100;
                    int totalWait = 0;

                    while (totalWait < maxWait)
                    {
                        System.Threading.Thread.Sleep(waitStep);
                        totalWait += waitStep;

                        // lastRecvTime 확인
                        if (port.lastRecvTime > sendtime)
                        {
                            addLog($"  >> ✓✓✓ AGV 응답 수신: {portName} (응답시간: {port.lastRecvTime:HH:mm:ss.fff}, 대기: {totalWait}ms)");
                            addLog($"  >> 응답 데이터: {port.LastRecvString}");
                            port.Close();
                            return true;
                        }

                        // LastRecvString도 확인 (lastRecvTime이 업데이트 안 될 수도 있음)
                        if (!string.IsNullOrEmpty(port.LastRecvString))
                        {
                            addLog($"  >> ✓✓✓ AGV 응답 감지 (문자열): {portName} (대기: {totalWait}ms)");
                            addLog($"  >> 응답 데이터: {port.LastRecvString}");
                            port.Close();
                            return true;
                        }
                    }

                    addLog($"  >> AGV 응답 없음: {portName} (최종 대기: {totalWait}ms, 마지막수신: {port.lastRecvTime:HH:mm:ss.fff})");
                    port.Close();
                }
            }
            catch (Exception ex)
            {
                addLog($"  >> AGV 테스트 오류: {portName} - {ex.Message}");
            }

            return false;
        }

        /// <summary>
        /// BMS 포트인지 테스트 (btTestBMS_Click 코드 사용)
        /// </summary>
        private bool TestBMSPort(string portName, int baudRate)
        {
            try
            {
                using (SerialPort port = new SerialPort())
                {
                    port.PortName = portName;
                    port.BaudRate = baudRate;
                    port.DataBits = 8;
                    port.Parity = Parity.None;
                    port.StopBits = StopBits.One;
                    port.ReadTimeout = 500;
                    port.WriteTimeout = 500;

                    addLog($"  >> BMS 테스트 시작: {portName} @ {baudRate}bps");

                    port.Open();
                    addLog($"  >> BMS 포트 열기 성공: {portName}");

                    // BMS 테스트 명령 전송 (테스트 버튼과 동일한 명령)
                    var testCmd = new List<byte>();
                    testCmd.Add(0xDD);
                    testCmd.Add(0xA5);
                    testCmd.Add(0x03);
                    testCmd.Add(0x00);
                    testCmd.Add(0xFF);
                    testCmd.Add(0xFD);
                    testCmd.Add(0x77);
                    testCmd.Add(0x0D);

                    port.Write(testCmd.ToArray(), 0, testCmd.Count);
                    addLog($"  >> BMS 명령 전송: {portName} - {BitConverter.ToString(testCmd.ToArray())}");

                    // 응답 대기
                    System.Threading.Thread.Sleep(300);

                    if (port.BytesToRead > 0)
                    {
                        byte[] response = new byte[port.BytesToRead];
                        port.Read(response, 0, response.Length);

                        addLog($"  >> BMS 응답 수신: {portName} ({response.Length} bytes) - {BitConverter.ToString(response)}");

                        // BMS 프로토콜 확인 (STX=0xDD, ETX=0x77)
                        if (response.Length >= 2 && response[0] == 0xDD && response[response.Length - 1] == 0x77)
                        {
                            addLog($"  >> ✓✓✓ BMS 프로토콜 확인: {portName}");
                            port.Close();
                            return true;
                        }
                        else
                        {
                            addLog($"  >> BMS 프로토콜 불일치: {portName}");
                        }
                    }
                    else
                    {
                        addLog($"  >> BMS 응답 없음: {portName}");
                    }

                    port.Close();
                }
            }
            catch (Exception ex)
            {
                addLog($"  >> BMS 테스트 오류: {portName} - {ex.Message}");
            }

            return false;
        }

        private void toolStripButton1_Click(object sender, EventArgs e)
        {
            LoadSetting();
        }

        private void toolStripButton2_Click(object sender, EventArgs e)
        {
            SaveSetting();
        }
    }
}
