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;

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

        private UnifiedAGVCanvas _simulatorCanvas;
        private List<MapNode> _mapNodes;
        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();

            // 데이터 초기화
            _mapNodes = new List<MapNode>();
            _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();

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

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

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

            // 목적지 선택 이벤트 구독
            _simulatorCanvas.NodeSelected += 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 (*.agvmap)|*.agvmap|모든 파일 (*.*)|*.*";
                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 (_mapNodes == null || _mapNodes.Count == 0)
            {
                MessageBox.Show("먼저 맵을 로드해주세요.", "알림", MessageBoxButtons.OK, MessageBoxIcon.Information);
                return;
            }

            var agvId = $"AGV{_agvList.Count + 1:D2}";
            var startPosition = _mapNodes.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 OnCalculatePath_Click(object sender, EventArgs e)
        {
            var rlt = CalcPath();
            if (rlt.result == false) MessageBox.Show(rlt.message, "알림", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        (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(_mapNodes);
            }

            // 현재 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, _mapNodes);
                }

                _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}");
            }
        }

        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, MapNode selectedNode)
        {
            try
            {
                // 타겟계산 모드에서만 처리
                if (_isTargetCalcMode)
                {
                    // 타겟계산 모드 해제
                    //_isTargetCalcMode = false;
                    //_targetCalcButton.Text = "타겟계산";
                    //_targetCalcButton.BackColor = SystemColors.Control;
                    //_simulatorCanvas.CurrentEditMode = UnifiedAGVCanvas.EditMode.Select;
                    if (selectedNode == null) return;

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

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

                    // 자동으로 경로 계산 수행
                    OnCalculatePath_Click(this, EventArgs.Empty);
                }
            }
            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.NodeId}]"))
                            {
                                _startNodeCombo.SelectedIndex = i;
                                break;
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                _statusLabel.Text = $"시작 노드 설정 오류: {ex.Message}";
            }
        }

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

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

            foreach (var node in _mapNodes)
            {
                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(string rfidId, VirtualAGV selectedAGV)
        {
            try
            {
                // 현재 선택된 방향 확인 (최상단에서 먼저 확인)
                var directionItem = _directionCombo.SelectedItem as DirectionItem;
                var currentDirection = directionItem?.Direction ?? AgvDirection.Forward;

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

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

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

                    _statusLabel.Text = $"기존 노드로 이동: {existingNode.NodeId} [{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
                {
                    NodeId = newNodeId,
                    RfidId = rfidId,
                    Position = new Point(newX, newY),
                    Type = NodeType.Normal,
                    IsActive = true,
                    Name = $"N{_scanNodeCounter}"
                };

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

                _mapNodes.Add(newNode);

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

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

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

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

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

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

                // UI 업데이트
                UpdateNodeComboBoxes();

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

                Program.WriteLine($"[맵 스캔] 노드 생성 완료: {newNode.NodeId} (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 간격으로 업데이트
                }
            }

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

        #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 (string.IsNullOrEmpty(rfidId))
            {
                MessageBox.Show("RFID 값을 입력해주세요.", "알림", MessageBoxButtons.OK, MessageBoxIcon.Information);
                return;
            }
            
            // 선택된 방향 확인
            var selectedDirectionItem = _directionCombo.SelectedItem as DirectionItem;
            var selectedDirection = selectedDirectionItem?.Direction ?? AgvDirection.Forward;


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

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

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

            // 콘솔 출력 (상세한 리프트 방향 계산 과정)
            Program.WriteLine($"[AGV-{selectedAGV.AgvId}] 위치 설정:");
            Program.WriteLine($"  RFID: {rfidId} → 노드: {targetNode.NodeId}");
            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.NodeId}), 방향: {selectedDirectionItem?.DisplayText ?? "전진"}로 설정했습니다.";
            _rfidTextBox.Text = ""; // 입력 필드 초기화

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

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

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

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

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

            // 처음 10개의 RFID만 표시 (노드 이름 포함)
            var rfidList = nodesWithRfid.Take(10).Select(n =>
            {
                var nodeNamePart = !string.IsNullOrEmpty(n.Name) ? $" {n.Name}" : "";
                return $"- {n.RfidId} → {n.NodeId}{nodeNamePart}";
            });
            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);

                if (result.Success)
                {
                    Console.WriteLine($"Map File Load : {filePath}");

                    _mapNodes = result.Nodes;
                    _currentMapFilePath = filePath;

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

                    // 시뮬레이터 캔버스에 맵 설정
                    _simulatorCanvas.Nodes = _mapNodes;

                    // 설정에 마지막 맵 파일 경로 저장
                    _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 (_mapNodes != null)
            {
                foreach (var node in _mapNodes)
                {
                    if (node.IsActive && node.HasRfid())
                    {
                        // {rfid} - [{node}] {name} 형식으로 ComboBoxItem 생성
                        var nodeNamePart = !string.IsNullOrEmpty(node.Name) ? $" {node.Name}" : "";
                        var displayText = $"{node.RfidId} - [{node.NodeId}]{nodeNamePart}";
                        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;
            _calculatePathButton.Enabled = _startNodeCombo.SelectedItem != null &&
                                         _targetNodeCombo.SelectedItem != null;

            // RFID 위치 설정 관련
            var hasSelectedAGV = _agvListCombo.SelectedItem != null;
            var hasRfidNodes = _mapNodes != null && _mapNodes.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, _mapNodes);

            // 이동 각도 계산 (표시용)
            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, _mapNodes);

            // 도킹 방향 정보 추가
            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 string GetRfidByNodeId(string nodeId)
        {
            var node = _mapNodes?.FirstOrDefault(n => n.NodeId == nodeId);
            return node?.HasRfid() == true ? node.RfidId : nodeId;
        }

        /// <summary>
        /// 노드의 표시명 가져오기 (RFID 우선, 없으면 (NodeID) 형태)
        /// </summary>
        private string GetDisplayName(string nodeId)
        {
            var node = _mapNodes?.FirstOrDefault(n => n.NodeId == nodeId);
            if (node != null && !string.IsNullOrEmpty(node.RfidId))
            {
                return node.RfidId;
            }
            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 = info.NextNodeId != null ? GetRfidByNodeId(info.NextNodeId) : "END";

                var flags = new List<string>();
                if (info.CanRotate) 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 += "[↻]";

                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 (_mapNodes == null || _mapNodes.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 = _mapNodes.Where(n =>
                n.Type == NodeType.Charging || n.Type == NodeType.Docking).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 "-";

            if (!string.IsNullOrEmpty(node.RfidId))
                return node.RfidId;

            return $"({node.NodeId})";
        }

        /// <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?.NodeId == nodeId)
                {
                    _targetNodeCombo.SelectedIndex = i;
                    return;
                }
            }
        }

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

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

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

                    if (dockingValidation.IsValid)
                    {
                        logItem.Success = true;
                        logItem.Message = "성공";
                        logItem.DetailedPath = currentPath.GetDetailedPathInfo();
                    }
                    else
                    {
                        logItem.Success = false;
                        logItem.Message = $"도킹 검증 실패: {dockingValidation.ValidationError}";
                        logItem.DetailedPath = currentPath.GetDetailedPathInfo();
                    }
                }
                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 _mapNodes)
            {
                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.NodeId}→{nodeB.NodeId}";
                    var pairKey2 = $"{nodeB.NodeId}→{nodeA.NodeId}";

                    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 (nodeA, nodeB) in nodePairs)
            {
                foreach (var (direction, directionName) in directions)
                {
                    // 취소 확인
                    if (logForm.CancelRequested)
                    {
                        logForm.AppendLog($"테스트 취소됨 - {currentTest}/{totalTests} 완료");
                        return;
                    }

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

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

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

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

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

                        // 방향 콤보박스 선택
                        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.NodeId);

                            // 경로 계산 버튼 클릭 (실제 사용자 동작)
                            var calcResult = CalcPath();

                            // 테스트 결과 생성
                            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.Reason}\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];
            var command = agv.Predict();
            this.lbPredict.Text = $"Motor:{command.Motor},Magnet:{command.Magnet},Speed:{command.Speed} : {command.Reason}";
        }
    

        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)
                {
                    // 기존 맵 데이터 삭제
                    _mapNodes?.Clear();
                    _mapNodes = new List<MapNode>();
                    _simulatorCanvas.Nodes = _mapNodes;
                    _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 = $"맵 스캔 완료 - {_mapNodes?.Count ?? 0}개 노드 생성됨";

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

                // 맵 저장 권장
                if (_mapNodes != null && _mapNodes.Count > 0)
                {
                    var saveResult = MessageBox.Show(
                        $"맵 스캔이 완료되었습니다.\n\n" +
                        $"생성된 노드: {_mapNodes.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, _mapNodes);

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

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

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

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

                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);
            }
        }
    }

    /// <summary>
    /// 방향 콤보박스용 아이템 클래스
    /// </summary>
    public class DirectionItem
    {
        public AgvDirection Direction { get; }
        public string DisplayText { get; }

        public DirectionItem(AgvDirection direction, string displayText)
        {
            Direction = direction;
            DisplayText = displayText;
        }

        public override string ToString()
        {
            return DisplayText;
        }
    }

    /// <summary>
    /// 제네릭 콤보박스 아이템 클래스
    /// </summary>
    /// <typeparam name="T">값의 타입</typeparam>
    public class ComboBoxItem<T>
    {
        public T Value { get; }
        public string DisplayText { get; }

        public ComboBoxItem(T value, string displayText)
        {
            Value = value;
            DisplayText = displayText;
        }

        public override string ToString()
        {
            return DisplayText;
        }
    }
}