using System;
using System.Drawing;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using ENIG;
using ENIGProtocol;

namespace Test_ACS
{
    public partial class MainForm : Form
    {
        private SerialPort serialPort;
        private EEProtocol protocol;
        private byte selectedAGV = 11; // 기본값: AGV1 (11)
        private AppSettings settings;

        public MainForm()
        {
            InitializeComponent();
            InitializeProtocol();
            LoadPortList();

            txtAlias.Items.Clear();
            this.txtAlias.Items.AddRange(new[] {
                "LOADER",
                "PLATING",
                "CLEANER",
                "CHARGER",
                "BUFFER1",
                "BUFFER2",
                "BUFFER3",
                "BUFFER4",
                "BUFFER5",
                "BUFFER6",
            });
            this.txtAlias.Text = "LOADER";

        }



        private void InitializeProtocol()
        {
            protocol = new EEProtocol();
            protocol.OnDataReceived += Protocol_OnDataReceived;
            protocol.OnMessage += Protocol_OnMessage;

            serialPort = new SerialPort();
            serialPort.ReadTimeout = 2000;
            serialPort.WriteTimeout = 1000;
            serialPort.DataReceived += SerialPort_DataReceived;
        }

        private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            var buffer = new byte[serialPort.BytesToRead];
            serialPort.Read(buffer, 0, buffer.Length);
            protocol.ProcessReceivedData(buffer);
        }

        private void Protocol_OnDataReceived(object sender, EEProtocol.DataEventArgs e)
        {
            var hexstrRaw = string.Join(" ", e.ReceivedPacket.RawData.Select(b => b.ToString("X2")));
            var hexstr = string.Join(" ", e.ReceivedPacket.Data.Select(b => b.ToString("X2")));
            var cmd = e.ReceivedPacket.Command.ToString("X2");
            var id = e.ReceivedPacket.ID.ToString("X2");
            AddLog($"RX: {hexstrRaw}\nID:{id}, CMD:{cmd}, DATA:{hexstr}", LogType.RX);

            // AGV 상태 수신 처리 (cmd = 3)
            var device = e.ReceivedPacket.ID;
            var command = (ENIGProtocol.AGVCommandEH)e.ReceivedPacket.Command;
            var data = e.ReceivedPacket.Data;

            switch (command)
            {
                case AGVCommandEH.Status:
                    _remoteStatus.Mode = data[0];
                    _remoteStatus.RunSt = data[1];
                    _remoteStatus.HWError = BitConverter.ToUInt16(data, 2);
                    _remoteStatus.RunStep = data[4];
                    _remoteStatus.RunStepSeq = data[5];
                    _remoteStatus.MotorDir = data[6];
                    _remoteStatus.MagnetDir = data[7];
                    _remoteStatus.ChargeSt = data[8];
                    _remoteStatus.CartSt = data[9];
                    _remoteStatus.LiftSt = data[10];
                    _remoteStatus.ErrorCode = data[11];
                    _remoteErrorCode = (ENIGProtocol.AGVErrorCode)data[11];
                    _remoteErrorMessage = ENIGProtocol.AGVUtility.GetAGVErrorMessage(_remoteErrorCode);
                    _remoteStatus.LastTag = Encoding.ASCII.GetString(data, 12, 4);

                    UpdateUIStatus();
                    break;
                case AGVCommandEH.Error:

                    _remoteErrorCode = (ENIGProtocol.AGVErrorCode)data[0];
                    // _remoteErrorMessage = ... Error 메시지 자체는 패킷에 포함되지 않으므로 유틸리티 사용 가능
                    _remoteErrorMessage = ENIGProtocol.AGVUtility.GetAGVErrorMessage(_remoteErrorCode);
                    UpdateUIStatus();
                    AddLog($"Error Received : {_remoteErrorCode} ID:{e.ReceivedPacket.ID} MSG:{_remoteErrorMessage}", LogType.Info);
                    break;
                default:
                    AddLog($"unknown command:{command}", LogType.Error);
                    break;
            }
        }

        private void Protocol_OnMessage(object sender, EEProtocol.MessageEventArgs e)
        {
            AddLog(e.Message, e.IsError ? LogType.Error : LogType.Info);
        }

        private void LoadPortList()
        {
            cmbPort.Items.Clear();
            foreach (var port in SerialPort.GetPortNames())
            {
                cmbPort.Items.Add(port);
            }

            // 마지막 설정 불러오기
            LoadSettings();
        }

        private void LoadSettings()
        {
            try
            {
                // 설정 파일 로드
                settings = AppSettings.Load();

                // 포트 설정
                if (!string.IsNullOrEmpty(settings.LastPort) && cmbPort.Items.Contains(settings.LastPort))
                {
                    cmbPort.SelectedItem = settings.LastPort;
                }
                else if (cmbPort.Items.Count > 0)
                {
                    cmbPort.SelectedIndex = 0;
                }

                // 보레이트 설정
                txtBaudRate.Text = settings.LastBaudRate;

                // RFID 번호 설정
                txtRFID.Text = settings.LastRFID;

                // 별칭 설정
                txtAlias.Text = settings.LastAlias;

                // AGV 선택 설정
                if (settings.LastAGV == 11)
                {
                    rbAGV1.Checked = true;
                }
                else if (settings.LastAGV == 12)
                {
                    rbAGV2.Checked = true;
                }

                AddLog("설정을 불러왔습니다.", LogType.Info);
            }
            catch (Exception ex)
            {
                settings = new AppSettings();
                AddLog($"설정 불러오기 실패: {ex.Message}", LogType.Error);
            }
        }

        private void SaveSettings()
        {
            try
            {
                if (settings == null)
                    settings = new AppSettings();

                settings.LastPort = cmbPort.Text;
                settings.LastBaudRate = txtBaudRate.Text;
                settings.LastRFID = txtRFID.Text;
                settings.LastAlias = txtAlias.Text;
                settings.LastAGV = selectedAGV;
                settings.Save();
            }
            catch (Exception ex)
            {
                AddLog($"설정 저장 실패: {ex.Message}", LogType.Error);
            }
        }

        private void btnConnect_Click(object sender, EventArgs e)
        {
            if (serialPort.IsOpen)
            {
                serialPort.Close();
                btnConnect.Text = "연결";
                AddLog("포트 닫힘", LogType.Info);

            }
            else
            {
                try
                {
                    serialPort.PortName = cmbPort.Text;
                    serialPort.BaudRate = int.Parse(txtBaudRate.Text);
                    serialPort.Open();
                    btnConnect.Text = "해제";
                    AddLog($"{serialPort.PortName}:{serialPort.BaudRate} 연결됨", LogType.Info);
                    SaveSettings();
                }
                catch (Exception ex)
                {
                    AddLog($"연결 실패: {ex.Message}", LogType.Error);
                }
            }
        }

        private void btnRefresh_Click(object sender, EventArgs e)
        {
            LoadPortList();
        }

        private void cmbPort_SelectedIndexChanged(object sender, EventArgs e)
        {
            //SaveSettings();
        }

        private void txtBaudRate_TextChanged(object sender, EventArgs e)
        {
            //SaveSettings();
        }

        private void txtRFID_TextChanged(object sender, EventArgs e)
        {
            //SaveSettings();
        }

        private void txtAlias_TextChanged(object sender, EventArgs e)
        {
            //SaveSettings();
        }

        private void rbAGV1_CheckedChanged(object sender, EventArgs e)
        {
            if (rbAGV1.Checked)
            {
                selectedAGV = 11;
                //SaveSettings();
            }
        }

        private void rbAGV2_CheckedChanged(object sender, EventArgs e)
        {
            if (rbAGV2.Checked)
            {
                selectedAGV = 12;
                //SaveSettings();
            }
        }

        private void btnSetCurrent_Click(object sender, EventArgs e)
        {
            // SetCurrent: data = TargetID(2 hex) + RFID(4 hex)
            var targetID = selectedAGV.ToString("X2");
            var rfidBytes = Encoding.ASCII.GetBytes(txtRFID.Text.PadLeft(4, '0'));
            var rfidHex = string.Join("", rfidBytes.Select(b => b.ToString("X2")));
            var dataStr = targetID + rfidHex;
            SendCommand(AGVCommandHE.SetCurrent, dataStr);
        }

        private void btnGoto_Click(object sender, EventArgs e)
        {
            // Goto: data = TargetID(2 hex) + RFID(4 hex)

            var targetID = selectedAGV.ToString("X2");
            var rfidBytes = Encoding.ASCII.GetBytes(txtRFID.Text.PadLeft(4, '0'));
            var rfidHex = string.Join("", rfidBytes.Select(b => b.ToString("X2")));
            var dataStr = targetID + rfidHex;
            SendCommand(AGVCommandHE.Goto, dataStr);
        }

        private void btnGotoAlias_Click(object sender, EventArgs e)
        {
            // GotoAlias: data = TargetID(2 hex) + Alias(ASCII string)
            var alias = txtAlias.Text.Trim();
            if (string.IsNullOrEmpty(alias))
            {
                AddLog("별칭을 입력하세요.", LogType.Error);
                return;
            }

            var targetID = selectedAGV.ToString("X2");
            var aliasBytes = Encoding.ASCII.GetBytes(alias);
            var aliasHex = string.Join("", aliasBytes.Select(b => b.ToString("X2")));
            var dataStr = targetID + aliasHex;
            SendCommand(AGVCommandHE.GotoAlias, dataStr);
            SaveSettings();
        }

        private void btnStop_Click(object sender, EventArgs e)
        {
            // Stop: data = TargetID(2 hex)
            var targetID = selectedAGV.ToString("X2");
            SendCommand(AGVCommandHE.Stop, targetID);
        }

        private void btnReset_Click(object sender, EventArgs e)
        {
            // Reset: data = TargetID(2 hex)
            var targetID = selectedAGV.ToString("X2");
            SendCommand(AGVCommandHE.Reset, targetID);
        }

        private void btnManual_Click(object sender, EventArgs e)
        {
            var but = sender as Button;
            if (but.Tag == null) return;
            var tagstr = but.Tag.ToString();

            var targetID = selectedAGV.ToString("X2");
            var direction = byte.Parse(tagstr);//  //back
            var speed = (byte)0;// cmbSpeed.SelectedIndex;
            if (radSpdM.Checked) speed = 1;
            else if (radSpdH.Checked) speed = 2;


            var dataBytes = new byte[] { direction, speed };
            var dataStr = targetID + string.Join("", dataBytes.Select(b => b.ToString("X2")));
            SendCommand(AGVCommandHE.Manual, dataStr);
        }

        private void btnMarkStop_Click(object sender, EventArgs e)
        {
            // MarkStop: data = TargetID(2 hex) + MarkStop(1 byte)
            var targetID = selectedAGV.ToString("X2");
            var markStop = "01";// chkMarkStop.Checked ? "01" : "00";
            SendCommand(AGVCommandHE.MarkStop, targetID + markStop);
        }

        private void btnLiftUp_Click(object sender, EventArgs e)
        {
            SendLiftCommand(arDev.Narumi.LiftCommand.UP); // Up
        }

        private void btnLiftDown_Click(object sender, EventArgs e)
        {
            SendLiftCommand(arDev.Narumi.LiftCommand.DN); // Down
        }

        private void btnLiftStop_Click(object sender, EventArgs e)
        {
            SendLiftCommand(arDev.Narumi.LiftCommand.STP); // Stop
        }


        private void SendLiftCommand(arDev.Narumi.LiftCommand liftCmd)
        {
            // LiftControl: data = TargetID(2 hex) + LiftCommand(1 byte)
            var targetID = selectedAGV.ToString("X2");
            var dataStr = targetID + ((byte)liftCmd).ToString("X2");
            SendCommand(AGVCommandHE.LiftControl, dataStr);
        }

        private void SendCommand(AGVCommandHE command, string dataHexString)
        {
            if (!serialPort.IsOpen)
            {
                AddLog("포트가 연결되지 않았습니다.", LogType.Error);
                return;
            }

            try
            {
                // Hex 문자열을 byte 배열로 변환
                byte[] dataBytes = new byte[dataHexString.Length / 2];
                for (int i = 0; i < dataBytes.Length; i++)
                {
                    dataBytes[i] = Convert.ToByte(dataHexString.Substring(i * 2, 2), 16);
                }

                // 패킷 생성 (ACS ID = 0)
                byte[] packet = protocol.CreatePacket(0, (byte)command, dataBytes);

                // 전송
                serialPort.Write(packet, 0, packet.Length);

                var hexString = string.Join(" ", packet.Select(b => b.ToString("X2")));

                //정보를 조금더 추출한다.
                var Sender = packet[2];

                if (Sender == 0x00)
                {
                    var Receiver = packet[4];
                    var strdata = System.Text.Encoding.Default.GetString(dataBytes, 1, dataBytes.Length - 1);// (dataHexString.Substring(2), 16);

                    AddLog($"{hexString}|{command}({(byte)command:X2}) From:{Sender} To:{Receiver} => {dataHexString} STR:{strdata}", LogType.TX);
                }
                else
                {
                    AddLog($"{hexString}|{command}({(byte)command:X2}) From:{Sender} => {dataHexString}", LogType.TX);
                }

            }
            catch (Exception ex)
            {
                AddLog($"전송 실패: {ex.Message}", LogType.Error);
            }
        }
        public ENIGProtocol.AGVErrorCode _remoteErrorCode = ENIGProtocol.AGVErrorCode.None;
        public string _remoteErrorMessage = "";
        public RemoteStatus _remoteStatus = new RemoteStatus();
        private void UpdateUIStatus()
        {
            if (this.InvokeRequired)
            {
                this.BeginInvoke(new Action(UpdateUIStatus));
                return;
            }

            try
            {

                rtStatus.Text = _remoteStatus.ToString();

                string errCode = _remoteErrorCode.ToString();
                string errMsg = _remoteErrorMessage;

                if (_remoteStatus.HWError > 0)
                {
                    errCode = $"HW:{_remoteStatus.HWError:X4}" + (errCode == "None" ? "" : $" | {errCode}");

                    StringBuilder sbHw = new StringBuilder();
                    for (int i = 0; i < 16; i++)
                    {
                        if (((ushort)_remoteStatus.HWError & (1 << i)) != 0)
                        {
                            sbHw.Append($"{(arDev.Narumi.eflag)i}, ");
                        }
                    }
                    errMsg = $"[HW] {sbHw}" + (string.IsNullOrEmpty(errMsg) ? "" : $" | {errMsg}");
                }

                tbErCode.Text = errCode;
                tbErmsg.Text = errMsg;

                if (_remoteErrorCode != ENIGProtocol.AGVErrorCode.None || _remoteStatus.HWError > 0)
                {
                    tbErCode.BackColor = Color.Red;
                    tbErCode.ForeColor = Color.White;
                }
                else
                {
                    tbErCode.BackColor = SystemColors.Window;
                    tbErCode.ForeColor = SystemColors.WindowText;
                }



            }
            catch (Exception ex)
            {
                AddLog($"AGV 상태 업데이트 실패: {ex.Message}", LogType.Error);
            }
        }

        private enum LogType { TX, RX, Info, Error }

        private void AddLog(string message, LogType type)
        {
            if (InvokeRequired)
            {
                BeginInvoke(new Action(() => AddLog(message, type)));
                return;
            }

            var timestamp = DateTime.Now.ToString("HH:mm:ss");
            var logMessage = $"[{timestamp}] {message}\r\n";

            switch (type)
            {
                case LogType.TX:
                    txtTxLog.AppendText(logMessage);
                    txtTxLog.ScrollToCaret();
                    break;
                case LogType.RX:
                    txtRxLog.AppendText(logMessage);
                    txtRxLog.ScrollToCaret();
                    break;
                case LogType.Info:
                    txtInfoLog.AppendText(logMessage);
                    txtInfoLog.ScrollToCaret();
                    break;
                case LogType.Error:
                    txtInfoLog.ForeColor = Color.Red;
                    txtInfoLog.AppendText(logMessage);
                    txtInfoLog.ForeColor = Color.Black;
                    txtInfoLog.ScrollToCaret();
                    break;
            }
        }

        protected override void OnFormClosing(FormClosingEventArgs e)
        {
            if (serialPort != null && serialPort.IsOpen)
            {
                serialPort.Close();
            }
            base.OnFormClosing(e);
        }

        private void btAMove_Click(object sender, EventArgs e)
        {
            var targetID = selectedAGV.ToString("X2");
            byte Motdirection = 0;// (byte)cmbMotDirection.SelectedIndex;
            if (radForw.Checked) Motdirection = 1;

            byte Magdirection = 0;
            if (radLeft.Checked) Magdirection = 1;
            else if (radRight.Checked) Magdirection = 2;

            byte speed = 0;// (byte)cmbAutoSpeed.SelectedIndex;
            if (radSpdM.Checked) speed = 1;
            else if (radSpdH.Checked) speed = 2;

            byte lidar = 2;
            if (radLidarOff.Checked) lidar = 0;

            var dataBytes = new byte[] { Motdirection, Magdirection, speed, lidar };
            var dataStr = targetID + string.Join("", dataBytes.Select(b => b.ToString("X2")));
            SendCommand(AGVCommandHE.AutoMove, dataStr);
        }

        private void button2_Click(object sender, EventArgs e)
        {
            // Stop: data = TargetID(2 hex)
            var targetID = selectedAGV.ToString("X2");
            SendCommand(AGVCommandHE.Stop, targetID);
        }

        private void groupBox2_Enter(object sender, EventArgs e)
        {

        }

        private void button6_Click(object sender, EventArgs e)
        {
            var targetID = selectedAGV.ToString("X2");
            SendCommand(AGVCommandHE.Stop, targetID);
        }

        private void button7_Click(object sender, EventArgs e)
        {
            //lt180
            var dlg = MessageBox.Show("턴작업을 실행할까요? 회전반경에 장애물이 없어야 합니다", "확인", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
            if (dlg != DialogResult.Yes) return;
            var targetID = selectedAGV.ToString("X2");
            SendCommand(AGVCommandHE.LTurn180, targetID);
        }

        private void button8_Click(object sender, EventArgs e)
        {
            //rt180
            var dlg = MessageBox.Show("턴작업을 실행할까요? 회전반경에 장애물이 없어야 합니다", "확인", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
            if (dlg != DialogResult.Yes) return;
            var targetID = selectedAGV.ToString("X2");
            SendCommand(AGVCommandHE.RTurn180, targetID);
        }

        private void button9_Click(object sender, EventArgs e)
        {
            //l turn
            var dlg = MessageBox.Show("턴작업을 실행할까요? 회전반경에 장애물이 없어야 합니다", "확인", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
            if (dlg != DialogResult.Yes) return;
            var targetID = selectedAGV.ToString("X2");
            SendCommand(AGVCommandHE.LTurn, targetID);
        }

        private void button10_Click(object sender, EventArgs e)
        {
            ///r-turn
            var dlg = MessageBox.Show("턴작업을 실행할까요? 회전반경에 장애물이 없어야 합니다", "확인", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
            if (dlg != DialogResult.Yes) return;
            var targetID = selectedAGV.ToString("X2");
            SendCommand(AGVCommandHE.RTurn, targetID);
        }

        private void button12_Click(object sender, EventArgs e)
        {
            SendLiftCommand(arDev.Narumi.LiftCommand.ON);
        }

        private void button11_Click(object sender, EventArgs e)
        {
            SendLiftCommand(arDev.Narumi.LiftCommand.OFF);
        }

        private void button16_Click(object sender, EventArgs e)
        {
            //enter pickon
            var targetID = selectedAGV.ToString("X2");
            var dataStr = targetID;// + liftCmd.ToString("X2");
            SendCommand(AGVCommandHE.PickOnEnter, dataStr);
        }

        private void button15_Click(object sender, EventArgs e)
        {
            //enter pickoff
            var targetID = selectedAGV.ToString("X2");
            SendCommand(AGVCommandHE.PickOffEnter, targetID);
        }

        private void button14_Click(object sender, EventArgs e)
        {
            //exit pickon
            var targetID = selectedAGV.ToString("X2");
            SendCommand(AGVCommandHE.PickOnExit, targetID);
        }

        private void button13_Click(object sender, EventArgs e)
        {
            //exit pickoff
            var targetID = selectedAGV.ToString("X2");
            SendCommand(AGVCommandHE.PickOffExit, targetID);
        }

        private void MainForm_Load(object sender, EventArgs e)
        {
            
        }
    }
}
