using AGVNavigationCore.Models;
using AR;
using COMM;
using Project.StateMachine;
using System;
using System.CodeDom;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Windows.Forms;
using static AGVNavigationCore.PathFinding.Planning.AGVPathfinder;

namespace Project
{
    public partial class fMain : Form
    {
        double freeSpaceRate = 0.0;

        //뷰어용화면
        ViewForm.fAuto form_auto = null;
        ViewForm.fManual form_manu = null;
        ViewForm.fIO form_zlift = null;
        ViewForm.fFlag form_flag = null;
        ViewForm.fAgv form_agv = null;
        ViewForm.fBms form_bms = null;
        Dialog.fLog form_log = null;

        bool remoteClose = false;
        bool forceClose = false;

        public fMain()
        {
            InitializeComponent();
            VAR.Init(128);
            PUB.initCore();
            UTIL.MessgeBoxLegacyMode = false;
            this.KeyDown += (s1, e1) =>
            {
                if (e1.KeyCode == Keys.Escape) this.Close();
                else if (e1.KeyCode == Keys.F1 && e1.Control && e1.Shift)
                {

                }
                else if (e1.KeyCode == Keys.F5) btAutoRun.PerformClick();
                else if (e1.KeyCode == Keys.F9) btAutoRun.PerformClick();
                else if (e1.KeyCode == Keys.F11 && System.Diagnostics.Debugger.IsAttached)
                {
                    // F11: 모든 스레드 상태 덤프
                    DumpAllThreadsState();
                }

                if (DateTime.Now > PUB.LastInputTime) PUB.LastInputTime = DateTime.Now;
            };

            PUB._mapCanvas = new AGVNavigationCore.Controls.UnifiedAGVCanvas();
            PUB._mapCanvas.Dock = DockStyle.Fill;
            PUB._mapCanvas.ShowGrid = false;
            PUB._mapCanvas.BackColor = Color.FromArgb(32, 32, 32);
            PUB._mapCanvas.ForeColor = Color.White;
            PUB._mapCanvas.Mode = AGVNavigationCore.Controls.UnifiedAGVCanvas.CanvasMode.Run;

            this.panTopMenu.MouseMove += LbTitle_MouseMove;
            this.panTopMenu.MouseUp += LbTitle_MouseUp;
            this.panTopMenu.MouseDown += LbTitle_MouseDown;
            this.panTopMenu.DoubleClick += LbTitle_DoubleClick;

            this.MouseMove += (s1, e1) => { if (DateTime.Now > PUB.LastInputTime) PUB.LastInputTime = DateTime.Now; };
            this.FormClosing += __Closing;

            if (PUB.setting.FullScreen) this.WindowState = FormWindowState.Maximized;

        }


        private void __Closing(object sender, FormClosingEventArgs e)
        {
            // 장치 관리 태스크는 _STEP_CLOSING_START에서 종료됨

            PUB.popup.needClose = true;
            if (remoteClose == true)
            {
                PUB.log.Add("패치로 인해 프로그램을 종료 합니다");
                PUB.sm.SetNewStep(eSMStep.IDLE);
                //_Close_Start();
                _STEP_CLOSING_START(PUB.sm.Step);
                PUB.sm.Stop();

                var patchfile = new System.IO.FileInfo(System.IO.Path.Combine(UTIL.CurrentPath, "SWPatch.exe"));
                if (patchfile.Exists == false)
                {
                    UTIL.MsgE("패치파일(" + patchfile.Name + ")이 없습니다.\n프로그램을 다시 실행해주세요", true);
                }
                else
                {
                    UTIL.RunProcess(patchfile.FullName);
                }
            }
            else if (forceClose == true)
            {
                PUB.log.Add("강제 종료");
                PUB.sm.SetNewStep(eSMStep.IDLE);
                //_Close_Start();
                _STEP_CLOSING_START(PUB.sm.Step);
                PUB.sm.Stop();


            }
            else
            {
                if (PUB.sm.Step == eSMStep.RUN)
                {
                    UTIL.MsgE("동작 중에는 종료 할 수 없습니다.");
                    e.Cancel = true;
                    return;
                }
                if (PUB.sm.Step < eSMStep.CLOSING)
                {
                    var rlt = UTIL.MsgQ("종료하시겠습니까");
                    if (rlt == System.Windows.Forms.DialogResult.Yes)
                    {
                        //비젼의 측정상태가 변경된 경우
                        PUB.sm.SetNewStep(eSMStep.CLOSING, true);

                    }
                    e.Cancel = true;
                    return;
                    //_Close_Start();
                }
            }

        }

        private Dictionary<string, int> FlagMap = new Dictionary<string, int>();
        private void __Load(object sender, EventArgs e)
        {
            this.Text = Application.ProductName + " ver " + Application.ProductVersion;
            PUB.init();
            if (PUB.setting.FullScreen)
            {
                this.FormBorderStyle = FormBorderStyle.None;
                this.WindowState = FormWindowState.Maximized;
            }
            this.Show();
            Application.DoEvents();

            VAR.STR[eVarString.SWVersion] = Application.ProductVersion;


            AutoLoadMapData();

            //SETTING H/W
            this.IOState.ItemClick += gridView2_ItemClick;
            VAR.BOOL.PropertyChanged += BOOL_PropertyChanged;

            //지그비통신
            PUB.XBE = new Device.Xbee();
            PUB.XBE.MessageReceived += XBE_MessageReceived;
            PUB.XBE.ProtocReceived += XBE_ProtocReceived;

            //AGV
            PUB.AGV = new arDev.Narumi();
            PUB.AGV.Message += AGV_Message;
            PUB.AGV.DataReceive += AGV_DataReceive;
            PUB.AGV.TurnComplete += AGV_TurnComplete;

            //배터리관리시스템
            PUB.BMS = new arDev.BMS();
            PUB.BMS.BMSDataReceive += Bms_BMSDataReceive;
            PUB.BMS.BMSCellDataReceive += BMS_BMSCellDataReceive;
            PUB.BMS.Message += Bms_Message;
            PUB.BMS.ChargeDetect += BMS_ChargeDetect;
            PUB.BMS.ScanInterval = PUB.setting.interval_bms;//

            //디버그메세지 출력용 소켓
            PUB.sock_debug = new Device.Socket();
            PUB.sock_debug.GetMessage += socket_GetMessage;
            PUB.sock_debug.RecvMessage += socket_RecvMessage;
            PUB.sock_debug.SendMessage += socket_SendMessage;

            //clear 
            Resultclear();

            //충전이 시작되지 않도록 기본값을 넣는다
            VAR.TIME[eVarTime.ChargeTry] = DateTime.Now;
            VAR.TIME[eVarTime.ChargeSearch] = DateTime.Now;

            PUB.sm.SetMsgOptOff(); //모든 메세지출력을 해제한다. (이벤트는 동작함)

            PUB.log.Add("State Machine Start");

            try
            {
                PUB.sm = new StateMachine.StateMachine();
                PUB.log.Add("StateMachine", $"객체 생성 완료 - Type: {PUB.sm.GetType().FullName}");

                // StateMachine 객체의 속성 확인
                var smType = PUB.sm.GetType();
                var properties = smType.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
                PUB.log.Add("StateMachine", $"Public Properties: {properties.Length}개");

                var methods = smType.GetMethods(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.DeclaredOnly);
                PUB.log.Add("StateMachine", $"Public Methods: {methods.Length}개");
            }
            catch (Exception ex)
            {
                PUB.log.AddE($"StateMachine 객체 생성 실패: {ex.Message}");
                PUB.log.AddE($"StackTrace: {ex.StackTrace}");
                throw;
            }

            PUB.sm.StepChanged += sm_StepChanged;
            PUB.sm.Message += sm_Message;
            PUB.sm.Running += sm_Running;
            PUB.sm.SPS += sm_SPS;
            PUB.sm.Start();
            PUB.log.Add("StateMachine", "SM 이벤트 등록 완료");

            // 스레드 시작 대기 (최대 3초)
            for (int i = 0; i < 30; i++)
            {
                System.Threading.Thread.Sleep(100);
                if (PUB.sm.IsThreadRun)
                {
                    PUB.log.Add("StateMachine", $"스레드 시작 확인됨 ({i * 100}ms 소요)");
                    break;
                }
            }

            if (!PUB.sm.IsThreadRun)
            {
                PUB.log.AddE("경고: 3초 대기 후에도 스레드가 시작되지 않음!");
                System.Windows.Forms.MessageBox.Show(
                    "상태머신 스레드가 시작되지 않았습니다.\n" +
                    "로그 파일을 확인하세요.",
                    "오류",
                    System.Windows.Forms.MessageBoxButtons.OK,
                    System.Windows.Forms.MessageBoxIcon.Error);
            }

            tmDisplay.Tick += tmDisplay_Tick;
            tmDisplay.Start(); //start Display
            PUB.log.Add("Display", "Display Timer 시작 완료");

            this.btDebug.Visible = true;// System.Diagnostics.Debugger.IsAttached || PUB.setting.UseDebugMode;
            PUB.log.Add("Program Start");

            //수량표시
            PUB.counter.PropertyChanged += (s1, e1) => Update_Count();
            Update_Count();

            PUB.log.Add("프로그램 실행 기록 추가");
            PUB.CheckNRegister5(Application.ProductName, "chi", Application.ProductVersion,PUB.setting.MCID,PUB.setting.AGV_ADDRESS);

            if (PUB.setting.StartLog)
                MenuLog.PerformClick();

            PUB.AddEEDB("프로그램 시작");
        }

       
        void AutoLoadLastPosition()
        {
            PUB.log.Add("autoload last position");
            //마지막위치복원
            // 마지막 위치 로드
            var lastPos = PUB.LoadLastPosition();
            if (lastPos != null)
            {
                var sb = new System.Text.StringBuilder();
                var curNode = PUB.FindByNodeID(lastPos.NodeId);
                var prevNode = PUB.FindByNodeID(lastPos.PrevNode);
                var targNode = PUB.FindByNodeID(lastPos.TargetNode);
                var staNode = PUB.FindByNodeID(lastPos.StartNode);
                if (prevNode != null)
                {
                    PUB._virtualAGV.SetPosition(prevNode, lastPos.PrevDirection);
                    PUB._mapCanvas.SetAGVPosition(PUB.setting.MCID, prevNode, lastPos.PrevDirection);
                    sb.AppendLine($"이전: {prevNode.ID2}, {lastPos.PrevDirection}");
                }
                if (curNode != null)
                {
                    PUB._virtualAGV.SetPosition(curNode, lastPos.Direction);
                    PUB._mapCanvas.SetAGVPosition(PUB.setting.MCID, curNode, lastPos.Direction);
                    sb.AppendLine($"현재: {curNode.ID2}, {lastPos.Direction}");

                    //agv정보의 마지막 태그값도 복원한다
                    PUB.AGV.data.TagNo = curNode.RfidId;
                    PUB.Result.LastTAG = PUB.AGV.data.TagNo;
                }
                if (staNode != null)
                {
                    PUB._virtualAGV.StartNode = staNode;
                    sb.AppendLine($"시직:{staNode.ID2}");
                }
                if (targNode != null)
                {
                    PUB._virtualAGV.TargetNode = targNode;
                    sb.AppendLine($"대상:{targNode.ID2}");
                }

                PUB._virtualAGV.Turn = lastPos.Turn;
                sb.AppendLine($"S/W회전:{lastPos.Turn}");

                if (lastPos.AGV_Turn != null)
                {
                    PUB.AGV.TurnInformation = lastPos.AGV_Turn;
                    sb.AppendLine($"H/W회전:{lastPos.AGV_Turn}");
                }

                PUB.log.Add($"위치복원\n{sb}");
            }
            else PUB.log.AddE($"마지막위치가 없습니다");


        }
        void AutoLoadMapData()
        {
            //auto load 
            var mapPath = new System.IO.DirectoryInfo("route");
            if (mapPath.Exists == false) mapPath.Create();

            //맵파일로딩
            var basefile = System.IO.Path.Combine(mapPath.FullName, "default.json"); 
            if (System.IO.File.Exists(basefile) == false)
                if (PUB.setting.LastMapFile.isEmpty() == false) basefile = PUB.setting.LastMapFile;

            System.IO.FileInfo filePath = new System.IO.FileInfo(basefile);
            if (filePath.Exists == false)  //그래도없다면 맵폴더에서 파일을 찾아본다.
            {
                var files = mapPath.GetFiles("*.json");
                if (files.Any()) filePath = files[0];
            }

            if(PUB._mapCanvas == null)
            {
                UTIL.MsgE("Canvas null");
            }

            if (filePath.Exists)
            {
                var result = MapLoader.LoadMapFromFile(filePath.FullName);

                if (result != null && result.Success)
                {
                    PUB._mapCanvas.SetMapLoadResult(result);
                    PUB._mapCanvas.MapFileName = filePath.FullName;

                    // 🔥 가상 AGV 초기화 (첫 노드 위치에 생성)
                    if (PUB._virtualAGV == null)
                    {
                        PUB._virtualAGV = new VirtualAGV(PUB.setting.MCID, Point.Empty, AgvDirection.Forward);
                        PUB._virtualAGV.LowBatteryThreshold = PUB.setting.BatteryLimit_Low;
                        //PUB._virtualAGV.SetPosition(startNode, AgvDirection.Forward);

                        // 캔버스에 AGV 리스트 설정
                        var agvList = new System.Collections.Generic.List<AGVNavigationCore.Controls.IAGV> { PUB._virtualAGV };
                        PUB._mapCanvas.AGVList = agvList;

                        PUB.log.Add($"가상 AGV 생성: PointZero 위치");
                    }
                    else 
                    {
                        PUB._virtualAGV.LowBatteryThreshold = PUB.setting.BatteryLimit_Low;
                        // 기존 AGV가 있으면 캔버스에 다시 연결
                        var agvList = new System.Collections.Generic.List<AGVNavigationCore.Controls.IAGV> { PUB._virtualAGV };
                        PUB._mapCanvas.AGVList = agvList;
                    }
                    PUB.log.Add($"맵 파일 로드 완료: {filePath.Name}, 노드 수: {result.Nodes.Count}");



                    //// 🔥 초기 위치 설정 및 확인 화면 표시
                    //this.BeginInvoke(new Action(() =>
                    //{
                    //    using (var f = new Dialog.fSetCurrentPosition())
                    //    {
                    //        if (f.ShowDialog() == DialogResult.OK)
                    //        {
                    //            if (f.SelectedNode != null)
                    //            {
                    //                PUB._virtualAGV.Turn = f.SelectedTurn;
                    //                PUB._virtualAGV.SetPosition(f.SelectedNode, f.SelectedDirection);
                    //                PUB._mapCanvas.SetAGVPosition(PUB.setting.MCID, f.SelectedNode, f.SelectedDirection);
                    //                PUB.SaveLastPosition();
                    //                PUB.log.Add($"[초기위치] 설정 완료: {f.SelectedNode.Id}, {f.SelectedDirection}, Turn:{f.SelectedTurn}");
                    //            }
                    //        }
                    //    }
                    //}));

                    PUB._mapCanvas.SetView(287.6f, -147.6f, 1.13f);
                }
                else
                {
                    PUB.log.Add($"맵 파일 로딩 실패: {result.ErrorMessage}");
                    MessageBox.Show($"맵 파일 로딩 실패: {result.ErrorMessage}", "오류",
                        MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
            else
            {
                PUB.log.Add($"맵 파일을 찾을 수 없습니다: {filePath.FullName}");
            }

        }

        #region "Mouse Form Move"

        private Boolean fMove = false;
        private Point MDownPos;

        private void LbTitle_DoubleClick(object sender, EventArgs e)
        {
            if (this.WindowState == FormWindowState.Maximized) this.WindowState = FormWindowState.Normal;
            else this.WindowState = FormWindowState.Maximized;
        }
        private void LbTitle_MouseMove(object sender, MouseEventArgs e)
        {
            if (fMove)
            {
                Point offset = new Point(e.X - MDownPos.X, e.Y - MDownPos.Y);
                this.Left += offset.X;
                this.Top += offset.Y;
                //offset = new Point(0, 0);
            }
        }
        private void LbTitle_MouseUp(object sender, MouseEventArgs e)
        {
            fMove = false;
        }
        private void LbTitle_MouseDown(object sender, MouseEventArgs e)
        {
            MDownPos = new Point(e.X, e.Y);
            fMove = true;
        }

        #endregion
        void socket_SendMessage(object sender, Device.Socket.SocketMessageEventArgs e)
        {
            //핑로그가 꺼져있는 상황일때 핑로그는 로그를 기록하지 않는다.  181205
            if (!PUB.setting.Log_Ping && e.Message.Type == Device.Socket.eType.REPLY && e.Message.SType == Device.Socket.eSType.STATUS)
            {
                return;
            }
            PUB.log.Add("WS << " + e.rawData);
        }

        void socket_RecvMessage(object sender, Device.Socket.SocketMessageEventArgs e)
        {
            if (e.Message.isError)
                PUB.log.AddE("WS >> " + e.Message.ErrorMessage + ",RAW=" + e.rawData);
            else
            {

                switch (e.Message.Type)
                {
                    case Device.Socket.eType.REQUEST:
                        if (e.Message.SType == Device.Socket.eSType.STATUS)
                        {
                            var packet = PUB.sock_debug.makeReplyStatus(Device.Socket.eStatus.IDLE);
                            var rlt = PUB.sock_debug.Send(PUB.setting.Asset, Device.Socket.eType.REPLY, Device.Socket.eSType.STATUS, packet);
                            if (!rlt)
                            {
                                PUB.log.AddE("Status Reply Error");
                            }
                            else
                            {
                                if (PUB.setting.Log_Ping) PUB.log.AddI("Server Status Reply OK");
                            }
                        }
                        else
                        {
                            PUB.log.Add("WS >> " + e.rawData);
                        }
                        break;
                }
            }
        }

        void socket_GetMessage(object sender, Device.Socket.MesasgeEventArgs e)
        {
            if (e.isError) PUB.log.AddE("WS:" + e.Message);
            else PUB.log.Add("WS:" + e.Message);
        }
        delegate void SelectModelHandler(string modelName);
        void SelectModelM(string modelName)
        {
            if (this.InvokeRequired)
            {
                this.Invoke(new SelectModelHandler(SelectModelM), new object[] { modelName });
                return;
            }
        }

        void func_sw_start(bool Prompt = false)
        {
            if (PUB.sm.Step == eSMStep.SYNC)
            {
                UTIL.MsgE("초기화 중에는 사용할 수 없습니다\n초기화가 완료 된 후 시도하세요");
                return;
            }
            if (VAR.BOOL[eVarBool.FLAG_AUTORUN] == false)    //자동상태가 아니라면
            {
                ResetSystemError();
                if (Prompt)
                {
                    if (UTIL.MsgQ("AGV상태를 자동으로 전환 할까요?") != DialogResult.Yes) return;
                }

                //충전상태확인
                if (PUB.CheckManualChargeMode() == false) return;
                PUB.popup.needClose = true;                
                PUB.sm.SetNewStep(eSMStep.RUN);
                PUB.Speak(Lang.자동전환, addlog: false);

                PUB.log.Add($"자동전환 이전스텝:{PUB.sm.RunStep},IDX:{PUB.sm.RunStepSeq}]");
                PUB._mapCanvas.SetInfoMessage($"START ({DateTime.Now.ToShortTimeString()})");
            }
            else
            {
                PUB.AGV.AGVCharge(PUB.setting.ChargerID, false);    //230425 수동전환하면 충전 해제한다
                PUB.sm.SetNewStep(eSMStep.IDLE);
                PUB.Speak(Lang.작업종료);
            }
        }

        void Resultclear()
        {
            PUB.Result.Clear();
            PUB.log.AddI("Clear Resultclear");
        }

        public void ResetSystemError()
        {
            PUB.AGV.AGVErrorReset();
            Resultclear();
            PUB.log.AddI("System Error Reset");
        }


        void CheckFreeSpace()
        {
            try
            {

                if (PUB.path.FullName.StartsWith("\\"))
                {
                    SSInfo.setTitle(0, 1, "UNC Path Detected");
                    SSInfo.setValue(0, 1, 2);
                }
                else
                {
                    double freeSpaceRate_ = PUB.GetFreeSpace();
                    this.freeSpaceRate = freeSpaceRate_;
                    SSInfo.setTitle(0, 1, "FREE(" + PUB.path.FullName.Substring(0, 1) + ") : " + freeSpaceRate.ToString("N2") + "%");
                    if (freeSpaceRate < PUB.setting.AutoDeleteThreshold)
                    {
                        VAR.BOOL[eVarBool.MINSPACE] = true;
                        SSInfo.setValue(0, 1, 2); //lbFreeSpace.ForeColor = Color.Tomato;
                    }
                    else
                    {
                        VAR.BOOL[eVarBool.MINSPACE] = false;
                        SSInfo.setValue(0, 1, 3);// lbFreeSpace.ForeColor = Color.White;
                    }
                }

                //Pub.log.Add("남은디스크공간확인:" + freeSpaceRate.ToString("N2") + "%"); 190129
            }
            catch (Exception ex)
            {
                SSInfo.setTitle(0, 1, "FREE SPACE : -ERROR-");
                PUB.log.AddE("check free space : " + ex.Message);
            }
        }


        private void demoRunToolStripMenuItem_Click(object sender, EventArgs e)
        {
            PUB.Result.JobEndTime = DateTime.Now;
        }

        private void setZValidToolStripMenuItem_Click(object sender, EventArgs e)
        {
            PUB.log.Add("user set z-home set on");
        }

        private void errorToolStripMenuItem_Click(object sender, EventArgs e)
        {
            PUB.popup.setMessage("error\berror message");
        }

        private void informationToolStripMenuItem_Click(object sender, EventArgs e)
        {
            PUB.popup.setMessage("info\ninformation message", MessageWindow.eWindowType.information);
        }

        private void attentionToolStripMenuItem_Click(object sender, EventArgs e)
        {
            PUB.popup.setMessage("attention\nattention message", MessageWindow.eWindowType.attention);
        }


        private void btShowManual_Click(object sender, EventArgs e)
        {
            string file = System.IO.Path.Combine(UTIL.CurrentPath, "manual.pdf");
            if (System.IO.File.Exists(file) == false)
            {
                UTIL.MsgE("사용설명서 파일이 존재하지 않습니다\n" +
                    "문의 : T8567 (장비기술 1파트)\n" +
                    "파일명 : " + file);
                return;
            }
            UTIL.RunExplorer(file);
        }

        private void btClose_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void arLabel5_Click(object sender, EventArgs e)
        {
            //SetScreen(form_setup);


            var popmsg = PUB.popup.Visible;
            PUB.popup.Visible = false;
            VAR.BOOL[eVarBool.FLAG_SETUP] = true;
            using (var f = new fSetup())
                if (f.ShowDialog() == DialogResult.OK)
                {


                    //AGV정보 싱크
                    PUB.sm.ResetRunStepSeq();
                    PUB.sm.SetNewStep(eSMStep.SYNC);
                }

            VAR.BOOL[eVarBool.FLAG_SETUP] = false;// VAR.BOOL[eVarBool.FLAG_SETUP] = false;//VAR.BOOL[eVarBool.FLAG_SETUP] = false;

            if (popmsg) PUB.popup.Visible = true;
        }

        private void btMReset_Click(object sender, EventArgs e)
        {

            //if (Pub.sm.Step != eSMStep.IDLE)
            //{
            var dlg = UTIL.MsgQ(
                "프로그램 상태를 초기화 하시겠습니까?\n" +
                "진행 중인 사항은 모두 취소 됩니다");
            if (dlg != System.Windows.Forms.DialogResult.Yes) return;
            //}
            //else Pub.log.AddAT("대기상태일때에는 초기화 할 수 없습니다");

            PUB.log.Add("User Click : initialize", false);
            PUB.sm.ClearRunStep();
            PUB.sm.SetNewStep(eSMStep.RESET);
            PUB.sm.bPause = false;
            if (PUB.mplayer != null && PUB.mplayer.SoundLocation.isEmpty() == false)
            {
                PUB.mplayer.Stop();
                PUB.bPlayMusic = false;
            }
        }

        private void btCapture_Click(object sender, EventArgs e)
        {
            UTIL.ScreenCapture(Screen.PrimaryScreen.WorkingArea.Width, Screen.PrimaryScreen.WorkingArea.Height, new Point(0, 0));
        }

        private void btOpenDir_Click(object sender, EventArgs e)
        {
            UTIL.RunExplorer(UTIL.CurrentPath);
        }

        private void ManualMan_Click(object sender, EventArgs e)
        {
            SetScreen(form_manu);
            MenuMAN.ForeColor = Color.Gold;
        }

        private void MenuLift_Click(object sender, EventArgs e)
        {
            SetScreen(form_zlift);
            MenuLift.ForeColor = Color.Gold;
        }

        private void btDebug_Click(object sender, EventArgs e)
        {
            arCtl.arLabel ctl = sender as arCtl.arLabel;
            this.cmDebug.Show(ctl, new Point((int)(ctl.Width / 1.5), (int)(ctl.Height / 1.5)));
        }

        private void btAutorun_Click(object sender, EventArgs e)
        {
            var ctl = sender as arCtl.arLabel;
            if (ctl.Enabled == false) return;
            PUB.log.Add("User Click : Start", false);
            func_sw_start(true);
        }

        private void btMenuAuto_Click(object sender, EventArgs e)
        {
            SetScreen(form_auto);
            MenuAuto.ForeColor = Color.Gold;
        }

        private void btMenuFlag_Click(object sender, EventArgs e)
        {
            SetScreen(form_flag);
            MenuFlag.ForeColor = Color.Gold;
        }

        private void btLog_Click(object sender, EventArgs e)
        {
            if (PUB.log != null)
                PUB.log.Flush();

            if (form_log == null || form_log.IsDisposed || form_log.Disposing)
                form_log = new Dialog.fLog();

            form_log.Show();
            form_log.Activate();
            if (form_log.WindowState == FormWindowState.Minimized)
                form_log.WindowState = FormWindowState.Normal;
        }


        private void btCharge_Click(object sender, EventArgs e)
        {
            var bCharge =
              (PUB.sm.RunStep == ERunStep.GOCHARGE || PUB.sm.RunStep == ERunStep.CHARGECHECK || VAR.BOOL[eVarBool.FLAG_CHARGEONA] == true);

            if (VAR.BOOL[eVarBool.FLAG_CHARGEONM])
            {
                UTIL.MsgE("수동 충전 상태이므로 진행 할 수 없습니다");
                return;
            }

            //충전을 시작해라ㅏ
            if (bCharge == true)
            {
                var dlg = UTIL.MsgQ("충전을 중지할까요?");
                if (dlg == DialogResult.Yes)
                {
                    PUB.sm.ClearRunStep();
                    //대상노드를 충전기로하고 
                    PUB._virtualAGV.StartNode = null;
                    PUB._virtualAGV.TargetNode = null;
                    PUB.sm.SetNewRunStep(ERunStep.GOTO);
                    PUB.sm.SetNewStep(eSMStep.RUN);
                    PUB.log.Add("사용자 충전 해제");
                }
            }
            else
            {
                var dlg = UTIL.MsgQ("충전을 시작할까요?");
                if (dlg == DialogResult.Yes)
                {
                    PUB.sm.ClearRunStep();
                    PUB.sm.SetNewRunStep(ERunStep.GOCHARGE);
                    PUB.sm.SetNewStep(eSMStep.RUN);
                    PUB.log.Add("충전(사용자)작업 시작");
                }
            }
        }

        private void pictureBox1_Click(object sender, EventArgs e)
        {
            using (var f = new Dialog.fSystem())
            {
                f.ShowDialog();
                if (f.shutdown)
                {
                    forceClose = true;
                    this.Close();
                }
            }

        }

        private void SSInfo_Click(object sender, EventArgs e)
        {
            PUB.Speak("고경석 수석님 밥은 드셨습니까?", true);
            UpdateProgressStatus(20, 100, "progre");
        }
        private void brHome_Click(object sender, EventArgs e)
        {
            if (PUB.CheckManualChargeMode() == false) return;

            var bHome = PUB.sm.Step == eSMStep.RUN && (PUB.sm.RunStep == ERunStep.GOHOME);
            if (bHome == true)
            {
                var dlg = UTIL.MsgQ("홈 이동을 취소 할까요?");
                if (dlg == DialogResult.Yes)
                {
                    PUB.sm.ClearRunStep();
                    PUB.sm.SetNewStep(eSMStep.IDLE);
                    PUB.AGV.AGVMoveStop("user home cancle", arDev.Narumi.eStopOpt.Stop);
                    PUB.log.Add("사용자 홈 이동 취소");
                }
            }
            else
            {
                var dlg = UTIL.MsgQ("홈 이동을 실행 할까요?");
                if (dlg == DialogResult.Yes)
                {
                    PUB.sm.ClearRunStep();
                    PUB.sm.SetNewRunStep(ERunStep.GOHOME);
                    PUB.sm.SetNewStep(eSMStep.RUN);
                    PUB.log.Add("사용자 홈 이동 실행");
                }
            }
        }


        private void btTopMenu_Volume_Click_1(object sender, EventArgs e)
        {
            using (var f = new Dialog.fVolume())
                f.ShowDialog();
        }

        private void lbCNtUp_Click(object sender, EventArgs e)
        {
            //down,up둘다 연결해야함
            using (var f = new Dialog.fCounter())
                f.ShowDialog();
        }

        private void btMenuAGV_Click_1(object sender, EventArgs e)
        {
            SetScreen(form_agv);
            MenuAGV.ForeColor = Color.Gold;
        }

        private void btMenuBMS_Click_1(object sender, EventArgs e)
        {
            SetScreen(form_bms);
            MenuBMS.ForeColor = Color.Gold;
        }

        private void lbBat_Click(object sender, EventArgs e)
        {
            var bat = (int)PUB.BMS.BMSInformation.rsoc;
            var msg = $"{bat}퍼센트";
            PUB.Speak(msg, false, false);
            PUB.counter.CountQA += 1;
        }
        private void IOState_ItemClick_1(object sender, arFrame.Control.GridView.ItemClickEventArgs e)
        {
            var title = IOState.Titles[e.idx];
            switch (title.ToLower())
            {
                case "emg":
                    ResetSystemError();
                    break;
                case "itm":
                    var itemon = VAR.BOOL[eVarBool.ITEMON];
                    VAR.BOOL[eVarBool.ITEMON] = !itemon;
                    break;
            }
        }

        private void btChargeM_Click(object sender, EventArgs e)
        {
            if (VAR.BOOL[eVarBool.FLAG_CHARGEONM])
            {
                if (PUB.BMS.IsValid && PUB.BMS.IsCharging)
                {
                    UTIL.MsgE("현재 배터리에서 충전 상태가 감지되고 있어 해제할 수 없습니다");
                    return;
                }

                var dlg = UTIL.MsgQ("수동 충전을 해제 할까요?");
                if (dlg != DialogResult.Yes) return;
                VAR.BOOL[eVarBool.FLAG_CHARGEONM] = false;
                PUB.log.Add("수동 충전 실행");
            }
            else
            {
                if (PUB.AGV.system1.Battery_charging)
                {
                    UTIL.MsgE("현재 자동 충전 중이라 진행 할 수 없습니다");
                    return;
                }
                var dlg = UTIL.MsgQ("수동 충전을 진행 할까요?");
                if (dlg != DialogResult.Yes) return;
                VAR.BOOL[eVarBool.FLAG_CHARGEONM] = true;
                PUB.log.Add("수동 충전 실행");
            }
        }

        private void saveToolStripMenuItem_Click(object sender, EventArgs e)
        {
            //mapsave
            using (var sd = new SaveFileDialog())
            {
                sd.Filter = "AGV Map Files (*.json)|*.json|All Files (*.*)|*.*";
                sd.DefaultExt = "json";
                sd.FileName = PUB._mapCanvas.MapFileName;
                if (sd.ShowDialog() == DialogResult.OK)
                {
                    SaveMapToFile(sd.FileName);
                }
            }
        }

        private void loadToolStripMenuItem_Click(object sender, EventArgs e)
        {
            //load

            var od = new OpenFileDialog
            {
                Filter = "AGV Map Files (*.json)|*.json|All Files (*.*)|*.*",
                DefaultExt = "json",
                FileName = PUB._mapCanvas.MapFileName,
            };

            if (od.ShowDialog() == DialogResult.OK)
            {
                try
                {
                    LoadMapFromFile(od.FileName);
                }
                catch (Exception ex)
                {
                    MessageBox.Show($"맵 로드 중 오류가 발생했습니다: {ex.Message}", "오류", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
        }

        private void LoadMapFromFile(string filePath)
        {
            var result = MapLoader.LoadMapFromFile(filePath);

            if (result.Success)
            {
                var _mapCanvas = PUB._mapCanvas;
                _mapCanvas.SetMapLoadResult(result);
                PUB.log.Add($"Set _mapNodes");

                // 설정에 마지막 맵 파일 경로 저장
                PUB.setting.LastMapFile = filePath;
                PUB.setting.Save();

                // 맵 로드 후 자동으로 맵에 맞춤
                _mapCanvas.FitToNodes();
            }
            else
            {
                MessageBox.Show($"맵 파일 로딩 실패: {result.ErrorMessage}", "오류",
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void SaveMapToFile(string filePath)
        {
            // 🔥 백업 파일 생성 (기존 파일이 있을 경우)
            if (File.Exists(filePath))
            {
                try
                {
                    // 날짜시간 포함 백업 파일명 생성
                    var directory = Path.GetDirectoryName(filePath);
                    var fileNameWithoutExt = Path.GetFileNameWithoutExtension(filePath);
                    var extension = Path.GetExtension(filePath);
                    var timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
                    var backupFileName = $"{fileNameWithoutExt}_{timestamp}{extension}.bak";
                    var backupFilePath = Path.Combine(directory, backupFileName);

                    // 기존 파일을 백업 파일로 복사
                    File.Copy(filePath, backupFilePath, true);
                }
                catch (Exception ex)
                {
                    // 백업 파일 생성 실패 시 경고만 표시하고 계속 진행
                    MessageBox.Show($"백업 파일 생성 실패: {ex.Message}\n원본 파일은 계속 저장됩니다.", "백업 경고",
                        MessageBoxButtons.OK, MessageBoxIcon.Warning);
                }
            }

            var _mapCanvas = PUB._mapCanvas;


            // 🔥 현재 캔버스 설정을 맵 파일에 저장
            var settings = new MapLoader.MapSettings
            {
                BackgroundColorArgb = _mapCanvas.BackColor.ToArgb(),
                ShowGrid = _mapCanvas.ShowGrid
            };

            if (MapLoader.SaveMapToFile(filePath,
                _mapCanvas.Nodes, _mapCanvas.Labels,
                _mapCanvas.Images, _mapCanvas.Marks,
                _mapCanvas.Magnets,
                settings))
            {
                // 설정에 마지막 맵 파일 경로 저장
                PUB.setting.LastMapFile = filePath;
                PUB.setting.Save();
            }
            else
            {
                MessageBox.Show("맵 파일 저장 실패", "오류",
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void DumpAllThreadsState()
        {
            try
            {
                var sb = new System.Text.StringBuilder();
                sb.AppendLine("===== 스레드 상태 덤프 =====");
                sb.AppendLine($"시간: {DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}");
                //sb.AppendLine($"마지막 sm_Running 호출: {(DateTime.Now - lastSmRunningTime).TotalSeconds:F1}초 전");
                //sb.AppendLine($"sm_Running 호출 횟수: {sm_Running_CallCount}");
                sb.AppendLine($"IsThreadRun: {PUB.sm.IsThreadRun}");
                sb.AppendLine($"Current Step: {PUB.sm.Step}");
                sb.AppendLine($"Current RunStep: {PUB.sm.RunStep}");
                sb.AppendLine();

                var process = System.Diagnostics.Process.GetCurrentProcess();
                sb.AppendLine($"총 스레드 수: {process.Threads.Count}");
                sb.AppendLine();

                foreach (System.Diagnostics.ProcessThread thread in process.Threads)
                {
                    sb.AppendLine($"Thread {thread.Id}:");
                    sb.AppendLine($"  State: {thread.ThreadState}");
                    sb.AppendLine($"  Priority: {thread.PriorityLevel}");
                    if (thread.ThreadState == System.Diagnostics.ThreadState.Wait)
                    {
                        sb.AppendLine($"  WaitReason: {thread.WaitReason}");
                    }
                    sb.AppendLine();
                }

                var dump = sb.ToString();
                PUB.log.Add("THREAD_DUMP", dump);
                Console.WriteLine(dump);

                MessageBox.Show(
                    $"스레드 덤프 완료\n로그 파일에 저장되었습니다.\n\n" +
                    //$"마지막 sm_Running: {(DateTime.Now - lastSmRunningTime).TotalSeconds:F1}초 전\n" +
                    $"IsThreadRun: {PUB.sm.IsThreadRun}",
                    "스레드 덤프",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Information);
            }
            catch (Exception ex)
            {
                PUB.log.AddE($"DumpAllThreadsState 오류: {ex.Message}");
            }
        }

        private void stateMachineDebugToolStripMenuItem_Click(object sender, EventArgs e)
        {
            try
            {
                var debugForm = new Dialog.fStateMachineDebug();
                debugForm.Show();
            }
            catch (Exception ex)
            {
                MessageBox.Show($"디버그 창을 열 수 없습니다:\n{ex.Message}", "오류",
                              MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void editorToolStripMenuItem_Click(object sender, EventArgs e)
        {
            try
            {
                // MapEditor 실행 파일 경로 확인
                string mapEditorPath = "AGVMapEditor.exe";

                // 경로가 설정되지 않았거나 파일이 없는 경우 사용자에게 선택을 요청
                if (string.IsNullOrEmpty(mapEditorPath) || !File.Exists(mapEditorPath))
                {
                    using (var openDialog = new OpenFileDialog())
                    {
                        openDialog.Filter = "실행 파일 (*.exe)|*.exe|모든 파일 (*.*)|*.*";
                        openDialog.Title = "AGV MapEditor 실행 파일 선택";
                        openDialog.InitialDirectory = Application.StartupPath;

                        if (openDialog.ShowDialog() == DialogResult.OK)
                        {
                            mapEditorPath = openDialog.FileName;
                        }
                        else
                        {
                            return; // 사용자가 취소함
                        }
                    }
                }

                // MapEditor 실행
                var startInfo = new System.Diagnostics.ProcessStartInfo
                {
                    FileName = mapEditorPath,
                    UseShellExecute = true
                };

                // 현재 로드된 맵 파일이 있으면 파라미터로 전달
                var _currentMapFilePath = PUB._mapCanvas.MapFileName;
                if (!string.IsNullOrEmpty(_currentMapFilePath) && File.Exists(_currentMapFilePath))
                {
                    startInfo.Arguments = $"\"{_currentMapFilePath}\"";
                }

                System.Diagnostics.Process.Start(startInfo);
            }
            catch (Exception ex)
            {
                MessageBox.Show($"MapEditor를 실행할 수 없습니다:\n{ex.Message}", "오류",
                              MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void xbeeSettingToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var f = new Dialog.fXbeeSetting();
            f.Show();
        }

        private void buffer2ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var buffernode = PUB._mapCanvas.Nodes.Where(t => t.AliasName == "BUFFER2").FirstOrDefault();
            
        }
    }
}
