using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using AGVNavigationCore.Models;
using AGVNavigationCore.Controls;
using AGVNavigationCore.PathFinding;
using AGVNavigationCore.Utils;
using Newtonsoft.Json;
using AGVNavigationCore.PathFinding.Planning;
using AGVNavigationCore.PathFinding.Core;
using AGVSimulator.Models;
using System.IO.Ports;
using System.Text;

namespace AGVSimulator.Forms
{
    /// <summary>
    /// AGV 시뮬레이터 메인 폼
    /// </summary>
    public partial class SimulatorForm : Form
    {
        #region Fields

        // Emulator Fields
        private SerialPort _emulatorPort;
        private ComboBox _portCombo;
        private Button _connectButton;
        private bool _isEmulatorConnected = false;

        // Emulator State Fields
        private UInt16 _emu_system0 = 0;
        private UInt16 _emu_system1 = 0;
        private UInt16 _emu_error = 0;
        private byte _emu_signal = 0;
        private char _emu_sts_bunki = 'S';
        private char _emu_sts_speed = 'L';
        private char _emu_sts_dir = 'F';
        private char _emu_sts_sensor = '1';
        private string _lastSentNodeId = null;

        public enum esystemflag0
        {
            Memory_RW_State = 5,
            EXT_IO_Conn_State,
            RFID_Conn_State,
            M5E_Module_Run_State = 8,
            Front_Ultrasonic_Conn_State,
            Front_Untrasonic_Sensor_State,
            Side_Ultrasonic_Conn_State,
            Side_Ultrasonic_Sensor_State = 12,
            Front_Guide_Sensor_State,
            Rear_Guide_Sensor_State,
            Battery_Level_Check
        }
        public enum esystemflag1
        {
            Side_Detect_Ignore = 3,
            Melody_check,
            Mark2_check,
            Mark1_check,
            gateout_check,
            Battery_charging = 8,
            re_Start,
            front_detect_ignore,
            front_detect_check,
            stop_by_front_detect = 12,
            stop_by_cross_in,
            agv_stop,
            agv_run
        }
        public enum eerror
        {
            Emergency = 0,
            Overcurrent,
            Charger_run_error,
            Charger_pos_error,
            line_out_error = 4,
            runerror_by_no_magent_line,
            controller_comm_error = 11,
            arrive_ctl_comm_error,
            door_ctl_comm_error,
            charger_comm_error,
            cross_ctrl_comm_error,
        }
        public enum esignal
        {
            front_gate_out = 0,
            rear_sensor_out,
            mark_sensor_1,
            mark_sensor_2,
            front_left_sensor,
            front_right_sensor,
            front_center_sensor,
            charger_align_sensor,
        }
        public enum estsvaluetype
        {
            bunki,
            speed,
            direction,
            sensor
        }

        private UnifiedAGVCanvas _simulatorCanvas;
        // private AGVPathfinder _advancedPathfinder;
        private List<VirtualAGV> _agvList;
        private SimulationState _simulationState;
        private System.Windows.Forms.Timer _simulationTimer;
        private SimulatorConfig _config;
        private string _currentMapFilePath;
        private bool _isTargetCalcMode;  // 타겟계산 모드 상태

        // 맵 스캔 모드 관련
        private bool _isMapScanMode;  // 맵 스캔 모드 상태
        private DateTime _lastNodeAddTime;  // 마지막 노드 추가 시간
        private MapNode _lastScannedNode;  // 마지막으로 스캔된 노드
        private int _scanNodeCounter;  // 스캔 노드 카운터
        private AgvDirection _lastScanDirection;  // 마지막 스캔 방향

        // UI Controls - Designer에서 생성됨

        #endregion

        #region Properties

        /// <summary>
        /// 시뮬레이션 상태
        /// </summary>
        public SimulationState SimulationState => _simulationState;

        #endregion

        #region Constructor

        public SimulatorForm()
        {
            InitializeComponent();
            InitializeForm();

            // Load 이벤트 연결
            this.Load += SimulatorForm_Load;
        }

        #endregion

        #region Initialization

        private void InitializeForm()
        {
            // 설정 로드
            _config = SimulatorConfig.Load();

            // 데이터 초기화

            _agvList = new List<VirtualAGV>();
            _simulationState = new SimulationState();
            _currentMapFilePath = string.Empty;

            // 시뮬레이터 캔버스 생성 (중앙 패널에만)
            CreateSimulatorCanvas();

            // 타이머 초기화
            _simulationTimer = new System.Windows.Forms.Timer();
            _simulationTimer.Interval = 100; // 100ms 간격
            _simulationTimer.Tick += OnSimulationTimer_Tick;

            // 방향 콤보박스 초기화
            InitializeDirectionCombo();

            // 에뮬레이터 UI 초기화
            InitializeEmulatorUI();

            // 초기 상태 설정
            UpdateUI();

            // 마지막 맵 파일 자동 로드 확인은 Form_Load에서 수행
        }

        private void CreateSimulatorCanvas()
        {
            _simulatorCanvas = new UnifiedAGVCanvas();
            _simulatorCanvas.Dock = DockStyle.Fill;
            _simulatorCanvas.Mode = UnifiedAGVCanvas.CanvasMode.Emulator;

            // 목적지 선택 이벤트 구독
            _simulatorCanvas.NodesSelected += OnTargetNodeSelected;

            _canvasPanel.Controls.Add(_simulatorCanvas);
        }

        private void SetupLayout()
        {
            // Z-Order 설정 - 모든 컨트롤이 디자이너에 구현되어 자동 관리됨
            _canvasPanel.BringToFront();
        }

        /// <summary>
        /// 모터 구동방향 콤보박스 초기화
        /// </summary>
        private void InitializeDirectionCombo()
        {
            _directionCombo.Items.Clear();

            // AgvDirection enum 값들을 콤보박스에 추가
            _directionCombo.Items.Add(new DirectionItem(AgvDirection.Forward, "전진 (모니터쪽)"));
            _directionCombo.Items.Add(new DirectionItem(AgvDirection.Backward, "후진 (리프트쪽)"));
            _directionCombo.Items.Add(new DirectionItem(AgvDirection.Left, "좌회전"));
            _directionCombo.Items.Add(new DirectionItem(AgvDirection.Right, "우회전"));
            _directionCombo.Items.Add(new DirectionItem(AgvDirection.Stop, "정지"));

            // 기본 선택: 전진
            _directionCombo.SelectedIndex = 0;
        }

        #endregion

        #region Event Handlers

        private void SimulatorForm_Load(object sender, EventArgs e)
        {
            // 폼이 완전히 로드된 후 마지막 맵 파일 자동 로드 확인
            CheckAndLoadLastMapFile();
        }

        private void OnOpenMap_Click(object sender, EventArgs e)
        {
            using (var openDialog = new OpenFileDialog())
            {
                openDialog.Filter = "AGV Map Files (*.json)|*.json|모든 파일 (*.*)|*.*";
                openDialog.Title = "맵 파일 열기";

                if (openDialog.ShowDialog() == DialogResult.OK)
                {
                    try
                    {
                        LoadMapFile(openDialog.FileName);
                        _statusLabel.Text = $"맵 로드 완료: {Path.GetFileName(openDialog.FileName)}";
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show($"맵 파일을 로드할 수 없습니다:\n{ex.Message}", "오류",
                                      MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                }
            }
        }

        private void OnExit_Click(object sender, EventArgs e)
        {
            Close();
        }

        private void OnStartSimulation_Click(object sender, EventArgs e)
        {
            if (_simulationState.IsRunning)
                return;

            _simulationState.IsRunning = true;
            _simulationTimer.Start();
            _statusLabel.Text = "시뮬레이션 실행 중";
            Console.WriteLine("시뮬레이션 실행");
            UpdateUI();

            timer1.Start();
        }

        private void OnStopSimulation_Click(object sender, EventArgs e)
        {
            if (!_simulationState.IsRunning)
                return;

            _simulationState.IsRunning = false;
            _simulationTimer.Stop();
            _statusLabel.Text = "시뮬레이션 정지";
            Console.WriteLine("시뮬레이션 정지");
            UpdateUI();
        }

        private void OnReset_Click(object sender, EventArgs e)
        {

        }

        private void OnFitToMap_Click(object sender, EventArgs e)
        {
            _simulatorCanvas.FitToNodes();
        }

        private void OnResetZoom_Click(object sender, EventArgs e)
        {
            _simulatorCanvas.ResetZoom();
        }

        private void OnAbout_Click(object sender, EventArgs e)
        {
            MessageBox.Show("AGV 시뮬레이터 v1.0\n\nENIG AGV 시스템용 시뮬레이터", "정보",
                          MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        private void OnAddAGV_Click(object sender, EventArgs e)
        {
            if (_simulatorCanvas.Nodes == null || _simulatorCanvas.Nodes.Count == 0)
            {
                MessageBox.Show("먼저 맵을 로드해주세요.", "알림", MessageBoxButtons.OK, MessageBoxIcon.Information);
                return;
            }

            var agvId = $"AGV{_agvList.Count + 1:D2}";
            var startPosition = _simulatorCanvas.Nodes.First().Position; // 첫 번째 노드에서 시작

            var newAGV = new VirtualAGV(agvId, startPosition);
            _agvList.Add(newAGV);
            _simulatorCanvas.AGVList = new List<IAGV>(_agvList.Cast<IAGV>());

            // 콘솔 출력

            Program.WriteLine($"[SYSTEM] AGV 추가:");
            Program.WriteLine($"  AGV ID: {agvId}");
            Program.WriteLine($"  시작 위치: ({startPosition.X}, {startPosition.Y})");
            Program.WriteLine($"  총 AGV 수: {_agvList.Count}");
            Program.WriteLine("");


            UpdateAGVComboBox();
            UpdateUI();

            _statusLabel.Text = $"{agvId} 추가됨";
            _simulatorCanvas.FitToNodes();
        }

        private void OnRemoveAGV_Click(object sender, EventArgs e)
        {
            if (_agvListCombo.SelectedItem == null)
                return;

            var selectedAGV = _agvListCombo.SelectedItem as VirtualAGV;
            if (selectedAGV != null)
            {
                _agvList.Remove(selectedAGV);
                _simulatorCanvas.AGVList = new List<IAGV>(_agvList.Cast<IAGV>());

                // 콘솔 출력
                Console.WriteLine($"[SYSTEM] AGV 제거:");
                Console.WriteLine($"  AGV ID: {selectedAGV.AgvId}");
                Console.WriteLine($"  남은 AGV 수: {_agvList.Count}");
                Console.WriteLine("");


                UpdateAGVComboBox();
                UpdateUI();

                _statusLabel.Text = $"{selectedAGV.AgvId} 제거됨";
            }
        }

        private void OnAGVList_SelectedIndexChanged(object sender, EventArgs e)
        {
            UpdateUI();
        }



        private void OnClearPath_Click(object sender, EventArgs e)
        {
            _simulatorCanvas.CurrentPath = null;
            _pathLengthLabel.Text = "경로 길이: -";
            _statusLabel.Text = "경로 지움";

            // 🔥 VirtualAGV의 경로도 정지
            if (_agvList != null && _agvList.Count > 0)
            {
                _agvList[0].StopPath();
            }
        }

        private void OnTargetCalc_Click(object sender, EventArgs e)
        {
            if (_isTargetCalcMode)
            {
                // 타겟계산 모드 해제
                _isTargetCalcMode = false;
                _targetCalcButton.Text = "타겟계산";
                _targetCalcButton.BackColor = SystemColors.Control;
                _statusLabel.Text = "타겟계산 모드 해제";
            }
            else
            {
                // 타겟계산 모드 활성화
                _isTargetCalcMode = true;
                _targetCalcButton.Text = "계산 취소";
                _targetCalcButton.BackColor = Color.LightGreen;
                _statusLabel.Text = "목적지 노드를 클릭하세요 (자동으로 경로 계산됨)";
            }
        }

        private void OnTargetNodeSelected(object sender, List<NodeBase> selectedNodes)
        {
            try
            {
                // PropertyGrid 업데이트 (항상 수행)
                propertyNode.SelectedObject = selectedNodes.FirstOrDefault();

                // 타겟계산 모드에서만 처리
                if (_isTargetCalcMode)
                {
                    // 타겟계산 모드 해제
                    //_isTargetCalcMode = false;
                    //_targetCalcButton.Text = "타겟계산";
                    //_targetCalcButton.BackColor = SystemColors.Control;
                    //_simulatorCanvas.CurrentEditMode = UnifiedAGVCanvas.EditMode.Select;
                    var selectedNode = selectedNodes.FirstOrDefault();
                    if (selectedNode == null) return;

                    // 목적지를 선택된 노드로 설정
                    SetTargetNodeInCombo(selectedNode.Id);

                    var displayText = GetDisplayName(selectedNode.Id);
                    _statusLabel.Text = $"타겟계산 - 목적지: {displayText}";

                }
            }
            catch (Exception ex)
            {
                _statusLabel.Text = $"노드 선택 오류: {ex.Message}";
            }
        }

        /// <summary>
        /// 목적지 콤보박스에 노드 설정
        /// </summary>
        private void SetTargetNodeInCombo(string nodeId)
        {
            for (int i = 0; i < _targetNodeCombo.Items.Count; i++)
            {
                var item = _targetNodeCombo.Items[i].ToString();
                if (item.Contains($"[{nodeId}]"))
                {
                    _targetNodeCombo.SelectedIndex = i;
                    break;
                }
            }
        }

        /// <summary>
        /// AGV 현재 노드로 시작 노드 설정
        /// </summary>
        private void SetStartNodeFromAGVPosition()
        {
            try
            {
                if (_agvList.Count > 0)
                {
                    var agv = _agvList[0]; // 첫 번째 AGV 사용
                    var currentNodeId = agv.CurrentNodeId;

                    // AGV가 현재 노드 정보를 가지고 있는 경우 직접 사용
                    if (!string.IsNullOrEmpty(currentNodeId))
                    {
                        // 시작 노드 콤보박스에 설정
                        for (int i = 0; i < _startNodeCombo.Items.Count; i++)
                        {
                            var item = _startNodeCombo.Items[i].ToString();
                            if (item.Contains($"[{currentNodeId}]"))
                            {
                                _startNodeCombo.SelectedIndex = i;
                                return; // 성공적으로 설정됨
                            }
                        }
                    }

                    // CurrentNodeId가 없거나 콤보박스에서 찾지 못한 경우 위치 기반으로 폴백
                    var currentPos = agv.CurrentPosition;
                    var closestNode = FindClosestNode(currentPos);
                    if (closestNode != null)
                    {
                        // 시작 노드 콤보박스에 설정
                        for (int i = 0; i < _startNodeCombo.Items.Count; i++)
                        {
                            var item = _startNodeCombo.Items[i].ToString();
                            if (item.Contains($"[{closestNode.Id}]"))
                            {
                                _startNodeCombo.SelectedIndex = i;
                                break;
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                _statusLabel.Text = $"시작 노드 설정 오류: {ex.Message}";
            }
        }

        /// <summary>
        /// 위치에서 가장 가까운 노드 찾기
        /// </summary>
        private MapNode FindClosestNode(Point position)
        {
            if (_simulatorCanvas.Nodes == null || _simulatorCanvas.Nodes.Count == 0)
                return null;

            MapNode closestNode = null;
            double closestDistance = double.MaxValue;

            foreach (var node in _simulatorCanvas.Nodes)
            {
                var distance = Math.Sqrt(Math.Pow(node.Position.X - position.X, 2) +
                                       Math.Pow(node.Position.Y - position.Y, 2));
                if (distance < closestDistance)
                {
                    closestDistance = distance;
                    closestNode = node;
                }
            }

            return closestNode;
        }

        /// <summary>
        /// 방향을 기호로 변환
        /// </summary>
        private string GetDirectionSymbol(AgvDirection direction)
        {
            switch (direction)
            {
                case AgvDirection.Forward: return "→";
                case AgvDirection.Backward: return "←";
                case AgvDirection.Left: return "↺";
                case AgvDirection.Right: return "↻";
                default: return "-";
            }
        }

        /// <summary>
        /// 맵 스캔 모드에서 RFID로부터 노드 생성
        /// </summary>
        private void CreateNodeFromRfidScan(ushort rfidId, VirtualAGV selectedAGV)
        {
            try
            {
                // 현재 선택된 방향 확인 (최상단에서 먼저 확인)
                var directionItem = _directionCombo.SelectedItem as DirectionItem;
                var currentDirection = directionItem?.Direction ?? AgvDirection.Forward;

                // 중복 RFID 확인
                var existingNode = _simulatorCanvas.Nodes?.FirstOrDefault(n => n.RfidId == rfidId);
                if (existingNode != null)
                {
                    // 이미 존재하는 노드로 이동
                    Program.WriteLine($"[맵 스캔] RFID '{rfidId}'는 이미 존재합니다 (노드: {existingNode.Id})");

                    // 기존 노드로 AGV 위치 설정
                    _simulatorCanvas.SetAGVPosition(selectedAGV.AgvId, existingNode, currentDirection);
                    selectedAGV.SetPosition(existingNode, currentDirection);

                    _lastScannedNode = existingNode;
                    _lastNodeAddTime = DateTime.Now;
                    _lastScanDirection = currentDirection;  // 방향 업데이트

                    _statusLabel.Text = $"기존 노드로 이동: {existingNode.Id} [{GetDirectionSymbol(currentDirection)}]";
                    _rfidTextBox.Text = "";
                    return;
                }

                // 새 노드 생성 위치 계산
                int newX = 100; // 기본 시작 X 위치
                int newY = 300; // 기본 시작 Y 위치

                if (_lastScannedNode != null)
                {
                    // 시간차 기반 X축 거리 계산
                    var timeDiff = (DateTime.Now - _lastNodeAddTime).TotalSeconds;

                    // 10초당 10px, 최소 50px, 최대 100px
                    int distanceX = Math.Max(50, Math.Min(100, (int)(timeDiff * 10)));

                    // 방향 전환 확인
                    bool directionChanged = (_lastScanDirection != currentDirection);

                    if (directionChanged)
                    {
                        // 방향이 바뀌면 Y축을 50px 증가시켜서 겹치지 않게 함
                        newY = _lastScannedNode.Position.Y + 50;
                        newX = _lastScannedNode.Position.X; // X는 같은 위치에서 시작

                        Program.WriteLine($"[맵 스캔] 방향 전환: {_lastScanDirection} → {currentDirection}, Y축 +50px");
                    }
                    else
                    {
                        // 방향이 같으면 Y축 유지
                        newY = _lastScannedNode.Position.Y;

                        // 모터 방향에 따라 X축 증가/감소
                        if (currentDirection == AgvDirection.Forward)
                        {
                            // 전진: X축 증가
                            newX = _lastScannedNode.Position.X + distanceX;
                            Program.WriteLine($"[맵 스캔] 전진 모드: X축 +{distanceX}px");
                        }
                        else if (currentDirection == AgvDirection.Backward)
                        {
                            // 후진: X축 감소
                            newX = _lastScannedNode.Position.X - distanceX;
                            Program.WriteLine($"[맵 스캔] 후진 모드: X축 -{distanceX}px");
                        }
                        else
                        {
                            // 그 외(회전 등): 기본적으로 전진 방향 사용
                            newX = _lastScannedNode.Position.X + distanceX;
                            Program.WriteLine($"[맵 스캔] 기타 방향({currentDirection}): X축 +{distanceX}px");
                        }
                    }

                    Program.WriteLine($"[맵 스캔] 시간차: {timeDiff:F1}초 → 거리: {distanceX}px");
                }

                // 새 노드 생성
                var newNodeId = $"{_scanNodeCounter:D3}";
                var newNode = new MapNode
                {
                    Id = newNodeId,
                    RfidId = rfidId,
                    Position = new Point(newX, newY),
                    IsActive = true
                };

                // 맵에 추가
                if (_simulatorCanvas.Nodes == null)
                    _simulatorCanvas.Nodes = new List<MapNode>();

                _simulatorCanvas.Nodes.Add(newNode);

                // 이전 노드와 연결 생성
                if (_lastScannedNode != null)
                {
                    // 양방향 연결 (ConnectedNodes에 추가 - JSON 저장됨)
                    _lastScannedNode.AddConnection(newNode.Id);
                    newNode.AddConnection(_lastScannedNode.Id);

                    Program.WriteLine($"[맵 스캔] 연결 생성: {_lastScannedNode.Id} ↔ {newNode.Id}");
                }

                // AGV 위치 설정
                _simulatorCanvas.SetAGVPosition(selectedAGV.AgvId, newNode, currentDirection);
                selectedAGV.SetPosition(newNode, currentDirection);

                // 캔버스 업데이트
                _simulatorCanvas.Nodes = _simulatorCanvas.Nodes;

                // 화면을 새 노드 위치로 이동
                _simulatorCanvas.PanToNode(newNode.Id);
                _simulatorCanvas.Invalidate();

                // 상태 업데이트
                _lastScannedNode = newNode;
                _lastNodeAddTime = DateTime.Now;
                _lastScanDirection = currentDirection;  // 현재 방향 저장
                _scanNodeCounter++;

                // UI 업데이트
                UpdateNodeComboBoxes();

                _statusLabel.Text = $"노드 생성: {newNode.Id} (RFID: {rfidId}) [{GetDirectionSymbol(currentDirection)}] - 총 {_simulatorCanvas.Nodes.Count}개";
                _rfidTextBox.Text = "";

                Program.WriteLine($"[맵 스캔] 노드 생성 완료: {newNode.Id} (RFID: {rfidId}) at ({newX}, {newY}), 방향: {currentDirection}");
            }
            catch (Exception ex)
            {
                MessageBox.Show($"노드 생성 중 오류 발생:\n{ex.Message}", "오류",
                              MessageBoxButtons.OK, MessageBoxIcon.Error);
                Program.WriteLine($"[맵 스캔 오류] {ex.Message}");
            }
        }

        private void OnSetPosition_Click(object sender, EventArgs e)
        {
            SetAGVPositionByRfid();
            _simulatorCanvas.FitToNodes();
        }

        private void OnRfidTextBox_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (e.KeyChar == (char)Keys.Enter)
            {
                SetAGVPositionByRfid();
                _simulatorCanvas.FitToNodes();
                e.Handled = true;
            }
        }

        private void OnSimulationTimer_Tick(object sender, EventArgs e)
        {
            // 모든 AGV의 업데이트 메서드 호출 (100ms 간격)
            if (_agvList != null)
            {
                foreach (var agv in _agvList)
                {
                    agv.Update(100);  // 100ms 간격으로 업데이트

                    // Emulator Tag Logic
                    if (_isEmulatorConnected && agv == _agvList.FirstOrDefault())
                    {
                        if (agv.CurrentNodeId != null && agv.CurrentNodeId != _lastSentNodeId)
                        {
                            var rfid = GetRfidByNodeId(agv.CurrentNodeId);
                            if (rfid > 0)
                            {
                                SendTag(rfid);
                                _lastSentNodeId = agv.CurrentNodeId;
                            }
                        }
                    }
                }
            }

            // UI 업데이트
            UpdateUI();
            _simulatorCanvas.Invalidate();  // 화면 다시 그리기

            // 에뮬레이터 상태 전송
            if (_isEmulatorConnected)
            {
                SendEmulatorStatus();
            }
        }

        #endregion

        #region Private Methods

        private void SetAGVPositionByRfid()
        {
            // 선택된 AGV 확인
            var selectedAGV = _agvListCombo.SelectedItem as VirtualAGV;
            if (selectedAGV == null)
            {
                MessageBox.Show("먼저 AGV를 선택해주세요.", "알림", MessageBoxButtons.OK, MessageBoxIcon.Information);
                return;
            }

            // RFID 값 확인
            var rfidId = _rfidTextBox.Text.Trim();
            if (ushort.TryParse(rfidId, out ushort rfidvalue) == false)
            {
                MessageBox.Show("RFID 값을 입력해주세요.", "알림", MessageBoxButtons.OK, MessageBoxIcon.Information);
                return;
            }

            // 선택된 방향 확인
            var selectedDirectionItem = _directionCombo.SelectedItem as DirectionItem;
            var selectedDirection = selectedDirectionItem?.Direction ?? AgvDirection.Forward;


            // 맵 스캔 모드일 때: 노드 자동 생성
            if (_isMapScanMode)
            {
                CreateNodeFromRfidScan(rfidvalue, selectedAGV);
                this._simulatorCanvas.FitToNodes();
                return;
            }

            // RFID에 해당하는 노드 직접 찾기
            var targetNode = _simulatorCanvas.Nodes?.FirstOrDefault(n => n.RfidId == rfidvalue);
            if (targetNode == null)
            {
                MessageBox.Show($"RFID '{rfidId}'에 해당하는 노드를 찾을 수 없습니다.\n\n사용 가능한 RFID 목록:\n{GetAvailableRfidList()}",
                              "RFID 찾기 실패", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }


            //이전위치와 동일한지 체크한다.
            if (selectedAGV.CurrentNodeId == targetNode.Id && selectedAGV.CurrentDirection == selectedDirection)
            {
                Program.WriteLine($"이전 노드위치와 모터의 방향이 동일하여 현재 위치 변경이 취소됩니다(NODE:{targetNode.Id},RFID:{targetNode.RfidId},DIR:{selectedDirection})");
                return;
            }

            // 콘솔 출력 (상세한 리프트 방향 계산 과정)
            Program.WriteLine($"[AGV-{selectedAGV.AgvId}] 위치 설정:");
            Program.WriteLine($"  RFID: {rfidId} → 노드: {targetNode.Id}");
            Program.WriteLine($"  위치: ({targetNode.Position.X}, {targetNode.Position.Y})");
            Program.WriteLine($"  방향: {selectedDirectionItem?.DisplayText ?? "전진"} ({selectedDirection})");

            // SetPosition 호출 전 상태
            var PrevNodeID = selectedAGV.CurrentNodeId;
            var PrevDir = selectedAGV.CurrentDirection;
            var PrevPosition = selectedAGV.CurrentPosition;
            Program.WriteLine($"  [BEFORE] Node:{PrevNodeID}, Dir:{PrevDir},Pos X:{PrevPosition.X},{PrevPosition.Y}");


            // AGV 위치 및 방향 설정
            _simulatorCanvas.SetAGVPosition(selectedAGV.AgvId, targetNode, selectedDirection);

            // VirtualAGV 객체의 위치와 방향 업데이트
            selectedAGV.SetPosition(targetNode, selectedDirection);  // 이전 위치 기억하도록

            // SetPosition 호출 후 상태 확인 및 리프트 계산

            var newPrevPos = selectedAGV.PrevPosition;
            var newCurrentPos = selectedAGV.CurrentPosition;
            Program.WriteLine($"  [AFTER]  새로운 CurrentPosition: ({newCurrentPos.X}, {newCurrentPos.Y})");
            Program.WriteLine($"  [AFTER]  새로운 PrevPosition: {(newPrevPos.HasValue ? $"({newPrevPos.Value.X}, {newPrevPos.Value.Y})" : "None")}");

            // 리프트 방향 계산 과정 상세 출력
            Program.WriteLine($"  [LIFT] 리프트 방향 계산:");
            CalculateLiftDirectionDetailed(selectedAGV);
            Program.WriteLine("");

            _statusLabel.Text = $"{selectedAGV.AgvId} 위치를 RFID '{rfidId}' (노드: {targetNode.Id}), 방향: {selectedDirectionItem?.DisplayText ?? "전진"}로 설정했습니다.";
            _rfidTextBox.Text = ""; // 입력 필드 초기화

            // 시뮬레이터 캔버스의 해당 노드로 이동
            //_simulatorCanvas.PanToNode(targetNode.NodeId);

            // 시작 노드 콤보박스를 현재 위치로 자동 선택
            SetStartNodeToCombo(targetNode.Id);
        }

        /// <summary>
        /// 시작 노드 콤보박스에 노드를 설정
        /// </summary>
        private void SetStartNodeToCombo(string nodeId)
        {
            try
            {
                for (int i = 0; i < _startNodeCombo.Items.Count; i++)
                {
                    var item = _startNodeCombo.Items[i] as ComboBoxItem<MapNode>;//.ToString();
                    if (item.Value.Id.Equals(nodeId))
                    {
                        _startNodeCombo.SelectedIndex = i;
                        Program.WriteLine($"[SYSTEM] 시작 노드를 '{nodeId}'로 자동 선택했습니다.");
                        break;
                    }
                }
            }
            catch (Exception ex)
            {
                Program.WriteLine($"[ERROR] 시작 노드 자동 선택 실패: {ex.Message}");
            }
        }

        private string GetAvailableRfidList()
        {
            if (_simulatorCanvas.Nodes == null || _simulatorCanvas.Nodes.Count == 0)
                return "매핑된 RFID가 없습니다.";

            var nodesWithRfid = _simulatorCanvas.Nodes.Where(n => n.HasRfid()).ToList();
            if (nodesWithRfid.Count == 0)
                return "RFID가 할당된 노드가 없습니다.";

            // 처음 10개의 RFID만 표시 (노드 이름 포함)
            var rfidList = nodesWithRfid.Take(10).Select((Func<MapNode, string>)(n =>
            {
                return $"- {n.RfidId} → {n.Id}";
            }));
            var result = string.Join("\n", rfidList);

            if (nodesWithRfid.Count > 10)
                result += $"\n... 외 {nodesWithRfid.Count - 10}개";

            return result;
        }

        private void LoadMapFile(string filePath)
        {
            try
            {
                var result = MapLoader.LoadMapFromFile(filePath);
                sbFile.Text = filePath;
                if (result.Success)
                {
                    Console.WriteLine($"Map File Load : {filePath}");

                    _simulatorCanvas.Nodes = result.Nodes;
                    _currentMapFilePath = filePath;

                    // RFID 자동 할당 제거 - 에디터에서 설정한 값 그대로 사용

                    // 시뮬레이터 캔버스에 맵 설정
                    _simulatorCanvas.SetMapLoadResult(result);//.Nodes = _simulatorCanvas.Nodes;

                    // 맵 설정 적용 (배경색, 그리드 표시)
                    if (result.Settings != null)
                    {
                        _simulatorCanvas.BackColor = System.Drawing.Color.FromArgb(result.Settings.BackgroundColorArgb);
                        _simulatorCanvas.ShowGrid = result.Settings.ShowGrid;
                    }

                    // 설정에 마지막 맵 파일 경로 저장
                    _config.LastMapFilePath = filePath;
                    if (_config.AutoSave)
                    {
                        _config.Save();
                    }

                    // UI 업데이트
                    UpdateNodeComboBoxes();
                    UpdateUI();

                    // 맵에 맞춤
                    _simulatorCanvas.FitToNodes();
                }
                else
                {
                    throw new InvalidOperationException($"맵 파일 로드 실패: {result.ErrorMessage}");
                }
            }
            catch (Exception ex)
            {
                throw new InvalidOperationException($"맵 파일 로드 실패: {ex.Message}", ex);
            }
        }

        /// <summary>
        /// 마지막 맵 파일이 있는지 확인하고 사용자에게 로드할지 물어봄
        /// </summary>
        private void CheckAndLoadLastMapFile()
        {
            if (_config.AutoLoadLastMapFile && _config.HasValidLastMapFile())
            {
                string fileName = Path.GetFileName(_config.LastMapFilePath);
                var result = MessageBox.Show(
                    $"마지막으로 사용한 맵 파일을 찾았습니다:\n\n{fileName}\n\n이 파일을 열까요?",
                    "마지막 맵 파일 로드",
                    MessageBoxButtons.YesNo,
                    MessageBoxIcon.Question);

                if (result == DialogResult.Yes)
                {
                    try
                    {
                        LoadMapFile(_config.LastMapFilePath);
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show($"맵 파일 로드 중 오류가 발생했습니다:\n{ex.Message}",
                            "맵 파일 로드 오류", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                }
            }
        }

        private void UpdateNodeComboBoxes()
        {
            _startNodeCombo.Items.Clear();
            _targetNodeCombo.Items.Clear();

            if (_simulatorCanvas.Nodes != null)
            {
                foreach (var node in _simulatorCanvas.Nodes)
                {
                    if (node.IsActive)
                    {
                        // {rfid} - [{node}] {name} 형식으로 ComboBoxItem 생성                        
                        var displayText = $"{node.StationType.ToString().PadRight(7)} | {node.ID2}";
                        var item = new ComboBoxItem<MapNode>(node, displayText);

                        _startNodeCombo.Items.Add(item);
                        _targetNodeCombo.Items.Add(item);
                    }
                }
            }

            _startNodeCombo.DisplayMember = "DisplayText";
            _targetNodeCombo.DisplayMember = "DisplayText";
        }

        private void UpdateAGVComboBox()
        {
            _agvListCombo.Items.Clear();

            if (_agvList != null)
            {
                foreach (var agv in _agvList)
                {
                    _agvListCombo.Items.Add(agv);
                }
            }

            _agvListCombo.DisplayMember = "AgvId";

            if (_agvListCombo.Items.Count > 0)
            {
                _agvListCombo.SelectedIndex = 0;
            }
        }

        private void UpdateUI()
        {
            // 시뮬레이션 상태
            _simulationStatusLabel.Text = _simulationState.IsRunning ? "시뮬레이션: 실행 중" : "시뮬레이션: 정지";

            // AGV 수
            _agvCountLabel.Text = $"AGV 수: {_agvList?.Count ?? 0}";

            // 버튼 상태
            _startSimulationButton.Enabled = !_simulationState.IsRunning && _agvList?.Count > 0;
            _stopSimulationButton.Enabled = _simulationState.IsRunning;

            _removeAgvButton.Enabled = _agvListCombo.SelectedItem != null;
            // btPath1.Enabled = _startNodeCombo.SelectedItem != null &&
            //    _targetNodeCombo.SelectedItem != null;

            // RFID 위치 설정 관련
            var hasSelectedAGV = _agvListCombo.SelectedItem != null;
            var hasRfidNodes = _simulatorCanvas.Nodes != null && _simulatorCanvas.Nodes.Any(n => n.HasRfid());

            _setPositionButton.Enabled = hasSelectedAGV && hasRfidNodes;
            _rfidTextBox.Enabled = hasSelectedAGV && hasRfidNodes;

            // AGV 정보 패널 업데이트
            UpdateAGVInfoPanel();

            // 맵 다시열기 버튼
            var hasCurrentMap = !string.IsNullOrEmpty(_currentMapFilePath);
            reloadMapToolStripMenuItem.Enabled = hasCurrentMap;
            reloadMapToolStripButton.Enabled = hasCurrentMap;
        }

        /// <summary>
        /// AGV 정보 패널 업데이트
        /// </summary>
        private void UpdateAGVInfoPanel()
        {
            var selectedAGV = _agvListCombo.SelectedItem as VirtualAGV;

            if (selectedAGV == null)
            {
                _liftDirectionLabel.Text = "리프트 방향: -";
                _motorDirectionLabel.Text = "모터 방향: -";
                _agvInfoTitleLabel.Text = "AGV 상태 정보: (AGV 선택 안됨)";
                return;
            }

            // AGV 선택됨
            _agvInfoTitleLabel.Text = $"AGV 상태 정보: {selectedAGV.AgvId}";

            // 리프트 방향 계산
            var liftDirection = CalculateLiftDirection(selectedAGV);
            _liftDirectionLabel.Text = $"리프트 방향: {liftDirection}";

            // 모터 방향
            var motorDirection = GetMotorDirectionString(selectedAGV.CurrentDirection);
            _motorDirectionLabel.Text = $"모터 방향: {motorDirection}";
        }

        /// <summary>
        /// AGV의 리프트 방향 계산 (상세 출력 버전)
        /// </summary>
        private void CalculateLiftDirectionDetailed(VirtualAGV agv)
        {
            var currentPos = agv.CurrentPosition;
            var prevPos = agv.PrevPosition;
            var dockingDirection = agv.DockingDirection;

            Program.WriteLine($"    입력값: CurrentPos=({currentPos.X}, {currentPos.Y})");
            Program.WriteLine($"    입력값: prevPos={(!prevPos.HasValue ? "None" : $"({prevPos.Value.X}, {prevPos.Value.Y})")}");
            Program.WriteLine($"    입력값: DockingDirection={dockingDirection}");

            if (!prevPos.HasValue || prevPos.Value == currentPos)
            {
                Program.WriteLine($"    결과: 방향을 알 수 없음 (이전 위치값 없음 또는 같은 위치)");
                return;
            }

            // 이동 방향 계산 (이전 → 현재 = TargetPos → CurrentPos)
            var dx = currentPos.X - prevPos.Value.X;
            var dy = currentPos.Y - prevPos.Value.Y;
            Program.WriteLine($"    이동 벡터: dx={dx}, dy={dy}");

            if (Math.Abs(dx) < 1 && Math.Abs(dy) < 1)
            {
                Program.WriteLine($"    결과: 정지 상태 (이동거리 < 1픽셀)");
                return;
            }

            // 경로 예측 기반 LiftCalculator를 사용하여 리프트 방향 계산
            var liftInfo = AGVNavigationCore.Utils.LiftCalculator.CalculateLiftInfoWithPathPrediction(
                currentPos, prevPos.Value, agv.CurrentDirection, _simulatorCanvas.Nodes);

            // 이동 각도 계산 (표시용)
            var moveAngleRad = Math.Atan2(dy, dx);
            var moveAngleDeg = moveAngleRad * 180.0 / Math.PI;
            while (moveAngleDeg < 0) moveAngleDeg += 360;
            while (moveAngleDeg >= 360) moveAngleDeg -= 360;

            Program.WriteLine($"    이동 각도: {moveAngleDeg:F1}도 (라디안: {moveAngleRad:F3})");
            Program.WriteLine($"    모터 방향: {GetMotorDirectionString(liftInfo.MotorDirection)}");
            Program.WriteLine($"    리프트 각도: {liftInfo.AngleDegrees:F1}도 ({liftInfo.CalculationMethod})");

            // 도킹 방향 정보 추가
            string dockingInfo = dockingDirection == DockingDirection.Forward ? "전진도킹" : "후진도킹";

            Program.WriteLine($"    최종 결과: {liftInfo.DirectionString} ({liftInfo.AngleDegrees:F0}°) [{dockingInfo}]");
        }

        /// <summary>
        /// AGV의 리프트 방향 계산 (간단한 버전)
        /// </summary>
        private string CalculateLiftDirection(VirtualAGV agv)
        {
            var currentPos = agv.CurrentPosition;
            var targetPos = agv.PrevPosition;
            var dockingDirection = agv.DockingDirection;

            if (!targetPos.HasValue || targetPos.Value == currentPos)
            {
                // 방향을 알 수 없는 경우
                return "알 수 없음 (?)";
            }

            // 이동 방향 계산 (현재 → 타겟, 실제로는 이전 → 현재)
            var dx = currentPos.X - targetPos.Value.X;
            var dy = currentPos.Y - targetPos.Value.Y;

            if (Math.Abs(dx) < 1 && Math.Abs(dy) < 1)
            {
                return "정지 상태";
            }

            // 경로 예측 기반 LiftCalculator를 사용하여 리프트 방향 계산
            var liftInfo = AGVNavigationCore.Utils.LiftCalculator.CalculateLiftInfoWithPathPrediction(
                currentPos, targetPos.Value, agv.CurrentDirection, _simulatorCanvas.Nodes);

            // 도킹 방향 정보 추가
            string dockingInfo = dockingDirection == DockingDirection.Forward ? "전진도킹" : "후진도킹";

            return $"{liftInfo.DirectionString} ({liftInfo.AngleDegrees:F0}°) [{dockingInfo}]";
        }

        /// <summary>
        /// 모터 방향을 문자열로 변환
        /// </summary>
        private string GetMotorDirectionString(AgvDirection direction)
        {
            switch (direction)
            {
                case AgvDirection.Forward:
                    return "전진 (F)";
                case AgvDirection.Backward:
                    return "후진 (B)";
                case AgvDirection.Left:
                    return "좌회전 (L)";
                case AgvDirection.Right:
                    return "우회전 (R)";
                default:
                    return "알 수 없음";
            }
        }

        /// <summary>
        /// 노드 ID를 RFID 값으로 변환 (NodeResolver 사용)
        /// </summary>
        private ushort GetRfidByNodeId(string nodeId)
        {
            var node = _simulatorCanvas.Nodes?.FirstOrDefault(n => n.Id == nodeId);
            if (node == null) return 0;
            if (node.HasRfid()) return node.RfidId;
            else return 0;
        }

        /// <summary>
        /// 노드의 표시명 가져오기 (RFID 우선, 없으면 (NodeID) 형태)
        /// </summary>
        private string GetDisplayName(string nodeId)
        {
            var node = _simulatorCanvas.Nodes?.FirstOrDefault(n => n.Id == nodeId);
            if (node != null && node.HasRfid())
            {
                return node.RfidId.ToString("0000");
            }
            return $"({nodeId})";
        }

        /// <summary>
        /// 도킹 검증 결과 확인 및 UI 표시
        /// </summary>
        private void CheckAndDisplayDockingValidation(AGVPathResult agvResult)
        {
            if (agvResult?.DockingValidation == null)
                return;

            var validation = agvResult.DockingValidation;

            // 도킹 검증이 필요하지 않은 경우
            if (!validation.IsValidationRequired)
                return;

            // 도킹 검증 실패시 UI에 표시
            if (!validation.IsValid)
            {
                // 상태바에 경고 메시지 표시
                _statusLabel.Text = $"⚠️ 도킹 방향 오류: {validation.ValidationError}";
                _statusLabel.ForeColor = Color.Red;

                // 경로는 표시하되, 목적지 노드에 X 마크 표시 요청
                _simulatorCanvas.SetDockingError(validation.TargetNodeId, true);

                // 사용자에게 알림
                MessageBox.Show($"도킹 방향 검증 실패!\n\n" +
                              $"노드: {validation.TargetNodeId} ({validation.TargetNodeType})\n" +
                              $"필요 방향: {GetDirectionText(validation.RequiredDockingDirection)}\n" +
                              $"계산 방향: {GetDirectionText(validation.CalculatedFinalDirection)}\n\n" +
                              $"오류: {validation.ValidationError}",
                              "도킹 검증 실패", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }
            else
            {
                // 도킹 검증 성공시 정상 표시
                if (_statusLabel.ForeColor == Color.Red)
                    _statusLabel.ForeColor = Color.Black;
                _simulatorCanvas.SetDockingError(validation.TargetNodeId, false);
            }
        }

        /// <summary>
        /// AGV 방향을 한글 텍스트로 변환
        /// </summary>
        private string GetDirectionText(AgvDirection direction)
        {
            switch (direction)
            {
                case AgvDirection.Forward: return "전진";
                case AgvDirection.Backward: return "후진";
                case AgvDirection.Left: return "좌회전";
                case AgvDirection.Right: return "우회전";
                case AgvDirection.Stop: return "정지";
                default: return direction.ToString();
            }
        }



        /// <summary>
        /// 고급 경로 디버깅 정보 업데이트
        /// </summary>
        private void UpdateAdvancedPathDebugInfo(AGVPathResult advancedResult)
        {
            if (advancedResult == null || !advancedResult.Success)
            {
                _pathDebugLabel.Text = "고급 경로: 설정되지 않음";
                return;
            }

            // 노드 ID를 RFID로 변환한 경로 생성
            var pathWithRfid = advancedResult.GetSimplePath().Select(nodeId => GetRfidByNodeId(nodeId)).ToList();

            // 콘솔 디버그 정보 출력
            Program.WriteLine($"[ADVANCED DEBUG] 고급 경로 계산 완료:");
            Program.WriteLine($"  전체 경로 (RFID): [{string.Join(" → ", pathWithRfid)}]");
            Program.WriteLine($"  전체 경로 (NDID): [{string.Join(" → ", advancedResult.GetSimplePath())}]");
            Program.WriteLine($"  경로 노드 수: {advancedResult.DetailedPath.Count}");
            Program.WriteLine($"  방향 전환 필요: {advancedResult.RequiredDirectionChange}");

            if (advancedResult.RequiredDirectionChange && !string.IsNullOrEmpty(advancedResult.DirectionChangeNode))
            {
                Program.WriteLine($"  방향 전환 노드: {GetDisplayName(advancedResult.DirectionChangeNode)}");
            }

            Program.WriteLine($"  설명: {advancedResult.PlanDescription}");

            // 상세 경로 정보 출력
            for (int i = 0; i < advancedResult.DetailedPath.Count; i++)
            {
                var info = advancedResult.DetailedPath[i];
                var rfidId = GetRfidByNodeId(info.NodeId);

                var nextRfidId = "";
                if (info.NextNode != null && info.NextNode.HasRfid())
                {
                    nextRfidId = info.NextNode.RfidId.ToString("0000");
                }
                else if (info.NextNode != null)
                {
                    nextRfidId = info.NextNode.Id;
                }
                else
                {
                    nextRfidId = "-END-";
                }


                var flags = new List<string>();
                if (info.IsTurn) flags.Add("회전");
                if (info.IsDirectionChangePoint) flags.Add("방향전환");
                if (info.RequiresSpecialAction) flags.Add($"특수동작:{info.SpecialActionDescription}");
                if (info.MagnetDirection != MagnetDirection.Straight) flags.Add($"마그넷:{info.MagnetDirection}");

                var flagsStr = flags.Count > 0 ? $" [{string.Join(", ", flags)}]" : "";
                Program.WriteLine($"    {i}: {rfidId}({info.NodeId}) → {info.MotorDirection} → {nextRfidId}{flagsStr}");
            }

            // 경로 문자열 구성 (마그넷 방향 포함)
            var pathWithDetails = new List<string>();
            for (int i = 0; i < advancedResult.DetailedPath.Count; i++)
            {
                var motorInfo = advancedResult.DetailedPath[i];
                var rfidId = GetRfidByNodeId(motorInfo.NodeId);
                string motorSymbol = motorInfo.MotorDirection == AgvDirection.Forward ? "[F]" : "[B]";

                // 마그넷 방향 표시
                if (motorInfo.MagnetDirection != MagnetDirection.Straight)
                {
                    string magnetSymbol = motorInfo.MagnetDirection == MagnetDirection.Left ? "[L]" : "[R]";
                    motorSymbol += magnetSymbol;
                }
                else motorSymbol += "[S]";

                // 특수 동작 표시
                if (motorInfo.RequiresSpecialAction)
                    motorSymbol += "[🔄]";
                else if (motorInfo.IsDirectionChangePoint && motorInfo.CanRotate)
                    motorSymbol += "[↻]";

                if (motorInfo.IsTurn) motorSymbol = "[TURN]";

                pathWithDetails.Add($"{rfidId}{motorSymbol}");
            }

            string pathString = string.Join(" → ", pathWithDetails);

            // UI에 표시 (길이 제한)
            //if (pathString.Length > 100)
            //{
            //    pathString = pathString.Substring(0, 97) + "...";
            //}

            // 통계 정보
            var forwardCount = advancedResult.DetailedPath.Count(m => m.MotorDirection == AgvDirection.Forward);
            var backwardCount = advancedResult.DetailedPath.Count(m => m.MotorDirection == AgvDirection.Backward);
            var magnetDirectionChanges = advancedResult.DetailedPath.Count(m => m.MagnetDirection != MagnetDirection.Straight);

            string stats = $"전진: {forwardCount}, 후진: {backwardCount}";
            if (magnetDirectionChanges > 0)
                stats += $", 마그넷제어: {magnetDirectionChanges}";

            _pathDebugLabel.Text = $"고급경로: {pathString} (총 {advancedResult.DetailedPath.Count}개 노드, {advancedResult.TotalDistance:F1}px, {stats})";
        }



        private void OnReloadMap_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrEmpty(_currentMapFilePath))
            {
                MessageBox.Show("다시 로드할 맵 파일이 없습니다. 먼저 맵을 열어주세요.", "알림",
                              MessageBoxButtons.OK, MessageBoxIcon.Information);
                return;
            }

            if (!File.Exists(_currentMapFilePath))
            {
                MessageBox.Show($"맵 파일을 찾을 수 없습니다:\n{_currentMapFilePath}", "오류",
                              MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            try
            {
                LoadMapFile(_currentMapFilePath);
                _statusLabel.Text = $"맵 다시 로드 완료: {Path.GetFileName(_currentMapFilePath)}";
            }
            catch (Exception ex)
            {
                MessageBox.Show($"맵 파일을 다시 로드할 수 없습니다:\n{ex.Message}", "오류",
                              MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void OnLaunchMapEditor_Click(object sender, EventArgs e)
        {
            try
            {
                // MapEditor 실행 파일 경로 확인
                string mapEditorPath = _config.MapEditorExecutablePath;

                // 경로가 설정되지 않았거나 파일이 없는 경우 사용자에게 선택을 요청
                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;

                            // 설정에 저장
                            _config.MapEditorExecutablePath = mapEditorPath;
                            if (_config.AutoSave)
                            {
                                _config.Save();
                            }
                        }
                        else
                        {
                            return; // 사용자가 취소함
                        }
                    }
                }

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

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

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

        #endregion

        private void btAllReset_Click(object sender, EventArgs e)
        {
            // 시뮬레이션 정지
            if (_simulationState.IsRunning)
            {
                OnStopSimulation_Click(sender, e);
            }

            // AGV 초기화
            _agvList.Clear();
            _simulatorCanvas.AGVList = new List<IAGV>();

            // 경로 초기화
            _simulatorCanvas.CurrentPath = null;

            // UI 업데이트
            UpdateAGVComboBox();
            UpdateNodeComboBoxes();
            UpdateUI();

            _statusLabel.Text = "초기화 완료";
        }


        private async void toolStripButton1_Click(object sender, EventArgs e)
        {
            // 맵과 AGV 확인
            if (_simulatorCanvas.Nodes == null || _simulatorCanvas.Nodes.Count == 0)
            {
                MessageBox.Show("맵 데이터가 없습니다. 먼저 맵을 로드해주세요.", "알림",
                    MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            var selectedAGV = _agvListCombo.SelectedItem as VirtualAGV;
            if (selectedAGV == null)
            {
                MessageBox.Show("테스트할 AGV를 선택해주세요.", "알림",
                    MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            // 도킹 타겟 노드 찾기
            var dockingTargets = _simulatorCanvas.Nodes.Where(n => n.isDockingNode).ToList();

            if (dockingTargets.Count == 0)
            {
                MessageBox.Show("도킹 타겟(충전기 또는 장비)이 없습니다.", "알림",
                    MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            // 연결된 노드 쌍 찾기 (사전 계산)
            var nodePairs = GetConnectedNodePairs();
            var testCount = nodePairs.Count * dockingTargets.Count * 2; // 노드 쌍 x 도킹 타겟 x 방향(2)

            // 테스트 시작 확인
            var result = MessageBox.Show(
                $"경로 예측 테스트를 시작합니다.\n\n" +
                $"※ 실제 사용자 시나리오 재현 방식:\n" +
                $"   연결된 노드 쌍을 따라 AGV를 2번 이동시켜\n" +
                $"   방향을 확정한 후 각 도킹 타겟으로 경로 계산\n\n" +
                $"• 연결된 노드 쌍: {nodePairs.Count}개\n" +
                $"• 도킹 타겟: {dockingTargets.Count}개 (충전기/장비)\n" +
                $"• AGV 방향: 2가지 (정방향/역방향)\n" +
                $"• 총 테스트 케이스: {testCount}개\n\n" +
                $"※ UI가 실시간으로 업데이트됩니다.\n" +
                $"계속하시겠습니까?",
                "경로 예측 테스트 확인",
                MessageBoxButtons.YesNo,
                MessageBoxIcon.Question);

            if (result != DialogResult.Yes)
                return;

            // 로그 폼 생성 및 표시
            var logForm = new ProgressLogForm();
            logForm.Show(this);
            logForm.UpdateProgress(0, testCount);
            logForm.UpdateStatus("테스트 준비 중...");

            // 비동기 테스트 시작
            await Task.Run(() => RunPathPredictionTest(selectedAGV, dockingTargets, logForm));

            // 완료
            if (logForm.CancelRequested)
            {
                logForm.SetCancelled();
                MessageBox.Show("테스트가 취소되었습니다.", "알림",
                    MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            else
            {
                logForm.SetCompleted();
                MessageBox.Show("경로 예측 테스트가 완료되었습니다.", "완료",
                    MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
        }

        /// <summary>
        /// 노드의 표시 이름 가져오기 (RFID 우선, 없으면 (NodeId))
        /// </summary>
        private string GetNodeDisplayName(MapNode node)
        {
            if (node == null) return "-";
            var retval = "";
            if (node.HasRfid()) retval = node.RfidId.ToString("0000");
            else retval = $"({node.Id})";
            if (node.DockDirection == DockingDirection.Forward)
                retval += "(F)";
            else if (node.DockDirection == DockingDirection.Backward)
                retval += "(B)";
            return retval;
        }

        /// <summary>
        /// 방향 콤보박스 선택 (테스트용)
        /// </summary>
        private void SetDirectionComboBox(AgvDirection direction)
        {
            for (int i = 0; i < _directionCombo.Items.Count; i++)
            {
                var item = _directionCombo.Items[i] as DirectionItem;
                if (item != null && item.Direction == direction)
                {
                    _directionCombo.SelectedIndex = i;
                    return;
                }
            }
        }

        /// <summary>
        /// 목표 노드 콤보박스 선택 (테스트용)
        /// </summary>
        private void SetTargetNodeComboBox(string nodeId)
        {
            for (int i = 0; i < _targetNodeCombo.Items.Count; i++)
            {
                var item = _targetNodeCombo.Items[i] as ComboBoxItem<MapNode>;
                if (item?.Value?.Id == nodeId)
                {
                    _targetNodeCombo.SelectedIndex = i;
                    return;
                }
            }
        }

        /// <summary>
        /// UI 상태로부터 테스트 결과 생성 (테스트용)
        /// </summary>
        private PathTestLogItem CreateTestResultFromUI(MapNode prevNode, MapNode targetNode,
            string directionName, AGVPathResult calcResult)
        {
            var currentNode = _simulatorCanvas.Nodes.FirstOrDefault(n => n.Id ==
                (_agvListCombo.SelectedItem as VirtualAGV)?.CurrentNodeId);

            var logItem = new PathTestLogItem
            {
                PreviousPosition = GetNodeDisplayName(prevNode),
                MotorDirection = directionName,
                CurrentPosition = GetNodeDisplayName(currentNode),
                TargetPosition = GetNodeDisplayName(targetNode),
                DockingPosition = (targetNode.StationType == Station.Charger) ? "충전기" : "장비"
            };

            if (calcResult.Success)
            {
                // 경로 계산 성공 - 현재 화면에 표시된 경로 정보 사용
                var currentPath = calcResult;// _simulatorCanvas.CurrentPath;
                if (currentPath != null && currentPath.Success)
                {
                    // 도킹 검증
                    var dockingValidation = DockingValidator.ValidateDockingDirection(currentPath, _simulatorCanvas.Nodes);

                    if (dockingValidation.IsValid)
                    {
                        logItem.Success = true;
                        logItem.Message = "성공";
                        logItem.DetailedPath = currentPath.GetDetailedPathInfo(true);
                    }
                    else
                    {
                        logItem.Success = false;
                        logItem.Message = $"도킹 검증 실패: {dockingValidation.ValidationError}";
                        logItem.DetailedPath = currentPath.GetDetailedPathInfo(true);
                    }
                }
                else
                {
                    logItem.Success = true;
                    logItem.Message = "경로 계산 성공";
                    logItem.DetailedPath = "-";
                }
            }
            else
            {
                // 경로 계산 실패
                logItem.Success = false;
                logItem.Message = calcResult.Message;
                logItem.DetailedPath = "-";
            }

            return logItem;
        }

        /// <summary>
        /// 연결된 노드 쌍 찾기 (A→B 형태)
        /// </summary>
        private List<(MapNode nodeA, MapNode nodeB)> GetConnectedNodePairs()
        {
            var pairs = new List<(MapNode, MapNode)>();
            var processedPairs = new HashSet<string>();

            foreach (var nodeA in _simulatorCanvas.Nodes)
            {
                if (nodeA.ConnectedMapNodes == null || nodeA.ConnectedMapNodes.Count == 0)
                    continue;

                // 연결된 노드 객체 순회
                foreach (var nodeB in nodeA.ConnectedMapNodes)
                {
                    if (nodeB == null)
                        continue;

                    // 중복 방지 (A→B와 B→A를 같은 것으로 간주)
                    var pairKey1 = $"{nodeA.Id}→{nodeB.Id}";
                    var pairKey2 = $"{nodeB.Id}→{nodeA.Id}";

                    if (nodeA.HasRfid() && nodeB.HasRfid() && !processedPairs.Contains(pairKey1) && !processedPairs.Contains(pairKey2))
                    {
                        pairs.Add((nodeA, nodeB));
                        processedPairs.Add(pairKey1);
                        processedPairs.Add(pairKey2);
                    }
                }
            }

            return pairs;
        }

        /// <summary>
        /// 경로 예측 테스트 실행 (실제 사용자 시나리오 재현)
        /// </summary>
        private void RunPathPredictionTest(VirtualAGV agv, List<MapNode> dockingTargets, ProgressLogForm logForm)
        {
            var directions = new[]
            {
                (AgvDirection.Forward, "정방향"),
                (AgvDirection.Backward, "역방향")
            };

            // 연결된 노드 쌍 찾기
            var nodePairs = GetConnectedNodePairs();

            int totalTests = nodePairs.Count * dockingTargets.Count * 2;
            int currentTest = 0;
            int successCount = 0;
            int failCount = 0;

            logForm.UpdateStatus("경로 예측 테스트 진행 중...");
            logForm.AppendLog($"테스트 시작: 총 {totalTests}개 케이스");
            logForm.AppendLog($"연결된 노드 쌍: {nodePairs.Count}개");
            logForm.AppendLog($"도킹 타겟: {dockingTargets.Count}개");
            logForm.AppendLog("---");

            // 각 연결된 노드 쌍에 대해 테스트
            foreach (var (direction, directionName) in directions)
            {
                foreach (var (nodeA, nodeB) in nodePairs)
                {
                    // 취소 확인
                    if (logForm.CancelRequested)
                    {
                        logForm.AppendLog($"테스트 취소됨 - {currentTest}/{totalTests} 완료");
                        return;
                    }

                    // === 실제 사용자 워크플로우 재현 ===
                    // 1단계: AGV를 nodeA로 이동 (실제 UI 조작)
                    this.Invoke((MethodInvoker)delegate
                    {
                        // RFID 텍스트박스에 값 입력
                        _rfidTextBox.Text = nodeA.RfidId.ToString();

                        // 방향 콤보박스 선택
                        SetDirectionComboBox(direction);

                        // 위치설정 버튼 클릭 (실제 사용자 동작)
                        SetAGVPositionByRfid();

                        Application.DoEvents(); // UI 업데이트
                    });
                    Thread.Sleep(100); // 시각적 효과

                    // 2단계: AGV를 nodeB로 이동 (방향 확정됨)
                    this.Invoke((MethodInvoker)delegate
                    {
                        // RFID 텍스트박스에 값 입력
                        _rfidTextBox.Text = nodeB.RfidId.ToString();

                        // 방향 콤보박스 선택
                        SetDirectionComboBox(direction);

                        // 위치설정 버튼 클릭 (실제 사용자 동작)
                        SetAGVPositionByRfid();

                        Application.DoEvents(); // UI 업데이트
                    });
                    Thread.Sleep(100); // 시각적 효과

                    // 3단계: nodeB 위치에서 모든 도킹 타겟으로 경로 예측
                    foreach (var dockingTarget in dockingTargets)
                    {
                        // 취소 확인
                        if (logForm.CancelRequested)
                        {
                            logForm.AppendLog($"테스트 취소됨 - {currentTest}/{totalTests} 완료");
                            return;
                        }

                        currentTest++;

                        // UI 스레드에서 경로 계산 및 테스트 (실제 UI 사용)
                        PathTestLogItem testResult = null;
                        this.Invoke((MethodInvoker)delegate
                        {
                            // 진행상황 업데이트
                            logForm.UpdateProgress(currentTest, totalTests);
                            logForm.UpdateStatus($"테스트 진행 중... ({currentTest}/{totalTests}) [{GetNodeDisplayName(nodeA)}→{GetNodeDisplayName(nodeB)}→{GetNodeDisplayName(dockingTarget)}]");
                            prb1.Value = (int)((double)currentTest / totalTests * 100);

                            // 목표 노드 콤보박스 선택
                            SetTargetNodeComboBox(dockingTarget.Id);

                            // 경로 계산 버튼 클릭 (실제 사용자 동작)
                            var startNode = (_startNodeCombo.SelectedItem as ComboBoxItem<MapNode>)?.Value;
                            var targetNode = (_targetNodeCombo.SelectedItem as ComboBoxItem<MapNode>)?.Value;
                            var selectedAGV = _agvListCombo.SelectedItem as VirtualAGV;
                            var calcResult = CalcPath(startNode, targetNode, this._simulatorCanvas.Nodes, selectedAGV.PrevNode, selectedAGV.PrevDirection);

                            //// 테스트 결과 생성
                            testResult = CreateTestResultFromUI(nodeA, dockingTarget, directionName, calcResult);

                            //// 로그 추가
                            logForm.AddLogItem(testResult);

                            //// 실패한 경우에만 경로를 화면에 표시 (시각적 확인)
                            if (!testResult.Success && _simulatorCanvas.CurrentPath != null)
                            {
                                _simulatorCanvas.Invalidate();
                            }

                            Application.DoEvents();
                        });

                        if (testResult.Success)
                            successCount++;
                        else
                            failCount++;

                        // UI 반응성을 위한 짧은 대기
                        Thread.Sleep(50);
                    }
                }
            }

            // 최종 결과
            logForm.AppendLog($"");
            logForm.AppendLog($"=== 테스트 완료 ===");
            logForm.AppendLog($"총 테스트: {totalTests}");
            logForm.AppendLog($"성공: {successCount}");
            logForm.AppendLog($"실패: {failCount}");
            logForm.AppendLog($"성공률: {(double)successCount / totalTests * 100:F1}%");
        }

        private void btPredict_Click(object sender, EventArgs e)
        {
            // 다음 행동 예측
            if (_agvList == null || _agvList.Count == 0)
            {
                MessageBox.Show("AGV가 없습니다.", "예측 오류", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            // 첫 번째 AGV의 다음 행동 예측
            var agv = _agvList[0];
            var command = agv.Predict();

            //this.lbPredict.Text = $"MOT:{command.Motor},MAG:{command.Magnet},SPD:{command.Speed}:{command.Reason}";

            // 예측 결과 표시
            var message = $"[다음 행동 예측]\n\n" +
                         $"모터: {command.Motor}\n" +
                         $"마그넷: {command.Magnet}\n" +
                         $"속도: {command.Speed}\n" +
                         $"이유: {command.Message}\n\n" +
                         $"---\n" +
                         $"현재 상태: {agv.CurrentState}\n" +
                         $"현재 방향: {agv.CurrentDirection}\n" +
                         $"위치 확정: {agv.IsPositionConfirmed} (RFID {agv.DetectedRfidCount}개)\n" +
                         $"현재 노드: {agv.CurrentNodeId ?? "없음"}";

            Console.WriteLine(message);
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            if (_agvList == null || _agvList.Count == 0)
            {
                // MessageBox.Show("AGV가 없습니다.", "예측 오류", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            // 첫 번째 AGV의 다음 행동 예측
            var agv = _agvList[0];
            try
            {
                var command = agv.Predict();
                this.lbPredict.Text = $"Motor:{command.Motor},Magnet:{command.Magnet},Speed:{command.Speed} : {command.Message}";
            }catch  ( Exception ex)
            {
                lbPredict.Text = "예측오류" + ex.Message;
            }
            
        }


        private void btMakeMap_Click(object sender, EventArgs e)
        {
            if (!_isMapScanMode)
            {
                // 스캔 모드 시작
                var result = MessageBox.Show(
                    "맵 스캔 모드를 시작합니다.\n\n" +
                    "RFID를 입력하면 자동으로 맵 노드가 생성되고\n" +
                    "이전 노드와 연결됩니다.\n\n" +
                    "기존 맵 데이터를 삭제하고 시작하시겠습니까?\n\n" +
                    "예: 새 맵 시작\n" +
                    "아니오: 기존 맵에 추가",
                    "맵 스캔 모드",
                    MessageBoxButtons.YesNoCancel,
                    MessageBoxIcon.Question);

                if (result == DialogResult.Cancel)
                    return;

                if (result == DialogResult.Yes)
                {
                    // 기존 맵 데이터 삭제
                    _simulatorCanvas.Nodes?.Clear();
                    _simulatorCanvas.Nodes = new List<MapNode>();
                    _simulatorCanvas.Nodes = _simulatorCanvas.Nodes;
                    _currentMapFilePath = string.Empty;
                    UpdateNodeComboBoxes();
                    _statusLabel.Text = "맵 초기화 완료 - 스캔 모드 시작";
                }

                // 스캔 모드 활성화
                _isMapScanMode = true;
                _lastNodeAddTime = DateTime.Now;
                _lastScannedNode = null;
                _scanNodeCounter = 1;
                _lastScanDirection = AgvDirection.Forward;  // 기본 방향은 전진
                btMakeMap.Text = "스캔 중지";
                btMakeMap.BackColor = Color.LightCoral;
                _statusLabel.Text = "맵 스캔 모드: RFID를 입력하여 노드를 생성하세요";

                Program.WriteLine("[맵 스캔] 스캔 모드 시작");
            }
            else
            {
                // 스캔 모드 종료
                _isMapScanMode = false;
                btMakeMap.Text = "맵 생성";
                btMakeMap.BackColor = SystemColors.Control;
                _statusLabel.Text = $"맵 스캔 완료 - {_simulatorCanvas.Nodes?.Count ?? 0}개 노드 생성됨";

                Program.WriteLine($"[맵 스캔] 스캔 모드 종료 - 총 {_simulatorCanvas.Nodes?.Count ?? 0}개 노드");

                // 맵 저장 권장
                if (_simulatorCanvas.Nodes != null && _simulatorCanvas.Nodes.Count > 0)
                {
                    var saveResult = MessageBox.Show(
                        $"맵 스캔이 완료되었습니다.\n\n" +
                        $"생성된 노드: {_simulatorCanvas.Nodes.Count}개\n\n" +
                        "맵을 저장하시겠습니까?",
                        "맵 저장",
                        MessageBoxButtons.YesNo,
                        MessageBoxIcon.Question);

                    if (saveResult == DialogResult.Yes)
                    {
                        btMapSaveAs_Click(sender, e);
                    }
                }
            }
        }



        /// <summary>
        /// 맵 데이터를 파일에 저장 (MapLoader 공통 저장 로직 사용)
        /// </summary>
        private void SaveMapToFile(string filePath)
        {
            try
            {
                // MapLoader의 표준 저장 메서드 사용 (AGVMapEditor와 동일한 형식)
                bool success = MapLoader.SaveMapToFile(filePath, _simulatorCanvas.Nodes);

                if (success)
                {
                    Program.WriteLine($"[맵 저장] 파일 저장 완료: {filePath} ({_simulatorCanvas.Nodes.Count}개 노드)");
                }
                else
                {
                    throw new InvalidOperationException("맵 저장에 실패했습니다.");
                }
            }
            catch (Exception ex)
            {
                Program.WriteLine($"[맵 저장 오류] {ex.Message}");
                throw;
            }
        }

        private void btMapSaveAs_Click(object sender, EventArgs e)
        {
            // 맵 데이터 확인
            if (_simulatorCanvas.Nodes == null || _simulatorCanvas.Nodes.Count == 0)
            {
                MessageBox.Show("저장할 맵 데이터가 없습니다.", "알림",
                              MessageBoxButtons.OK, MessageBoxIcon.Information);
                return;
            }

            using (var saveDialog = new SaveFileDialog())
            {
                saveDialog.Filter = "AGV Map Files (*.json)|*.json|모든 파일 (*.*)|*.*";
                saveDialog.Title = "맵 파일 저장";
                saveDialog.DefaultExt = "json";

                // 현재 파일이 있으면 기본 파일명으로 설정
                if (!string.IsNullOrEmpty(_currentMapFilePath))
                {
                    saveDialog.FileName = Path.GetFileName(_currentMapFilePath);
                    saveDialog.InitialDirectory = Path.GetDirectoryName(_currentMapFilePath);
                }
                else
                {
                    // 기본 파일명: 날짜_시간 형식
                    saveDialog.FileName = $"ScanMap_{DateTime.Now:yyyyMMdd_HHmmss}.json";
                }

                if (saveDialog.ShowDialog() == DialogResult.OK)
                {
                    try
                    {
                        SaveMapToFile(saveDialog.FileName);
                        _currentMapFilePath = saveDialog.FileName;

                        // 설정에 마지막 맵 파일 경로 저장
                        _config.LastMapFilePath = _currentMapFilePath;
                        if (_config.AutoSave)
                        {
                            _config.Save();
                        }

                        _statusLabel.Text = $"맵 저장 완료: {Path.GetFileName(_currentMapFilePath)}";
                        MessageBox.Show($"맵이 저장되었습니다.\n\n파일: {_currentMapFilePath}", "저장 완료",
                                      MessageBoxButtons.OK, MessageBoxIcon.Information);
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show($"맵 저장 중 오류 발생:\n{ex.Message}", "저장 오류",
                                      MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                }
            }
        }

        private void 맵저장SToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // 현재 맵 파일 경로가 있는 경우 해당 파일에 저장
            if (string.IsNullOrEmpty(_currentMapFilePath))
            {
                // 경로가 없으면 다른 이름으로 저장 다이얼로그 표시
                btMapSaveAs_Click(sender, e);
                return;
            }

            try
            {
                SaveMapToFile(_currentMapFilePath);
                _statusLabel.Text = $"맵 저장 완료: {Path.GetFileName(_currentMapFilePath)}";
                MessageBox.Show($"맵이 저장되었습니다.\n\n파일: {_currentMapFilePath}", "저장 완료",
                              MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            catch (Exception ex)
            {
                MessageBox.Show($"맵 저장 중 오류 발생:\n{ex.Message}", "저장 오류",
                              MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        #region Emulator Logic

        private void InitializeEmulatorUI()
        {
            // 에뮬레이터 제어 패널 생성
            var emulatorPanel = new GroupBox();
            emulatorPanel.Text = "AGV Emulator (RS232)";
            emulatorPanel.Height = 60;
            emulatorPanel.Dock = DockStyle.Top;

            var layout = new FlowLayoutPanel();
            layout.Dock = DockStyle.Fill;
            layout.Padding = new Padding(5);
            layout.AutoSize = true;

            // 포트 선택 콤보박스
            _portCombo = new ComboBox();
            _portCombo.Width = 100;
            _portCombo.DropDownStyle = ComboBoxStyle.DropDownList;
            _portCombo.DropDown += (s, e) =>
            {
                _portCombo.Items.Clear();
                _portCombo.Items.AddRange(SerialPort.GetPortNames());
            };
            _portCombo.Items.AddRange(SerialPort.GetPortNames());
            if (_portCombo.Items.Count > 0) _portCombo.SelectedIndex = 0;

            // 연결 버튼
            _connectButton = new Button();
            _connectButton.Text = "Connect";
            _connectButton.Click += OnConnectEmulator_Click;

            layout.Controls.Add(new Label { Text = "Port:", AutoSize = true, Margin = new Padding(3, 8, 3, 3) });
            layout.Controls.Add(_portCombo);
            layout.Controls.Add(_connectButton);

            emulatorPanel.Controls.Add(layout);

            // 폼의 최상단에 추가
            this.Controls.Add(emulatorPanel);
            emulatorPanel.BringToFront();
        }

        private void OnConnectEmulator_Click(object sender, EventArgs e)
        {
            if (_isEmulatorConnected)
            {
                DisconnectEmulator();
            }
            else
            {
                if (_portCombo.SelectedItem == null)
                {
                    MessageBox.Show("포트를 선택해주세요.");
                    return;
                }
                ConnectEmulator(_portCombo.SelectedItem.ToString());
            }
        }

        private void ConnectEmulator(string portName)
        {
            try
            {
                _emulatorPort = new SerialPort(portName, 115200, Parity.None, 8, StopBits.One);
                _emulatorPort.DataReceived += OnEmulatorDataReceived;
                _emulatorPort.Open();
                _isEmulatorConnected = true;
                _connectButton.Text = "Disconnect";
                _portCombo.Enabled = false;
                MessageBox.Show($"에뮬레이터 시작: {portName}");
            }
            catch (Exception ex)
            {
                MessageBox.Show($"연결 실패: {ex.Message}");
            }
        }

        private void DisconnectEmulator()
        {
            try
            {
                if (_emulatorPort != null)
                {
                    // 이벤트 핸들러 해제 (중복 호출 방지)
                    _emulatorPort.DataReceived -= OnEmulatorDataReceived;

                    if (_emulatorPort.IsOpen)
                        _emulatorPort.Close();
                }
                _isEmulatorConnected = false;
                _connectButton.Text = "Connect";
                _portCombo.Enabled = true;
            }
            catch (Exception ex)
            {
                MessageBox.Show($"해제 실패: {ex.Message}");
            }
        }

        private StringBuilder _recvBuffer = new StringBuilder();

        private void OnEmulatorDataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            try
            {
                if (_emulatorPort == null || !_emulatorPort.IsOpen) return;

                string data = _emulatorPort.ReadExisting();
                _recvBuffer.Append(data);

                string buffer = _recvBuffer.ToString();
                int stxIndex = buffer.IndexOf((char)0x02);

                while (stxIndex >= 0)
                {
                    int etxIndex = buffer.IndexOf((char)0x03, stxIndex);
                    if (etxIndex > stxIndex)
                    {
                        string packet = buffer.Substring(stxIndex + 1, etxIndex - stxIndex - 1);
                        ProcessEmulatorPacket(packet);

                        // 처리된 패킷 제거
                        buffer = buffer.Substring(etxIndex + 1);
                        stxIndex = buffer.IndexOf((char)0x02);
                    }
                    else
                    {
                        break; // ETX 아직 안옴
                    }
                }

                _recvBuffer.Clear();
                _recvBuffer.Append(buffer);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Emulator Recv Error: {ex.Message}");
                // 수신 중 오류 발생 시 연결 해제 처리 (UI 스레드에서 실행)
                this.Invoke(new Action(() =>
                {
                    if (_isEmulatorConnected) DisconnectEmulator();
                }));
            }
        }

        #region Emulator Helpers
        private bool GetBit(ref UInt16 _value, int idx)
        {
            var offset = (UInt16)(1 << idx);
            return (_value & offset) != 0;
        }

        private bool SetBit(ref UInt16 _value, int idx, bool value)
        {
            var oldvalue = GetBit(ref _value, idx);
            if (value)
            {
                var offset = (UInt16)(1 << idx);
                _value = (UInt16)(_value | offset);
            }
            else
            {
                var offset = (UInt16)(~(1 << idx));
                _value = (UInt16)(_value & offset);
            }
            return oldvalue != value;
        }

        private bool SetBit(ref byte _value, int idx, bool value)
        {
            var offset = (byte)(1 << idx);
            if (value) _value |= offset;
            else _value &= (byte)~offset;
            return true;
        }

        private void SetAGV(esystemflag0 flag, bool value)
        {
            SetBit(ref _emu_system0, (int)flag, value);
        }
        private void SetAGV(esystemflag1 flag, bool value)
        {
            SetBit(ref _emu_system1, (int)flag, value);
        }
        private void SetAGV(eerror flag, bool value)
        {
            SetBit(ref _emu_error, (int)flag, value);
        }
        private void SetAGV(esignal flag, bool value)
        {
            SetBit(ref _emu_signal, (int)flag, value);
        }
        private void SetSTS(estsvaluetype target, char value)
        {
            switch (target)
            {
                case estsvaluetype.sensor: _emu_sts_sensor = value; break;
                case estsvaluetype.direction: _emu_sts_dir = value; break;
                case estsvaluetype.speed: _emu_sts_speed = value; break;
                case estsvaluetype.bunki: _emu_sts_bunki = value; break;
            }
        }
        #endregion

        private void ProcessEmulatorPacket(string packet)
        {
            // Packet: CMD(3) + DATA(...) + Checksum(2)
            // But here packet is substring between STX and ETX.
            // Example: STS...Checksum

            if (packet.Length < 3) return;
            string cmd = packet.Substring(0, 3);
            string data = "";
            if (packet.Length > 5) // CMD + Checksum(2)
            {
                data = packet.Substring(3, packet.Length - 5);
            }

            // AGV 제어 (첫 번째 AGV 대상)
            var agv = _agvList.FirstOrDefault();

            this.Invoke(new Action(() =>
            {
                switch (cmd)
                {
                    case "CRN": // 기동명령
                        if (data.Length > 0) SetSTS(estsvaluetype.direction, data[0]);
                        SetAGV(esystemflag1.agv_stop, false);
                        SetAGV(esystemflag1.agv_run, true);
                        if (agv != null) agv.Resume();
                        break;

                    case "CST": // 중지명령
                        if (data.StartsWith("M"))
                        {
                            // Mark Stop
                            // TODO: Implement Mark Stop logic in VirtualAGV if needed
                            // For now, just log it
                            Console.WriteLine("Mark Stop Command Received");
                        }
                        else
                        {
                            SetAGV(esystemflag1.agv_run, false);
                            SetAGV(esystemflag1.agv_stop, true);
                            if (agv != null) agv.Pause();
                        }
                        break;

                    case "CBR": // 분기명령
                        // FSL1
                        if (data.Length >= 4)
                        {
                            SetSTS(estsvaluetype.direction, data[0]);
                            SetSTS(estsvaluetype.bunki, data[1]);
                            SetSTS(estsvaluetype.speed, data[2]);
                            SetSTS(estsvaluetype.sensor, data[3]);
                        }
                        break;

                    case "CRT": // 수동제어
                        if (data.Length >= 4)
                        {
                            _emu_sts_dir = data[0];
                            _emu_sts_bunki = data[1];
                            _emu_sts_speed = data[2];
                            _emu_sts_sensor = data[3];
                            SetAGV(esystemflag1.agv_stop, false);
                            SetAGV(esystemflag1.agv_run, true);
                            if (agv != null) agv.Resume();
                        }
                        break;

                    case "SFR": // Reset
                        SetAGV(eerror.Emergency, false);
                        SetAGV(eerror.line_out_error, false);
                        SetAGV(eerror.Overcurrent, false);
                        SetAGV(esystemflag1.agv_run, false);
                        SetAGV(esystemflag1.agv_stop, true);
                        if (agv != null) agv.Pause();
                        break;

                    case "CBT": // 충전
                        SetAGV(esystemflag1.Battery_charging, true);
                        if (data.Length >= 5)
                        {
                            var cmdChar = data[4];
                            if (cmdChar == 'I') SetAGV(esystemflag1.Battery_charging, true);
                            else SetAGV(esystemflag1.Battery_charging, false);
                        }
                        break;

                    case "ACK":
                        // Log ACK
                        break;

                    default:
                        // Send ACK for other commands
                        SendCmd("ACK", cmd);
                        break;
                }
            }));
        }

        private void SendCmd(string cmd, string value)
        {
            if (_emulatorPort == null || !_emulatorPort.IsOpen) return;

            var barr = new List<byte>();
            barr.Add(0x02);
            barr.AddRange(System.Text.Encoding.Default.GetBytes(cmd));
            barr.AddRange(System.Text.Encoding.Default.GetBytes(value));
            barr.Add((byte)'*');
            barr.Add((byte)'*');
            barr.Add(0x03);

            try
            {
                _emulatorPort.Write(barr.ToArray(), 0, barr.Count);
            }
            catch { }
        }

        public void SendTag(ushort tagno)
        {
            if (_emulatorPort == null || !_emulatorPort.IsOpen) return;

            var tagnostr = tagno.ToString("000000");

            var barr = new List<byte>();
            barr.Add(0x02);
            barr.Add((byte)'T');
            barr.Add((byte)'A');
            barr.Add((byte)'G');
            barr.AddRange(System.Text.Encoding.Default.GetBytes(tagnostr));
            barr.Add((byte)'*');
            barr.Add((byte)'*');
            barr.Add(0x03);

            try
            {
                _emulatorPort.Write(barr.ToArray(), 0, barr.Count);
            }
            catch { }
        }

        private void SendEmulatorStatus()
        {
            if (_emulatorPort == null || !_emulatorPort.IsOpen)
            {
                if (_isEmulatorConnected) DisconnectEmulator();
                return;
            }

            var agv = _agvList.FirstOrDefault();

            // Sync state from VirtualAGV
            if (agv != null)
            {
                // Update Battery
                // Update Position/Tag?
            }

            // STS Packet Construction
            // STS(3) + Volt(3) + Sys0(4) + Sys1(4) + Err(4) + Spd(1) + Bunki(1) + Dir(1) + Sensor(1) + Signal(2) + Checksum(2)

            // Default buffer
            var sample = "02 53 54 53 32 35 38 46 46 46 46 34 30 30 30 30 30 30 30 4C 53 46 30 30 30 30 30 30 33 41 03";
            var barr = sample.Split(' ').Select(t => Convert.ToByte(t, 16)).ToArray();

            // Volt (255 for now)
            var voltstr = "255";
            var bufarr = System.Text.Encoding.Default.GetBytes(voltstr);
            Array.Copy(bufarr, 0, barr, 4, bufarr.Length);

            // System0
            bufarr = System.Text.Encoding.Default.GetBytes(_emu_system0.ToString("X2").PadLeft(4, '0'));
            Array.Copy(bufarr, 0, barr, 7, bufarr.Length);

            // System1
            bufarr = System.Text.Encoding.Default.GetBytes(_emu_system1.ToString("X2").PadLeft(4, '0'));
            Array.Copy(bufarr, 0, barr, 11, bufarr.Length);

            // Error
            bufarr = System.Text.Encoding.Default.GetBytes(_emu_error.ToString("X2").PadLeft(4, '0'));
            Array.Copy(bufarr, 0, barr, 15, bufarr.Length);

            // Status Chars
            barr[19] = (byte)_emu_sts_speed;
            barr[20] = (byte)_emu_sts_bunki;
            barr[21] = (byte)_emu_sts_dir;
            barr[22] = (byte)_emu_sts_sensor;

            // Signal
            bufarr = System.Text.Encoding.Default.GetBytes(_emu_signal.ToString("X2").PadLeft(2, '0'));
            Array.Copy(bufarr, 0, barr, 23, bufarr.Length);

            // Checksum (**)
            barr[barr.Length - 3] = (byte)'*';
            barr[barr.Length - 2] = (byte)'*';

            try
            {
                _emulatorPort.Write(barr, 0, barr.Length);
            }
            catch
            {
                if (_isEmulatorConnected) DisconnectEmulator();
            }
        }

        private string CalculateChecksum(string data)
        {
            int sum = 0;
            foreach (char c in data) sum += c;

            // 16진수 변환 후 뒤 2자리
            string hex = sum.ToString("X");
            if (hex.Length >= 2) return hex.Substring(hex.Length - 2);
            return hex.PadLeft(2, '0');
        }

        //(bool result, string message) CalcPath()
        //{
        //    // 시작 RFID가 없으면 AGV 현재 위치로 설정
        //    if (_startNodeCombo.SelectedItem == null || _startNodeCombo.Text == "선택하세요")
        //    {
        //        SetStartNodeFromAGVPosition();
        //    }

        //    if (_startNodeCombo.SelectedItem == null || _targetNodeCombo.SelectedItem == null)
        //    {
        //        return (false, "시작 RFID와 목표 RFID를 선택해주세요.");
        //    }

        //    var startItem = _startNodeCombo.SelectedItem as ComboBoxItem<MapNode>;
        //    var targetItem = _targetNodeCombo.SelectedItem as ComboBoxItem<MapNode>;
        //    var startNode = startItem?.Value;
        //    var targetNode = targetItem?.Value;

        //    if (startNode == null || targetNode == null)
        //    {
        //        return (false, "선택한 노드 정보가 올바르지 않습니다.");
        //    }


        //    if (_advancedPathfinder == null)
        //    {
        //        _advancedPathfinder = new AGVPathfinder(_simulatorCanvas.Nodes);
        //    }

        //    // 현재 AGV 방향 가져오기
        //    var selectedAGV = _agvListCombo.SelectedItem as VirtualAGV;
        //    if (selectedAGV == null)
        //    {
        //        return (false, "Virtual AGV 가 없습니다");
        //    }
        //    var currentDirection = selectedAGV.CurrentDirection;

        //    // AGV의 이전 위치에서 가장 가까운 노드 찾기
        //    var prevNode = selectedAGV.PrevNode;
        //    var prevDir = selectedAGV.PrevDirection;

        //    // 고급 경로 계획 사용 (노드 객체 직접 전달)
        //    var advancedResult = _advancedPathfinder.FindPath(startNode, targetNode, prevNode, prevDir, currentDirection);

        //    _simulatorCanvas.FitToNodes();
        //    if (advancedResult.Success)
        //    {
        //        // 도킹 검증이 없는 경우 추가 검증 수행
        //        if (advancedResult.DockingValidation == null || !advancedResult.DockingValidation.IsValidationRequired)
        //        {
        //            advancedResult.DockingValidation = DockingValidator.ValidateDockingDirection(advancedResult, _simulatorCanvas.Nodes);
        //        }

        //        //마지막대상이 버퍼라면 시퀀스처리를 해야한다
        //        if (targetNode.StationType == StationType.Buffer)
        //        {
        //            var lastDetailPath = advancedResult.DetailedPath.Last();
        //            if (lastDetailPath.NodeId == targetNode.Id)  //마지막노드 재확인
        //            {
        //                //버퍼에 도킹할때에는 마지막 노드에서 멈추고 시퀀스를 적용해야한다
        //                advancedResult.DetailedPath = advancedResult.DetailedPath.Take(advancedResult.DetailedPath.Count - 1).ToList();
        //                Console.WriteLine("최종위치가 버퍼이므로 마지막 RFID에서 멈추도록 합니다");
        //            }
        //        }


        //        _simulatorCanvas.CurrentPath = advancedResult;
        //        _pathLengthLabel.Text = $"경로 길이: {advancedResult.TotalDistance:F1}";
        //        _statusLabel.Text = $"경로 계산 완료 ({advancedResult.CalculationTimeMs}ms)";

        //        // 🔥 VirtualAGV에도 경로 설정 (Predict()가 동작하려면 필요)
        //        selectedAGV.SetPath(advancedResult);

        //        // 도킹 검증 결과 확인 및 UI 표시
        //        CheckAndDisplayDockingValidation(advancedResult);

        //        // 고급 경로 디버깅 정보 표시
        //        UpdateAdvancedPathDebugInfo(advancedResult);
        //        return (true, string.Empty);
        //    }
        //    else
        //    {
        //        // 경로 실패시 디버깅 정보 초기화
        //        _pathDebugLabel.Text = $"경로: 실패 - {advancedResult.ErrorMessage}";

        //        return (false, $"경로를 찾을 수 없습니다:\n{advancedResult.ErrorMessage}");
        //    }
        //}
        #endregion

        private void btPath2_Click(object sender, EventArgs e)
        {
            // 1. 기본 정보 획득
            if (_startNodeCombo.SelectedItem == null || _startNodeCombo.Text == "선택하세요") SetStartNodeFromAGVPosition();
            if (_startNodeCombo.SelectedItem == null || _targetNodeCombo.SelectedItem == null)
            {
                MessageBox.Show("시작/목표 노드를 확인하세요");
                return;
            }

            //var selectedAGV = _agvListCombo.SelectedItem as VirtualAGV;
            //if (selectedAGV == null) return AGVPathResult.CreateFailure("Virtual AGV 없음");
            var selectedAGV = _agvListCombo.SelectedItem as VirtualAGV;

            // 경로계산2 (Gateway Logic)
            var startNode = (_startNodeCombo.SelectedItem as ComboBoxItem<MapNode>)?.Value;
            var targetNode = (_targetNodeCombo.SelectedItem as ComboBoxItem<MapNode>)?.Value;
            var rlt = CalcPath(startNode, targetNode, this._simulatorCanvas.Nodes, selectedAGV.PrevNode, selectedAGV.PrevDirection);
            if (rlt.Success == false) MessageBox.Show(rlt.Message, "알림", MessageBoxButtons.OK, MessageBoxIcon.Information);
            else
            {
                // 8. 적용  

                ApplyResultToSimulator(rlt, selectedAGV);
                UpdateAdvancedPathDebugInfo(rlt);
            }

        }

        /// <summary>
        /// 길목(Gateway) 기반 경로 계산
        /// 버퍼-버퍼 상태에서는 별도의 추가 로직을 적용합니다
        /// </summary>
        public AGVPathResult CalcPath(MapNode startNode, MapNode targetNode, List<MapNode> nodes,
            MapNode prevNode, AgvDirection prevDir)
        {
            // Core Logic으로 이관됨
            var pathFinder = new AGVPathfinder(nodes);
            var result = pathFinder.CalculatePath(startNode, targetNode, prevNode, prevDir);

            //게이트웨이노드를 하이라이트강조 한단
            this._simulatorCanvas.HighlightNodeId = (result.Gateway?.Id ?? string.Empty);
            return result;
        }
        /// <summary>
        /// 길목(Gateway) 기반 경로 계산
        /// 버퍼-버퍼 상태에서는 별도의 추가 로직을 적용합니다
        /// </summary>
        public AGVPathResult CalcPath_New(MapNode startNode, MapNode targetNode, List<MapNode> nodes,
            MapNode prevNode, AgvDirection prevDir)
        {
            // Core Logic으로 이관됨
            var pathFinder = new AGVPathfinder(nodes);
            var result = pathFinder.CalculateScriptedPath(startNode, targetNode, prevNode, prevDir);

            //게이트웨이노드를 하이라이트강조 한단
            this._simulatorCanvas.HighlightNodeId = (result.Gateway?.Id ?? string.Empty);
            return result;
        }
        private void ApplyResultToSimulator(AGVPathResult result, VirtualAGV agv)
        {
            _simulatorCanvas.CurrentPath = result;
            _pathLengthLabel.Text = $"Gateway경로: {result.TotalDistance:F1}";
            agv.SetPath(result);
            //_simulatorCanvas.CheckAndDisplayDockingValidation(result); // Optional/Needs access
            _simulatorCanvas.FitToNodes();
        }

        private void btSelectMapEditor_Click(object sender, EventArgs e)
        {
            using (var openDialog = new OpenFileDialog())
            {
                openDialog.Filter = "실행 파일 (*.exe)|*.exe|모든 파일 (*.*)|*.*";
                openDialog.Title = "MapEditor 실행 파일 선택";

                if (!string.IsNullOrEmpty(_config.MapEditorExecutablePath) && File.Exists(_config.MapEditorExecutablePath))
                {
                    openDialog.InitialDirectory = Path.GetDirectoryName(_config.MapEditorExecutablePath);
                    openDialog.FileName = Path.GetFileName(_config.MapEditorExecutablePath);
                }

                if (openDialog.ShowDialog() == DialogResult.OK)
                {
                    _config.MapEditorExecutablePath = openDialog.FileName;
                    _config.Save();

                    _statusLabel.Text = $"MapEditor 경로 설정: {Path.GetFileName(openDialog.FileName)}";
                    MessageBox.Show($"MapEditor 실행 파일이 설정되었습니다:\n{openDialog.FileName}",
                                  "경로 설정 완료", MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            // 1. 기본 정보 획득
            if (_startNodeCombo.SelectedItem == null || _startNodeCombo.Text == "선택하세요") SetStartNodeFromAGVPosition();
            if (_startNodeCombo.SelectedItem == null || _targetNodeCombo.SelectedItem == null)
            {
                MessageBox.Show("시작/목표 노드를 확인하세요");
                return;
            }

            //var selectedAGV = _agvListCombo.SelectedItem as VirtualAGV;
            //if (selectedAGV == null) return AGVPathResult.CreateFailure("Virtual AGV 없음");
            var selectedAGV = _agvListCombo.SelectedItem as VirtualAGV;

            // 경로계산2 (Gateway Logic)
            var startNode = (_startNodeCombo.SelectedItem as ComboBoxItem<MapNode>)?.Value;
            var targetNode = (_targetNodeCombo.SelectedItem as ComboBoxItem<MapNode>)?.Value;
            var rlt = CalcPath_New(startNode, targetNode, this._simulatorCanvas.Nodes, selectedAGV.PrevNode, selectedAGV.PrevDirection);
            if (rlt.Success == false) MessageBox.Show(rlt.Message, "알림", MessageBoxButtons.OK, MessageBoxIcon.Information);
            else
            {
                // 8. 적용  

                ApplyResultToSimulator(rlt, selectedAGV);
                UpdateAdvancedPathDebugInfo(rlt);
            }
        }
    }
}