using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.IO;
using System.Linq;
using System.Management.Instrumentation;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows.Forms;
using AGVControl.Models;
using AR;
using COMM;

namespace AGVControl
{

    public partial class MapControl : Control
    {
        #region 상수 정의
        private const int LINE_WIDTH = 20;          // 선 굵기
        private const int TOOLBAR_WIDTH = 58;       // 툴바 너비
        private const int TOOLBAR_WIDTHR = 78;      // 우측 툴바 너비
        private const int TOOLBAR_BUTTON_HEIGHT = 40; // 툴바 버튼 높이
        private const int TOOLBAR_MARGIN = 5;       // 툴바 마진
        private const int SELECTION_DISTANCE_POINT = 7;   // 선택 가능 거리
        private const int SELECTION_DISTANCE_TEXT = 3;   // 선택 가능 거리
        #endregion

        #region 멤버 변수
        // 맵 데이터

        private List<MapText> mapTexts;
        //private List<CustomLine> customLines;
        private List<RFIDLine> rfidLines;

        public MapControlManager Manager = new MapControlManager();

        // 화면 조작 관련
        private float zoom = 1.0f;
        private PointF offset = PointF.Empty;
        private Point lastMousePosition;
        private Point currentMousePosition;
        private bool isDragging = false;
        private Point? previewStartPoint = null;
        private Point? branchPoint = null;

        // 모드 관련
        private bool isAddingText = false;
        private bool isAddingPoint = false;
        private bool isDrawingCustomLine = false;
        private bool isDrawingRFIDLine = false;
        private bool isDeletingRFIDLine = false;
        private bool isDrawingLine = false;
        private bool isDraggingPoint = false;
        private bool isDraggingText = false;
        private Point dragOffset;

        // 선택된 객체
        private MapText selectedText = null;
        private CustomLine selectedLine = null;
        private RFIDPoint selectedRFID = null;
        private RFIDLine selectedRFIDLine = null;
        private Point? draggingPoint = null;

        // 툴바 관련
        private List<ToolBarItem> toolbarRects;

        // RFID 관련
        public string RFIDStartNo { get; set; } = string.Empty;
        public int RFIDLastNumber = 0;
        public string Filename = string.Empty;
        #endregion

        #region 속성
        public MapText SelectedText => selectedText;
        public CustomLine SelectedLine => selectedLine;
        public RFIDPoint SelectedRFID => selectedRFID;
        public RFIDLine SelectedRFIDLine => selectedRFIDLine;
        #endregion

        #region 생성자 및 초기화
        public MapControl()
        {
            this.DoubleBuffered = true;
            mapTexts = new List<MapText>();
            // customLines = new List<CustomLine>();
            rfidLines = new List<RFIDLine>();

            UpdateToolbarRects();
        }
        #endregion

        #region 이벤트 핸들러
        public event EventHandler<MouseEventArgs> OnRightClick;
        #endregion

        #region OVERRIDE
        protected override void OnMouseUp(MouseEventArgs e)
        {
            base.OnMouseUp(e);
            if (e.Button == MouseButtons.Middle)
            {
                isDragging = false;
                this.Cursor = Cursors.Default;
            }
            else if (e.Button == MouseButtons.Left)
            {
                isDragging = false;
                isDraggingPoint = false;
                isDraggingText = false;
            }
        }
        protected override void OnMouseClick(MouseEventArgs e)
        {
            base.OnMouseClick(e);
            var mapPoint = ScreenToMap(e.Location);
            if (e.Button == MouseButtons.Right)
            {
                OnRightClick?.Invoke(this, e);
                this.MouseMode = eMouseMode.Default;
            }
            else if (e.Button == MouseButtons.Left)
            {
                // 툴바 버튼 클릭 처리
                var toolbar = toolbarRects.FirstOrDefault(t => t.Bounds.Contains(e.Location));
                if (toolbar != null)
                {
                    switch (toolbar.Title.ToLower())
                    {
                        case "+": ZoomIn(); break;
                        case "-": ZoomOut(); break;
                        case "1:1": ResetZoom(); break;
                        case "cut": MouseMode = (eMouseMode.rfidcut); break;
                        case "text": MouseMode = (eMouseMode.addtext); break;
                        case "line": MouseMode = (eMouseMode.addrfidline); break;
                        case "cline": MouseMode = (eMouseMode.addcustomline); break;
                        case "point": MouseMode = (eMouseMode.addrfidpoint); break;
                        case "clear":
                            Manager.agv.MainPath.Clear();
                            break;
                        case "path":

                            var input1 = AR.UTIL.InputBox("input start");
                            if (input1.Item1 == false) return;
                            var input2 = AR.UTIL.InputBox("input end");
                            if (input2.Item1 == false) return;

                            var startRFID = input1.Item2;
                            var endRFID = input2.Item2;

                            var valid1 = uint.TryParse(input1.Item2, out uint vstart);
                            var valid2 = uint.TryParse(input2.Item2, out uint vend);

                            if (valid1 == false || valid2 == false)
                            {
                                MessageBox.Show("RFID값은 정수로 입력하세요", "경로 계산", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                                return;
                            }

                            var rlt = Manager.CalculatePath(vstart, vend);
                            if (rlt.Success == false)
                            {
                                MessageBox.Show(rlt.Message, "경로 계산", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                                return;
                            }
                            else
                            {
                                SetCurrentPath(rlt.Path);   //현재 경로로 설정함
                                MessageBox.Show($"경로가 계산되었습니다.\nRFID 순서: {string.Join(" -> ", rlt.Path)}",
                                   "경로 계산", MessageBoxButtons.OK, MessageBoxIcon.Information);
                                if (SetTargetPosition(vend) == false)
                                {
                                    MessageBox.Show("목적지 설정 실패");
                                }
                            }
                            break;
                        case "pos":
                            var tag = AR.UTIL.InputBox("input rfid tag value");
                            if (tag.Item1 && tag.Item2 != "" && uint.TryParse(tag.Item2, out uint val) == true)
                            {
                                var targetRFID = SetCurrentPosition((ushort)val);
                            }
                            break;
                        case "save":
                            using (var od = new SaveFileDialog())
                            {
                                od.Filter = "path data|*.route";
                                od.FilterIndex = 0;
                                od.RestoreDirectory = true;
                                if (Filename.isEmpty() == false)
                                {
                                    od.FileName = System.IO.Path.GetFileName(Filename);
                                    od.InitialDirectory = System.IO.Path.GetDirectoryName(Filename);
                                }

                                if (od.ShowDialog() == DialogResult.OK)
                                {
                                    Filename = od.FileName;
                                    this.SaveToFile(Filename);
                                    this.Invalidate();
                                }
                            }
                            break;
                        case "load":
                            using (var od = new OpenFileDialog())
                            {
                                od.Filter = "path data|*.route";
                                od.FilterIndex = 0;


                                if (string.IsNullOrEmpty(this.Filename) == false)
                                {
                                    od.FileName = System.IO.Path.GetFileName(this.Filename);
                                    od.InitialDirectory = System.IO.Path.GetDirectoryName(this.Filename);
                                }
                                else
                                {
                                    od.RestoreDirectory = true;
                                }


                                if (od.ShowDialog() == DialogResult.OK)
                                {
                                    this.LoadFromFile(od.FileName, out string errmsg);
                                    if (errmsg.isEmpty() == false) UTIL.MsgE(errmsg);
                                    this.Invalidate();
                                }
                            }
                            break;
                    }
                    return;
                }

                // RFID 포인트 선택
                var clickedRFID = Manager.RFIDPoints.FirstOrDefault(r => Manager.GetDistance(mapPoint, r.Location) <= SELECTION_DISTANCE_POINT);
                switch (mousemode)
                {
                    case eMouseMode.rfidcut:
                        DeleteNearbyRFIDLine(mapPoint);
                        break;
                    case eMouseMode.addrfidpoint:
                        if (string.IsNullOrEmpty(this.RFIDStartNo) == false)
                        {
                            if (ushort.TryParse(this.RFIDStartNo, out ushort rfidvalue))
                            {
                                AddRFIDPoint(mapPoint, rfidvalue);

                                // 숫자로 끝나는 RFID 값인 경우 자동 증가
                                if (Regex.IsMatch(RFIDStartNo, @"^[A-Za-z]+\d+$"))
                                {
                                    // 마지막 숫자 부분 추출
                                    Match match = Regex.Match(RFIDStartNo, @"\d+$");
                                    if (match.Success)
                                    {
                                        int currentNumber = int.Parse(match.Value);
                                        if (currentNumber > this.RFIDLastNumber)
                                        {
                                            RFIDLastNumber = currentNumber;
                                        }
                                        RFIDLastNumber++;

                                        // 숫자 부분을 새로운 번호로 교체
                                        RFIDStartNo = RFIDStartNo.Substring(0, match.Index) + RFIDLastNumber.ToString("D4");
                                    }
                                }
                            }

                        }
                        break;
                    case eMouseMode.addtext:
                        var text = new MapText
                        {
                            Location = mapPoint,
                            Text = "새 텍스트",
                            TextColor = Color.Black,
                            BackgroundColor = Color.Transparent,
                            Font = new Font("Arial", 12)
                        };
                        mapTexts.Add(text);
                        selectedText = text;
                        this.mousemode = eMouseMode.Default;
                        this.Invalidate();
                        break;
                    //case eMouseMode.addcustomline:
                    //    if (previewStartPoint == null)
                    //    {
                    //        previewStartPoint = mapPoint;
                    //    }
                    //    else
                    //    {
                    //        var line = new CustomLine
                    //        {
                    //            StartPoint = previewStartPoint.Value,
                    //            EndPoint = mapPoint,
                    //            LineColor = Color.Red,
                    //            LineWidth = 2
                    //        };
                    //        customLines.Add(line);
                    //        selectedLine = line;
                    //        previewStartPoint = null;
                    //        this.Invalidate();
                    //    }
                    //    break;
                    case eMouseMode.addrfidline:
                        if (clickedRFID != null)
                        {
                            if (previewStartPoint == null)
                            {
                                previewStartPoint = clickedRFID.Location;
                            }
                            else
                            {
                                var startRFID = Manager.RFIDPoints.FirstOrDefault(r => r.Location == previewStartPoint);
                                if (startRFID != null)
                                {
                                    var line = new RFIDLine
                                    {
                                        StartPoint = previewStartPoint.Value,
                                        EndPoint = clickedRFID.Location,
                                    };
                                    rfidLines.Add(line);
                                    selectedRFIDLine = line;

                                    // RFID 연결 정보 처리
                                    ProcessRFIDConnections();
                                }
                                // 다음 라인을 위해 현재 클릭한 RFID를 시작점으로 설정
                                previewStartPoint = clickedRFID.Location;
                            }
                            this.Invalidate();
                        }
                        break;
                }
            }
        }
        protected override void OnMouseDoubleClick(MouseEventArgs e)
        {
            base.OnMouseDoubleClick(e);

            if (e.Button == MouseButtons.Left)
            {
                var mapPoint = ScreenToMap(e.Location);

                //RFID 포인트 찾기
                var selected_rfid = Manager.RFIDPoints.Where(t => t.Bounds.Expand(SELECTION_DISTANCE_POINT, SELECTION_DISTANCE_POINT).Contains(mapPoint)).FirstOrDefault();
                if (selected_rfid != null)
                {
                    //연결정보확인
                    List<RFIDConnection> connections = new List<RFIDConnection>();
                    var p1 = Manager.rfidConnections.Where(t => t.P1.Value == selected_rfid.Value);
                    var p2 = Manager.rfidConnections.Where(t => t.P2.Value == selected_rfid.Value);
                    if (p1.Any()) connections.AddRange(p1.ToArray());
                    if (p2.Any()) connections.AddRange(p2.ToArray());


                    using (var f = new Dialog.fPropertyRFIDPoint(selected_rfid, connections))
                        f.ShowDialog();
                    //UTIL.ShowPropertyDialog(selected_rfid);
                    this.Invalidate();
                    return;
                }

                // 텍스트 객체 찾기                
                var selected_txt = mapTexts.Where(t => t.Bounds.Expand(SELECTION_DISTANCE_TEXT , SELECTION_DISTANCE_TEXT).Contains(mapPoint)).FirstOrDefault();
                if (selected_txt != null)
                {
                    UTIL.ShowPropertyDialog(selected_txt);
                    this.Invalidate();
                    return;
                }
            }
        }

        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);
            UpdateToolbarRects();
            this.Invalidate();
        }

        protected override void OnMouseWheel(MouseEventArgs e)
        {
            base.OnMouseWheel(e);
            if (e.Delta > 0)
            {
                zoom *= 1.1f;
            }
            else
            {
                zoom /= 1.2f;
            }

            zoom = Math.Max(0.1f, Math.Min(10.0f, zoom));
            this.Invalidate();
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);
            lastMousePosition = e.Location;
            var mapPoint = ScreenToMap(e.Location);

            if (e.Button == MouseButtons.Middle)
            {
                isDragging = true;
                this.Cursor = Cursors.SizeAll;
            }
            else if (e.Button == MouseButtons.Left && !isAddingText && !isDrawingCustomLine && !isDrawingRFIDLine)
            {
                isDragging = true;

                // 텍스트 선택 및 드래그 시작
                foreach (var text in mapTexts)
                {
                    var textSize = CreateGraphics().MeasureString(text.Text, text.Font);
                    var rect = new RectangleF(
                        text.Location.X - SELECTION_DISTANCE_TEXT,
                        text.Location.Y - SELECTION_DISTANCE_TEXT,
                        textSize.Width + SELECTION_DISTANCE_TEXT * 2,
                        textSize.Height + SELECTION_DISTANCE_TEXT * 2
                    );

                    if (rect.Contains(mapPoint))
                    {
                        selectedText = text;
                        isDraggingText = true;
                        // 드래그 시작점과 텍스트 위치의 차이를 저장
                        dragOffset = new Point(
                            text.Location.X - mapPoint.X,
                            text.Location.Y - mapPoint.Y
                        );
                        return;
                    }
                }

                //// 커스텀 라인 선택 및 드래그 포인트 설정
                //foreach (var line in customLines)
                //{
                //    float startDistance = Manager.GetDistance(mapPoint, line.StartPoint);
                //    float endDistance = Manager.GetDistance(mapPoint, line.EndPoint);

                //    if (startDistance < SELECTION_DISTANCE)
                //    {
                //        selectedLine = line;
                //        draggingPoint = line.StartPoint;
                //        isDraggingPoint = true;
                //        return;
                //    }
                //    else if (endDistance < SELECTION_DISTANCE)
                //    {
                //        selectedLine = line;
                //        draggingPoint = line.EndPoint;
                //        isDraggingPoint = true;
                //        return;
                //    }
                //}


                // RFID 포인트 선택
                var clickedRFID = Manager.RFIDPoints.FirstOrDefault(r =>
                     Manager.GetDistance(mapPoint, r.Location) <= SELECTION_DISTANCE_POINT);
                if (clickedRFID != null)
                {
                    selectedRFID = clickedRFID;
                    draggingPoint = clickedRFID.Location;
                    isDraggingPoint = true;
                    return;
                }


            }
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            currentMousePosition = e.Location;
            var mapPoint = ScreenToMap(e.Location);

            if ((mousemode == eMouseMode.addcustomline || mousemode == eMouseMode.addrfidline) && branchPoint.HasValue)
            {
                UpdatePreviewLine(e.Location);
            }

            // 툴바 버튼 호버 상태 업데이트
            var oldHovering = toolbarRects.OrderBy(t => t.Idx).Select(t => t.isHovering).ToArray();

            //toolbar check
            toolbarRects.ForEach(t => t.isHovering = t.Bounds.Contains(e.Location));

            //hovering check
            if (toolbarRects.Where(t => t.isHovering).Any())
            {
                this.Cursor = Cursors.Hand;
            }
            else if (isDeletingRFIDLine)
            {
                this.Cursor = GetScissorsCursor();
            }
            else
            {
                this.Cursor = Cursors.Default;
            }

            //hovering display update
            if (toolbarRects.Where(t => t.Dirty).Any())
            {
                this.Invalidate();
            }

            if (isDragging)
            {
                if (e.Button == MouseButtons.Middle)
                {
                    offset = new PointF(
                        offset.X + (e.Location.X - lastMousePosition.X),
                        offset.Y + (e.Location.Y - lastMousePosition.Y)
                    );
                }
                else if (isDraggingText && selectedText != null)
                {
                    // 텍스트 이동 - 드래그 오프셋 적용
                    selectedText.Location = new Point(
                        mapPoint.X + dragOffset.X,
                        mapPoint.Y + dragOffset.Y
                    );
                }
                else if (isDraggingPoint && draggingPoint.HasValue)
                {
                    // 포인트 이동
                    var delta = new Point(
                        mapPoint.X - ScreenToMap(lastMousePosition).X,
                        mapPoint.Y - ScreenToMap(lastMousePosition).Y
                    );

                    if (selectedLine != null)
                    {
                        // 커스텀 라인 포인트 이동
                        if (draggingPoint.Value == selectedLine.StartPoint)
                        {
                            selectedLine.StartPoint = new Point(
                                selectedLine.StartPoint.X + delta.X,
                                selectedLine.StartPoint.Y + delta.Y
                            );
                        }
                        else if (draggingPoint.Value == selectedLine.EndPoint)
                        {
                            selectedLine.EndPoint = new Point(
                                selectedLine.EndPoint.X + delta.X,
                                selectedLine.EndPoint.Y + delta.Y
                            );
                        }
                    }
                    else if (selectedRFID != null)   // RFID 포인트 이동
                    {
                        // RFID 포인트 위치 업데이트
                        selectedRFID.Location = new Point(
                            selectedRFID.Location.X + delta.X,
                            selectedRFID.Location.Y + delta.Y
                        );

                        // 연결된 RFID 라인 업데이트
                        foreach (var line in rfidLines)
                        {
                            if (line.StartPoint == draggingPoint.Value)
                            {
                                line.StartPoint = selectedRFID.Location;
                            }
                            if (line.EndPoint == draggingPoint.Value)
                            {
                                line.EndPoint = selectedRFID.Location;
                            }
                        }
                    }
                }

                lastMousePosition = e.Location;
                this.Invalidate();
            }

            // 미리보기 라인 업데이트를 위한 마우스 위치 저장
            if (isDrawingRFIDLine || isDrawingCustomLine || previewStartPoint.HasValue)
            {
                currentMousePosition = mapPoint;
            }

            this.Invalidate();
        }

        #endregion

        #region 기타 메서드

        private PointF DrawLineWithLength(Graphics graphics, Pen pen, PointF p1, PointF p2, float length)
        {
            // 두 점 사이의 벡터 계산
            float dx = p2.X - p1.X;
            float dy = p2.Y - p1.Y;

            // 두 점 사이의 거리 계산
            float distance = (float)Math.Sqrt(dx * dx + dy * dy);

            // 거리가 0이면 선을 그릴 수 없음
            if (distance == 0)
                return PointF.Empty;

            // 단위 벡터 계산 (방향)
            float unitX = dx / distance;
            float unitY = dy / distance;

            // 시작점에서 지정된 길이만큼 떨어진 끝점 계산
            PointF endPoint = new PointF(
                p1.X + unitX * length,
                p1.Y + unitY * length
            );

            // 선 그리기
            graphics.DrawLine(pen, p1, endPoint);
            return endPoint;
        }


        private void UpdateToolbarRects()
        {
            int x, y, c, row;
            var idx = 0;

            if (this.toolbarRects == null) this.toolbarRects = new List<ToolBarItem>();
            else this.toolbarRects.Clear();

            //left toolbar
            x = TOOLBAR_MARGIN;
            y = 10;

            var menu_left = new string[] { "+", "-", "1:1", "Cut" };
            foreach (var item in menu_left)
            {
                toolbarRects.Add(new ToolBarItem { Idx = idx++, Title = item, Bounds = new Rectangle(x, y, TOOLBAR_WIDTH - 2 * TOOLBAR_MARGIN, TOOLBAR_BUTTON_HEIGHT) });
                y += TOOLBAR_BUTTON_HEIGHT + TOOLBAR_MARGIN;
            }


            //right toolbar
            y = 10;
            row = 0;
            x = DisplayRectangle.Right - TOOLBAR_WIDTHR - TOOLBAR_MARGIN;

            var menu_right = new string[] { "Text", "Line", "Point", "Magnet", "Load", "Save", "Pos", "Path", "Clear" };
            foreach (var item in menu_right)
            {
                toolbarRects.Add(new ToolBarItem { Idx = idx++, Title = item, Bounds = new Rectangle(x, y, TOOLBAR_WIDTHR - 2 * TOOLBAR_MARGIN, TOOLBAR_BUTTON_HEIGHT) });
                y += TOOLBAR_BUTTON_HEIGHT + TOOLBAR_MARGIN;
            }


            //toolbarRects.Add(new ToolBarItem { Idx = idx++, Title = "Line", Bounds = new Rectangle(x, y, TOOLBAR_WIDTHR - 2 * TOOLBAR_MARGIN, TOOLBAR_BUTTON_HEIGHT) });

            //y += TOOLBAR_BUTTON_HEIGHT + TOOLBAR_MARGIN;
            //toolbarRects.Add(new ToolBarItem { Idx = idx++, Title = "Point", Bounds = new Rectangle(x, y, TOOLBAR_WIDTHR - 2 * TOOLBAR_MARGIN, TOOLBAR_BUTTON_HEIGHT) });

            //y += TOOLBAR_BUTTON_HEIGHT + TOOLBAR_MARGIN;
            //toolbarRects.Add(new ToolBarItem { Idx = idx++, Title = "Magnet", Bounds = new Rectangle(x, y, TOOLBAR_WIDTHR - 2 * TOOLBAR_MARGIN, TOOLBAR_BUTTON_HEIGHT) });

            //y += TOOLBAR_BUTTON_HEIGHT + TOOLBAR_MARGIN;
            //toolbarRects.Add(new ToolBarItem { Idx = idx++, Title = "Load", Bounds = new Rectangle(x, y, TOOLBAR_WIDTHR - 2 * TOOLBAR_MARGIN, TOOLBAR_BUTTON_HEIGHT) });

            //y += TOOLBAR_BUTTON_HEIGHT + TOOLBAR_MARGIN;
            //toolbarRects.Add(new ToolBarItem { Idx = idx++, Title = "Save", Bounds = new Rectangle(x, y, TOOLBAR_WIDTHR - 2 * TOOLBAR_MARGIN, TOOLBAR_BUTTON_HEIGHT) });

            //y += TOOLBAR_BUTTON_HEIGHT + TOOLBAR_MARGIN;
            //toolbarRects.Add(new ToolBarItem { Idx = idx++, Title = "Pos", Bounds = new Rectangle(x, y, TOOLBAR_WIDTHR - 2 * TOOLBAR_MARGIN, TOOLBAR_BUTTON_HEIGHT) });

            //y += TOOLBAR_BUTTON_HEIGHT + TOOLBAR_MARGIN;
            //toolbarRects.Add(new ToolBarItem { Idx = idx++, Title = "Path", Bounds = new Rectangle(x, y, TOOLBAR_WIDTHR - 2 * TOOLBAR_MARGIN, TOOLBAR_BUTTON_HEIGHT) });

            //y += TOOLBAR_BUTTON_HEIGHT + TOOLBAR_MARGIN;
            //toolbarRects.Add(new ToolBarItem { Idx = idx++, Title = "Clear", Bounds = new Rectangle(x, y, TOOLBAR_WIDTHR - 2 * TOOLBAR_MARGIN, TOOLBAR_BUTTON_HEIGHT) });

        }

        public void SetPreviewStartPoint(Point? point)
        {
            previewStartPoint = point;
            this.Invalidate();
        }

        public void UpdatePreviewLine(Point currentPosition)
        {
            currentMousePosition = currentPosition;
            this.Invalidate();
        }

        private Point SnapToPoint(Point point)
        {
            // RFID 포인트와 근접한지 확인
            foreach (var rfid in Manager.RFIDPoints)
            {
                if (Manager.GetDistance(point, rfid.Location) <= SELECTION_DISTANCE_POINT)
                {
                    return rfid.Location;
                }
            }

            return point;
        }



        /// <summary>
        /// 현재위치를 설정합니다
        /// </summary>
        /// <param name="rfidTagNo">RFID TagNo</param>
        /// <returns></returns>
        public bool SetCurrentPosition(ushort rfidTagNo)
        {
            var rfidPoint = Manager.FindRFIDPoint(rfidTagNo);
            if (rfidPoint != null)
            {
                // 이동 경로에 추가 (위치 업데이트보다 먼저)
                Manager.agv.AddToMovementHistory(rfidTagNo, rfidPoint.Location, Manager.agv.Current_Motor_Direction);

                // AGV 위치 업데이트
                Manager.agv.CurrentRFID = rfidPoint;

                // 목적지가 설정되어 있고 경로가 있는 경우 검증
                if (Manager.agv.TargetRFID.IsEmpty == false && Manager.agv.MainPath.Count > 0)
                {
                    // 현재 위치가 경로에 있는지 확인
                    bool isOnPath = Manager.agv.MainPath.Contains(Manager.agv.CurrentRFID);

                    if (!isOnPath)
                    {
                        // 경로를 벗어났으므로 새로운 경로 계산
                        var pathResult = Manager.CalculatePath(Manager.agv.CurrentRFID, Manager.agv.TargetRFID);
                        if (pathResult.Success)
                        {
                            SetCurrentPath(pathResult.Path);
                        }
                    }
                }

                // 목적지 RFID에 도착했고, 해당 RFID에 고정방향이 있으면 TargetDirection을 강제 설정
                if (Manager.agv.TargetRFID.Value == rfidTagNo)
                {
                    var destRFID = Manager.FindRFIDPoint(rfidTagNo);
                    if (destRFID != null && destRFID.FixedDirection.HasValue)
                    {
                        Manager.agv.TargetDirection = destRFID.FixedDirection.Value;
                    }
                }

                this.Invalidate();
                return true;
            }
            return false;
        }




        public bool SetTargetPosition(uint rfidValue)
        {
            var rfidPoint = Manager.FindRFIDPoint(rfidValue);
            if (rfidPoint != null)
            {
                Manager.agv.TargetRFID = rfidPoint;
                this.Invalidate();
                return true;
            }
            return false;
        }

        public void SetRFIDPoints(List<RFIDPoint> points)
        {
            Manager.RFIDPoints = points;
            this.Invalidate();
        }

        public void SetAGV(AGV vehicle)
        {
            Manager.agv = vehicle;
            this.Invalidate();
        }

        public void SetMapTexts(List<MapText> texts)
        {
            mapTexts = texts;
            if (mapTexts == null) mapTexts = new List<MapText>();
            this.Invalidate();
        }

        //public void SetCustomLines(List<CustomLine> lines)
        //{
        //    customLines = lines;
        //    if (customLines == null) customLines = new List<CustomLine>();
        //    this.Invalidate();
        //}

        public void SetIsAddingText(bool value)
        {
            isAddingText = value;
            isDrawingCustomLine = false;
            this.Cursor = value ? Cursors.IBeam : Cursors.Default;
        }

        public void SetIsDrawingCustomLine(bool value)
        {
            isDrawingCustomLine = value;
            isAddingText = false;
            this.Cursor = value ? Cursors.Cross : Cursors.Default;
        }

        public void SetIsDrawingRFIDLine(bool value)
        {
            isDrawingRFIDLine = value;
            isDrawingCustomLine = false;
            isAddingText = false;
            this.Cursor = value ? Cursors.Cross : Cursors.Default;
        }

        public void SetIsDeletingRFIDLine(bool value)
        {
            isDeletingRFIDLine = value;
            isDrawingCustomLine = false;
            isAddingText = false;
            isDrawingRFIDLine = false;
            this.Cursor = value ? GetScissorsCursor() : Cursors.Default;
        }

        public void SetIsDrawingLine(bool value)
        {
            isDrawingLine = value;
            isDrawingCustomLine = false;
            isAddingText = false;
            isDrawingRFIDLine = false;
            this.Cursor = value ? Cursors.Cross : Cursors.Default;
        }

        public enum eMouseMode : byte
        {
            Default = 0,
            pan,
            rfidcut,
            addtext,
            addcustomline,
            addrfidpoint,
            addrfidline,
        }

        private eMouseMode mousemode = eMouseMode.Default;
        public eMouseMode MouseMode
        {
            get { return mousemode; }
            set
            {
                if (this.mousemode == value) mousemode = eMouseMode.Default;
                else this.mousemode = value;
                switch (this.mousemode)
                {
                    case eMouseMode.pan: this.Cursor = Cursors.Hand; break;
                    case eMouseMode.addrfidline: this.Cursor = Cursors.Default; break;
                    case eMouseMode.addrfidpoint: this.Cursor = Cursors.Default; break;
                    case eMouseMode.addtext: this.Cursor = Cursors.Default; break;
                    case eMouseMode.addcustomline: this.Cursor = Cursors.Default; break;
                    default: this.Cursor = Cursors.Default; break;
                }
                previewStartPoint = null;
                Invalidate();
            }
        }

        public void SetIsAddingMagnet(bool value)
        {

        }

        public void SetIsAddingPoint(bool value)
        {
            isDrawingCustomLine = false;
            isDrawingLine = false;
            isDrawingRFIDLine = false;
            isAddingPoint = value;
            this.Cursor = value ? Cursors.Cross : Cursors.Default;
        }

        private Cursor GetScissorsCursor()
        {
            // 가위 커서 아이콘 생성
            using (var bitmap = new Bitmap(32, 32))
            using (var g = Graphics.FromImage(bitmap))
            {
                g.Clear(Color.Transparent);

                // 가위 모양 그리기
                using (var pen = new Pen(Color.Black, 2))
                {
                    // 가위 손잡이
                    g.DrawEllipse(pen, 12, 20, 8, 8);
                    g.DrawLine(pen, 16, 20, 16, 16);

                    // 가위 날
                    g.DrawLine(pen, 16, 16, 8, 8);
                    g.DrawLine(pen, 16, 16, 24, 8);
                }

                return new Cursor(bitmap.GetHicon());
            }
        }

        public void SetCurrentPath(List<RFIDPoint> path)
        {
            Manager.agv.MainPath = path;
            this.Invalidate();
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            //base.OnPaint(e);

            e.Graphics.TranslateTransform(offset.X, offset.Y);
            e.Graphics.ScaleTransform(zoom, zoom);

            DrawRFIDLines(e.Graphics);
            DrawRFIDPoints(e.Graphics);
            //DrawCustomLines(e.Graphics);
            DrawMapTexts(e.Graphics);

            DrawPath(e.Graphics);
            DrawAGV(e.Graphics);
            //DrawAGVMotor(e.Graphics);
            DrawTargetFlag(e.Graphics);  // 목적지 깃발 그리기 추가

            // 선택된 개체 강조 표시
            if (selectedRFID != null)
            {
                using (Pen pen = new Pen(Color.Magenta, 2))
                {
                    pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
                    e.Graphics.DrawEllipse(pen,
                        selectedRFID.Location.X - 10,
                        selectedRFID.Location.Y - 10,
                        20, 20);
                }
            }

            if (selectedRFIDLine != null)
            {
                using (Pen pen = new Pen(Color.Magenta, 2))
                {
                    pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
                    e.Graphics.DrawLine(pen, selectedRFIDLine.StartPoint, selectedRFIDLine.EndPoint);
                }
            }

            // 미리보기 라인 그리기
            if (previewStartPoint.HasValue)
            {
                using (Pen previewPen = new Pen(Color.FromArgb(180, Color.Yellow), LINE_WIDTH))
                {
                    previewPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
                    e.Graphics.DrawLine(previewPen, previewStartPoint.Value, currentMousePosition);
                }
            }

            // 그래픽스 변환 초기화
            e.Graphics.ResetTransform();

            // 툴바 버튼 그리기
            foreach (var item in this.toolbarRects)
                DrawToolbarButton(e.Graphics, item.Bounds, item.Title, item.isHovering);

            //예측값디스플레잉(임시)
            var predictresult = Manager.PredictResult;
            if (predictresult != null)
            {
                var nextRFID = predictresult.NextRFID?.Value.ToString() ?? "";
                var nextSPD = predictresult.MoveSpeed?.ToString() ?? string.Empty;
                var nextDIV = predictresult.MoveDiv?.ToString() ?? string.Empty;    
                var str = $"{predictresult.ReasonCode}|{predictresult.MoveState}|{predictresult.Direction}|Next:{nextRFID}|SPD:{nextSPD}|DIV:{nextDIV}";
                var strsize = e.Graphics.MeasureString(str, this.Font);
                var textcolor = Color.Red;
                if (predictresult.MoveState == AGVMoveState.Stop) textcolor = Color.Gold;

                using (var sb = new SolidBrush(textcolor))
                    e.Graphics.DrawString(str, this.Font, sb, this.Right - strsize.Width - 10, this.Bottom - strsize.Height - 10);
            }

            //경로정보표시(임시)
            var pathstr = "";
            if (Manager.agv.MainPath.Any())
            {
                pathstr = "● Path : " + string.Join("▶", Manager.agv.MainPath.Select(t => t.Value).ToArray());
                //pathstr += "\n● Target Direction Match : " + (agv.IsTargetDirectionMatch ? "O" : "X");
            }
            else pathstr = "● Path : no data";
            using (var f = new Font("Arial", 10, FontStyle.Bold))
                e.Graphics.DrawString(pathstr, f, Brushes.DeepSkyBlue, this.Left + 65, this.Top + 10);

            var histstr = "";
            if (Manager.agv.MovementHistory.Count > 1)
            {
                histstr = "● History : " + string.Join("▶", Manager.agv.MovementHistory.Select(t => t.Value.ToString() + $"[{t.Direction.ToString()[0]}]").ToArray());
            }
            else histstr = "● History : no data";
            using (var f = new Font("Arial", 10, FontStyle.Bold))
                e.Graphics.DrawString(histstr, f, Brushes.DeepSkyBlue, this.Left + 65, this.Top + 30);

        }

        private void DrawRFIDPoints(Graphics g)
        {
            // RFID 포인트 그리기
            foreach (var rfid in Manager.RFIDPoints)
            {
                var MarkerSize = 5;
                var half = MarkerSize / 2f;
                rfid.Bounds = new RectangleF(rfid.Location.X - half, rfid.Location.Y - half, MarkerSize, MarkerSize);

                // 종단 RFID는 특별한 색상으로 표시
                Color pointColor;
                if (rfid.IsTerminal)
                {
                    pointColor = Color.Orange; // 종단은 주황색
                }
                else if (rfid.IsRotatable)
                {
                    pointColor = Color.Yellow; // 회전 가능은 노란색
                }
                else
                {
                    pointColor = Color.Green;  // 일반은 초록색
                }

                using (var brush = new SolidBrush(pointColor))
                {
                    g.FillEllipse(brush, rfid.Bounds);
                }

                // 회전가능 항목 테두리 색상 표시
                if (rfid.IsRotatable)
                {
                    Color borderColor = Color.Yellow;
                    using (var pen = new Pen(borderColor, 2))
                    {
                        g.DrawEllipse(pen, rfid.Bounds.Expand(16, 16));
                        g.DrawEllipse(pen, rfid.Bounds.Expand(10, 10));
                        g.DrawEllipse(pen, rfid.Bounds.Expand(4, 4));
                    }
                }


                // 고정방향이 있으면 테두리 색상 표시
                if (rfid.FixedDirection.HasValue)
                {
                    Color borderColor = rfid.FixedDirection.Value == AgvDir.Forward ? Color.DeepSkyBlue : Color.Gold;
                    using (var pen = new Pen(borderColor, 2))
                    {
                        g.DrawEllipse(pen, rfid.Bounds.Expand(5, 5));
                    }
                }

                // 종단 RFID는 특별한 테두리 표시
                if (rfid.IsTerminal)
                {
                    using (var pen = new Pen(Color.Red, 3))
                    {
                        pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
                        g.DrawEllipse(pen, rfid.Bounds.Expand(8, 8));
                    }
                }

                var str = rfid.Value.ToString();
                g.DrawString(str, this.Font, Brushes.DarkGray, rfid.Bounds.X, rfid.Bounds.Y + 5);
            }


        }

        private void DrawAGV(Graphics g)
        {
            var agvsize = 30;
            var halfsize = (int)(agvsize / 2);

            // AGV의 현재 위치를 중심으로 하는 원
            var circleRect = new Rectangle(
                Manager.agv.CurrentRFID.Location.X - halfsize,
                Manager.agv.CurrentRFID.Location.Y - halfsize,
                agvsize, agvsize);


            //이동경로정보를 따라서 리프트의 위치를 표시해준다.
            if (Manager.agv.MovementHistory.Any() && Manager.agv.MovementHistory.Count > 1)
            {
                var prept = Manager.agv.MovementHistory.Skip(Manager.agv.MovementHistory.Count - 2).First();
                var lstpt = Manager.agv.MovementHistory.Last();

                RFIDPoint TargetPT = null;

                //뒤로이동하는경우라면 이전위치에 리프트가 있다.
                if (lstpt.Direction == AgvDir.Backward)
                {
                    TargetPT = prept;
                }
                else //앞으로이동한다면 이동방향과 동일하다
                {
                    //이전위치는 제거 하고 처음발견된 것을 대상으로 한다
                    TargetPT = Manager.GetNeighbors(lstpt).Where(t => t.Value != prept.Value).FirstOrDefault();
                }

                if (TargetPT != null)
                {
                    using (var p = new Pen(Color.Black, 3))
                    {
                        var circleRadius = 6;
                        var pt = DrawLineWithLength(g, p, lstpt.Location, TargetPT.Location, 25);
                        var liftRect = new RectangleF(pt.X - circleRadius, pt.Y - circleRadius, circleRadius * 2, circleRadius * 2);
                        g.FillEllipse(Brushes.Black, liftRect);

                        var liftColor = Manager.agv.IsTargetDirectionMatch ? Color.White : Color.HotPink;
                        using (var pBorder = new Pen(liftColor, 3))
                            g.DrawEllipse(pBorder, liftRect);
                    }
                }
            }



            // --- BodyAngle이 결정되지 않은 경우: 기본 방향으로 그림 ---
            Color bgcolor = Manager.agv.BatteryLevel > 80 ? Color.Lime : (Manager.agv.BatteryLevel > 60 ? Color.Gold : Color.Tomato);
            using (var circleBrush = new SolidBrush(Color.FromArgb(150, bgcolor)))
                g.FillEllipse(circleBrush, circleRect);
            using (var circlePen = new Pen(Color.Black, 4))
                g.DrawEllipse(circlePen, circleRect);

            //motor direction
            var str = Manager.agv.Current_Motor_Direction.ToString().Substring(0, 1);
            var strsize = g.MeasureString(str, this.Font);
            g.DrawString(str, this.Font, Brushes.White, circleRect, new StringFormat
            {
                Alignment = StringAlignment.Center,
                LineAlignment = StringAlignment.Center
            });


            // 과거 이동 경로 화살표 그리기
            //DrawMovementHistoryArrows(g);
        }

        private void DrawAGVMotor(Graphics g)
        {
            var agvsize = 30;
            var halfsize = (int)(agvsize / 2);

            // 삼각형 포인트 계산 (회전 중심점 0,0 기준)
            Point[] trianglePoints = new Point[3];
            var arrowSize = halfsize - 5;
            trianglePoints[0] = new Point(0, -arrowSize); // 꼭짓점
            trianglePoints[1] = new Point(-arrowSize, arrowSize); // 왼쪽 아래
            trianglePoints[2] = new Point(arrowSize, arrowSize); // 오른쪽 아래

            // 삼각형 그리기
            using (var arrowBrush = new SolidBrush(Color.FromArgb(200, Color.White)))
                g.FillPolygon(arrowBrush, trianglePoints);
            using (var arrowPen = new Pen(Color.Black, 2))
                g.DrawPolygon(arrowPen, trianglePoints);
        }

        // 과거 이동 경로를 화살표로 표시
        private void DrawMovementHistoryArrows(Graphics g)
        {
            if (Manager.agv.MovementHistory.Count < 2)
                return;

            // 최근 3개의 이동 경로 표시 (가장 오래된 것부터)
            int startIndex = Math.Max(0, Manager.agv.MovementHistory.Count - 3);

            for (int i = startIndex; i < Manager.agv.MovementHistory.Count - 1; i++)
            {
                var startRFID = Manager.agv.MovementHistory[i];
                var endRFID = Manager.agv.MovementHistory[i + 1];
                //var startPos = agv.PositionHistory[i];
                //var endPos = agv.PositionHistory[i + 1];

                // 시간에 따른 투명도 계산
                int age = Manager.agv.MovementHistory.Count - 1 - i;
                int alpha = Math.Max(50, 255 - (age * 50));

                var directConnection = Manager.rfidConnections.FirstOrDefault(c =>
                    (c.P1.Value == startRFID.Value && c.P2.Value == endRFID.Value) ||
                    (c.P1.Value == endRFID.Value && c.P2.Value == startRFID.Value));

                if (directConnection != null)
                {
                    // 직접 연결된 경우: 실선 화살표
                    Color arrowColor = (directConnection.P1.Value == startRFID.Value) ? Color.Lime : Color.Red;
                    arrowColor = Color.FromArgb(alpha, arrowColor);
                    DrawArrow(g, startRFID.Location, endRFID.Location, arrowColor, 3);
                }
                else
                {
                    // 직접 연결되지 않은 경우: 경로 탐색 후 점선 화살표 체인
                    var pathResult = Manager.CalculatePath(startRFID, endRFID);
                    if (pathResult.Success && pathResult.Path != null && pathResult.Path.Count > 1)
                    {
                        // 경로의 첫 단계 방향으로 전체 색상 결정
                        Color arrowColor = Color.Gray;
                        var firstStepEndPoint = pathResult.Path[1];
                        var firstStepEndRfidPoint = Manager.RFIDPoints.FirstOrDefault(p => p.Location == firstStepEndPoint.Location);
                        if (firstStepEndRfidPoint != null)
                        {
                            var firstStepConnection = Manager.rfidConnections.FirstOrDefault(c =>
                                (c.P1.Value == startRFID.Value && c.P2.Value == firstStepEndRfidPoint.Value) ||
                                (c.P1.Value == firstStepEndRfidPoint.Value && c.P2.Value == startRFID.Value));

                            if (firstStepConnection != null)
                            {
                                arrowColor = (firstStepConnection.P1.Value == startRFID.Value) ? Color.Lime : Color.Red;
                            }
                        }

                        arrowColor = Color.FromArgb(alpha, arrowColor);

                        // 경로의 각 세그먼트를 점선 화살표로 그리기
                        for (int j = 0; j < pathResult.Path.Count - 1; j++)
                        {
                            Point segmentStart = pathResult.Path[j].Location;
                            Point segmentEnd = pathResult.Path[j + 1].Location;
                            DrawDashedArrow(g, segmentStart, segmentEnd, arrowColor, 3);
                        }
                    }
                }
            }
        }

        // 점선 화살표 그리기 헬퍼 메서드
        private void DrawDashedArrow(Graphics g, Point start, Point end, Color color, int width)
        {
            using (var pen = new Pen(color, width))
            {
                pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;

                // 선 그리기
                g.DrawLine(pen, start, end);

                // 화살표 머리 그리기 (실선으로)
                pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Solid;
                var arrowSize = 8;
                var angle = Math.Atan2(end.Y - start.Y, end.X - start.X);
                var arrowAngle = Math.PI / 6; // 30도

                // 화살표 끝점에서 약간 뒤로 이동
                var arrowStart = new PointF(
                    end.X - (float)(arrowSize * Math.Cos(angle)),
                    end.Y - (float)(arrowSize * Math.Sin(angle))
                );

                // 화살표 날개 그리기
                var arrow1 = new PointF(
                    arrowStart.X - (float)(arrowSize * Math.Cos(angle - arrowAngle)),
                    arrowStart.Y - (float)(arrowSize * Math.Sin(angle - arrowAngle))
                );
                var arrow2 = new PointF(
                    arrowStart.X - (float)(arrowSize * Math.Cos(angle + arrowAngle)),
                    arrowStart.Y - (float)(arrowSize * Math.Sin(angle + arrowAngle))
                );

                g.DrawLine(pen, arrowStart, arrow1);
                g.DrawLine(pen, arrowStart, arrow2);
            }
        }

        // 실선 화살표 그리기 헬퍼 메서드
        private void DrawArrow(Graphics g, Point start, Point end, Color color, int width, int arrowSize = 8)
        {
            using (var pen = new Pen(color, width))
            {
                // 선 그리기
                g.DrawLine(pen, start, end);

                // 화살표 머리 그리기
                //var arrowSize = 8;
                var angle = Math.Atan2(end.Y - start.Y, end.X - start.X);
                var arrowAngle = Math.PI / 6; // 30도

                // 화살표 끝점에서 약간 뒤로 이동
                var arrowStart = new PointF(
                    end.X - (float)(arrowSize / 4f * Math.Cos(angle)),
                    end.Y - (float)(arrowSize / 4f * Math.Sin(angle))
                );

                // 화살표 날개 그리기
                var arrow1 = new PointF(
                    arrowStart.X - (float)(arrowSize * Math.Cos(angle - arrowAngle)),
                    arrowStart.Y - (float)(arrowSize * Math.Sin(angle - arrowAngle))
                );
                var arrow2 = new PointF(
                    arrowStart.X - (float)(arrowSize * Math.Cos(angle + arrowAngle)),
                    arrowStart.Y - (float)(arrowSize * Math.Sin(angle + arrowAngle))
                );

                g.DrawLine(pen, arrowStart, arrow1);
                g.DrawLine(pen, arrowStart, arrow2);
            }
        }

        //private void DrawCustomLines(Graphics g)
        //{
        //    if (customLines == null) return;
        //    foreach (var line in customLines)
        //    {
        //        using (Pen linePen = new Pen(line.LineColor, line.LineWidth))
        //        {
        //            g.DrawLine(linePen, line.StartPoint, line.EndPoint);
        //        }
        //    }
        //}

        private void DrawMapTexts(Graphics g)
        {
            if (mapTexts == null) return;
            foreach (var text in mapTexts)
            {
                var textSize = g.MeasureString(text.Text, text.Font);
                if (text.Dirty)
                {
                    text.Bounds = new RectangleF(
                        text.Location.X,
                        text.Location.Y,
                        textSize.Width,
                        textSize.Height
                    );
                    text.Dirty = false;
                }

                if (text.BackgroundColor != Color.Transparent)
                {
                    using (var brush = new SolidBrush(text.BackgroundColor))
                    {
                        g.FillRectangle(brush, text.Bounds);
                    }
                }

                using (var brush = new SolidBrush(text.TextColor))
                {
                    g.DrawString(text.Text, text.Font, brush, text.Location);
                }

                if (text == selectedText)
                {
                    using (Pen pen = new Pen(Color.Blue, 1))
                    {
                        pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
                        g.DrawRectangle(pen, text.Bounds.X, text.Bounds.Y, text.Bounds.Width, text.Bounds.Height);
                    }
                }
            }
        }

        public void ShowDesign()
        {
            using (var f = new Dialog.fMapDesign(this.Filename))
                if (f.ShowDialog() == DialogResult.OK)
                    this.LoadFromFile(this.Filename, out string ermsg);
        }

        private void DrawRFIDLines(Graphics g)
        {
            var idx = 0;
            using (Font f = new Font("arial", 4))
            {
                //foreach (var item in rfidLines)
                //{
                //    var sp = item.StartPoint;
                //    var ep = item.EndPoint;
                //    using (var p = new Pen(Color.FromArgb(50, Color.White), 10))
                //    {
                //        g.DrawLine(p, sp, ep);
                //        var x = sp.X;
                //        var y = sp.Y;
                //        g.DrawString($"{idx}", f, Brushes.Gold, x, y);

                //        x = ep.X;
                //        y = ep.Y;
                //        g.DrawString($"{idx}", f, Brushes.Pink, x, y);
                //        idx++;
                //    }
                //}
            }

            //연결정보에서 그림을 그린다.
            var didx = 0;
            foreach (var connection in Manager.rfidConnections)
            {
                didx += 1;
                var SP = Manager.RFIDPoints.FirstOrDefault(p => p.Value == connection.P1.Value)?.Location ?? Point.Empty;
                var EP = Manager.RFIDPoints.FirstOrDefault(p => p.Value == connection.P2.Value)?.Location ?? Point.Empty;

                if (SP.IsEmpty || EP.IsEmpty) continue;

                //var lcolor = didx % 2 == 0 ? Color.White : Color.LightSkyBlue;
                var lineColorP = connection.EnableP ? Color.White : Color.Red;
                var lineColorN = connection.EnableN ? Color.White : Color.Red;

                var boundBoxP = new Rectangle(SP.X + 1, SP.Y - 6, EP.X - SP.X - 2, 10);
                var boundBoxN = new Rectangle(SP.X + 1, SP.Y + 6, EP.X - SP.X - 2, 10);

                var RotatePts = GetRotatedRectangle(SP, EP, 6);
                var RotatePtsP = GetRotatedRectangle(RotatePts[0], RotatePts[1], 4);
                var RotatePtsN = GetRotatedRectangle(RotatePts[2], RotatePts[3], 4);
                var RectP = this.GetRotatedRectanglePath(RotatePtsP);
                var RectN = this.GetRotatedRectanglePath(RotatePtsN);

                //foreach (var item in RotatePts)
                //{
                //    var ballsize = 1f;
                //    var r = new Rectangle((int)(item.X - ballsize), (int)(item.Y - ballsize), (int)(ballsize * 2), (int)(ballsize * 2));
                //    g.FillEllipse(Brushes.WhiteSmoke, r);
                //}

                using(var p = new Pen(Color.FromArgb(130,Color.Red),1))
                    g.DrawPath(p, RectP);
                using (var p = new Pen(Color.FromArgb(130, Color.Blue), 1))
                    g.DrawPath(p, RectN);

                using (Pen linePen = new Pen(Color.FromArgb(30, lineColorP), 1))
                {
                    var DriveMethod = "";
                    var nulChar = "■";
                    if (connection.MoveDirectionP != null)
                    {
                        // if (DriveMethod.isEmpty() == false) DriveMethod += "|";
                        DriveMethod += ((AgvSts)connection.MoveDirectionP).ToString()[0];
                    }
                    else DriveMethod += nulChar;
                    if (connection.MoveSpeedP != null)
                    {
                        // if (DriveMethod.isEmpty() == false) DriveMethod += "|";
                        DriveMethod += ((AgvSpeed)connection.MoveSpeedP).ToString()[0];
                    }
                    else DriveMethod += nulChar;
                    if (connection.LiftDirectionP != null)
                    {
                        //if (DriveMethod.isEmpty() == false) DriveMethod += "|";
                        DriveMethod += ((AgvSts)connection.LiftDirectionP).ToString()[0];
                    }
                    else DriveMethod += nulChar;

                    //g.DrawLine(linePen, SP, EP);
                    //g.DrawLine(Pens.SlateGray, SP, EP);
                    //g.DrawRectangle(linePen, boundBoxP);

                    if (connection.EnableP == false)
                    {
                        g.DrawLine(linePen, boundBoxP.Left, boundBoxP.Top, boundBoxP.Right, boundBoxP.Bottom);
                        g.DrawLine(linePen, boundBoxP.Right, boundBoxP.Top, boundBoxP.Left, boundBoxP.Bottom);
                    }
                    else
                    {
                        //DrawArrow(g,
                        //new Point(boundBoxP.X, (int)(boundBoxP.Y + boundBoxP.Height / 2f)),
                        //new Point(boundBoxP.Right, (int)(boundBoxP.Y + boundBoxP.Height / 2f)),
                        //lineColorP, 1);
                    }

                    if (DriveMethod.isEmpty() == false && DriveMethod.Equals($"{nulChar}{nulChar}{nulChar}") == false)
                        using (Font f = new Font("Consolas", 5, FontStyle.Bold))
                            g.DrawString(DriveMethod, f, Brushes.Gold, boundBoxP, new StringFormat
                            {
                                Alignment = StringAlignment.Center,
                                LineAlignment = StringAlignment.Center,
                            });

                }


                using (Pen linePen = new Pen(Color.FromArgb(30, lineColorN), 1))
                {

                    var DriveMethod = "";
                    var nulChar = "■";
                    if (connection.MoveDirectionN != null)
                    {
                        //if (DriveMethod.isEmpty() == false) DriveMethod += "|";
                        DriveMethod += ((AgvSts)connection.MoveDirectionN).ToString()[0];
                    }
                    else DriveMethod += nulChar;
                    if (connection.MoveSpeedN != null)
                    {
                        //if (DriveMethod.isEmpty() == false) DriveMethod += "|";
                        DriveMethod += ((AgvSpeed)connection.MoveSpeedN).ToString()[0];
                    }
                    else DriveMethod += nulChar;
                    if (connection.LiftDirectionN != null)
                    {
                        //if (DriveMethod.isEmpty() == false) DriveMethod += "|";
                        DriveMethod += ((AgvSts)connection.LiftDirectionN).ToString()[0];
                    }
                    else DriveMethod += nulChar;


                    //g.DrawLine(linePen, SP, EP);
                    //g.DrawLine(Pens.SlateGray, SP, EP);
                    //g.DrawRectangle(linePen, boundBoxN);
                    if (connection.EnableP == false)
                    {
                        g.DrawLine(linePen, boundBoxN.Left, boundBoxN.Top, boundBoxN.Right, boundBoxN.Bottom);
                        g.DrawLine(linePen, boundBoxN.Right, boundBoxN.Top, boundBoxN.Left, boundBoxN.Bottom);
                    }
                    else
                    {
                  //      DrawArrow(g,
                  //new Point(boundBoxN.Right, (int)(boundBoxN.Y + boundBoxN.Height / 2f)),
                  //new Point(boundBoxN.X, (int)(boundBoxN.Y + boundBoxN.Height / 2f)),
                  //lineColorN, 1);
                    }

                    if (DriveMethod.isEmpty() == false && DriveMethod.Equals($"{nulChar}{nulChar}{nulChar}") == false)
                        using (Font f = new Font("Consolas", 5, FontStyle.Bold))
                            g.DrawString(DriveMethod, f, Brushes.Gold, boundBoxN, new StringFormat
                            {
                                Alignment = StringAlignment.Center,
                                LineAlignment = StringAlignment.Center,
                            });

                }

                //g.DrawLine(Pens.SlateGray, SP, EP);
            }

            // 미리보기 라인 그리기
            if (previewStartPoint.HasValue && isDrawingRFIDLine)
            {
                using (Pen previewPen = new Pen(Color.FromArgb(180, Color.Green), 2))
                {
                    previewPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
                    var currentMapPosition = ScreenToMap(currentMousePosition);
                    g.DrawLine(previewPen, previewStartPoint.Value, currentMapPosition);
                }
            }
        }

        private void DrawPath(Graphics g)
        {
            if (Manager.agv.MainPath == null || Manager.agv.MainPath.Count < 2)
                return;

            Color pathColor = Color.FromArgb(150, Color.White);
            if (Manager.agv.SubPath != null && Manager.agv.SubPath.Any())
                pathColor = Color.FromArgb(70, Color.LightGray);
            int pathWidth = 10;

            using (Pen pathPen = new Pen(pathColor, pathWidth))
            {
                pathPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
                for (int i = 0; i < Manager.agv.MainPath.Count - 1; i++)
                {
                    g.DrawLine(pathPen, Manager.agv.MainPath[i].Location, Manager.agv.MainPath[i + 1].Location);
                }
            }

            if (Manager.agv.SubPath == null || Manager.agv.SubPath.Count < 2)
                return;

            pathColor = Color.FromArgb(150, Color.DeepSkyBlue);
            pathWidth = 10;

            using (Pen pathPen = new Pen(pathColor, pathWidth))
            {
                pathPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
                for (int i = 0; i < Manager.agv.SubPath.Count - 1; i++)
                {
                    g.DrawLine(pathPen, Manager.agv.SubPath[i].Location, Manager.agv.SubPath[i + 1].Location);
                }
            }
        }

        public List<RFIDPoint> GetRFIDPoints()
        {
            return Manager.RFIDPoints;
        }

        public List<RFIDLine> GetRFIDLines()
        {
            return rfidLines;
        }

        public void SetRFIDLines(List<RFIDLine> lines)
        {
            rfidLines = lines;
            this.Invalidate();
        }

        public void AddRFIDConnection(RFIDConnection item)
        {
            //item 의 rfid를 찾아서 연결해야함
            item.P1 = Manager.RFIDPoints.Where(t => t.Value == item.P1.Value).FirstOrDefault();
            item.P2 = Manager.RFIDPoints.Where(t => t.Value == item.P2.Value).FirstOrDefault();

            Manager.rfidConnections.Add(item);
            this.Invalidate();
        }


        //public void AddRFIDLine(Point startPoint, Point endPoint)
        //{
        //    // 시작점과 끝점 사이의 모든 RFID 포인트 찾기
        //    var allPoints = new List<(RFIDPoint Point, float Distance)>();
        //    var lineVector = new Point(endPoint.X - startPoint.X, endPoint.Y - startPoint.Y);
        //    var lineLength = (float)Math.Sqrt(lineVector.X * lineVector.X + lineVector.Y * lineVector.Y);

        //    foreach (var rfid in Manager.RFIDPoints)
        //    {
        //        if (rfid.Location == startPoint || rfid.Location == endPoint)
        //            continue;

        //        // RFID 포인트가 선 위에 있는지 확인
        //        var pointVector = new Point(rfid.Location.X - startPoint.X, rfid.Location.Y - startPoint.Y);
        //        var dotProduct = pointVector.X * lineVector.X + pointVector.Y * lineVector.Y;
        //        var projectionLength = dotProduct / (lineLength * lineLength);

        //        if (projectionLength >= 0 && projectionLength <= 1)
        //        {
        //            // 선과 RFID 포인트 사이의 거리 계산
        //            var distance = GetDistanceToLine(rfid.Location, startPoint, endPoint);
        //            if (distance <= SNAP_DISTANCE)
        //            {
        //                allPoints.Add((rfid, projectionLength));
        //            }
        //        }
        //    }

        //    // 시작점에서 끝점 방향으로 정렬
        //    allPoints.Sort((a, b) => a.Distance.CompareTo(b.Distance));

        //    // 모든 연결 정보를 포함하는 RFID 라인 생성
        //    var line = new RFIDLine
        //    {
        //        StartPoint = startPoint,
        //        EndPoint = endPoint,
        //    };


        //    rfidLines.Add(line);
        //    this.Invalidate();
        //}

        public void ClearMap()
        {
            Manager.RFIDPoints.Clear();
            Manager.rfidConnections.Clear();

            mapTexts.Clear();
            // customLines.Clear();
            rfidLines.Clear();

            // 선택 상태도 초기화
            selectedText = null;
            selectedLine = null;
            selectedRFID = null;
            selectedRFIDLine = null;
            draggingPoint = null;

            // 미리보기 상태도 초기화
            previewStartPoint = null;

            // 화면 갱신
            this.Invalidate();
        }

        public void AddRFIDPoint(Point mapLocation, ushort rfidValue)
        {
            var rfidPoint = new RFIDPoint
            {
                Location = mapLocation,
                Value = rfidValue
            };

            Manager.RFIDPoints.Add(rfidPoint);
            this.Invalidate();
        }

        public void SaveToFile(string filename)
        {
            var lines = new List<string>();

            // RFID 포인트 저장
            lines.Add("[RFID_POINTS]");
            foreach (var point in Manager.RFIDPoints)
            {
                lines.Add($"{point.Location.X},{point.Location.Y},{point.Value},{point.IsRotatable},{point.FixedDirection},{point.IsTerminal}");
            }

            //// RFID 라인 저장
            //lines.Add("[RFID_LINES]");
            //foreach (var connection in Manager.rfidConnections)
            //{
            //    var startPoint = Manager.RFIDPoints.First(p => p.Value == connection.P1.Value).Location;
            //    var endPoint = Manager.RFIDPoints.First(p => p.Value == connection.P2.Value).Location;
            //    lines.Add($"{startPoint.X},{startPoint.Y},{endPoint.X},{endPoint.Y}," +
            //        $"{connection.P1},{connection.P2},false,false,0");
            //}

            // RFID 연결 정보
            lines.Add("[RFID_CONNECTION]");
            foreach (var connection in Manager.rfidConnections)
            {
                lines.Add(connection.DataFileString);
            }

            // 텍스트 저장
            lines.Add("[MAP_TEXTS]");
            foreach (var text in mapTexts)
            {
                lines.Add($"{text.Location.X},{text.Location.Y},{text.TextColor.ToArgb()},{text.BackgroundColor.ToArgb()},{text.Font.Name},{text.Font.Size},{text.Text}");
            }

            //// 커스텀 라인 저장
            //lines.Add("[CUSTOM_LINES]");
            //foreach (var line in customLines)
            //{
            //    lines.Add($"{line.StartPoint.X},{line.StartPoint.Y},{line.EndPoint.X},{line.EndPoint.Y},{line.LineColor.ToArgb()},{line.LineWidth}");
            //}


            File.WriteAllLines(filename, lines);
            this.Filename = filename;
        }

        public bool LoadFromFile(string filename, out string message)
        {
            this.Filename = filename;
            message = string.Empty;
            ClearMap();

            var lines = File.ReadAllLines(filename);
            var section = "";

            var sb = new System.Text.StringBuilder();

            foreach (var line in lines)
            {
                if (line.StartsWith("[") && line.EndsWith("]"))
                {
                    section = line;
                    continue;
                }

                switch (section)
                {
                    case "[RFID_POINTS]":
                        var rfidParts = line.Split(',');
                        if (rfidParts.Length >= 3)
                        {
                            var validX = int.TryParse(rfidParts[0], out int valX);
                            var validY = int.TryParse(rfidParts[1], out int valY);
                            var validN = ushort.TryParse(rfidParts[2], out ushort valRfid);

                            if (validX && validY && validN)
                            {
                                if (Manager.RFIDPoints.Where(t => t.Value == valRfid).Any())
                                {
                                    //이미존재한다
                                    var newvalue =
                                    sb.AppendLine($"rfid중복{valRfid}");
                                }

                                var rfidPoint = new RFIDPoint
                                {
                                    Location = new Point(valX, valY),
                                    Value = valRfid
                                };

                                // 추가 속성 로드 (기본값 처리)
                                if (rfidParts.Length >= 4)
                                {
                                    bool isRotatable;
                                    bool.TryParse(rfidParts[3], out isRotatable);
                                    rfidPoint.IsRotatable = isRotatable;
                                }
                                if (rfidParts.Length >= 5 && !string.IsNullOrEmpty(rfidParts[4]))
                                    rfidPoint.FixedDirection = (AgvDir)Enum.Parse(typeof(AgvDir), rfidParts[4]);
                                if (rfidParts.Length >= 6)
                                {
                                    bool isTerminal;
                                    bool.TryParse(rfidParts[5], out isTerminal);
                                    rfidPoint.IsTerminal = isTerminal;
                                }

                                Manager.RFIDPoints.Add(rfidPoint);
                            }
                            else sb.AppendLine($"[{section}] {line}");
                        }
                        break;

                    case "[RFID_CONNECTION]":
                        AddRFIDConnection(new RFIDConnection(line));
                        break;


                    //case "[RFID_LINES]":
                    //    var rfidLineParts = line.Split(',');
                    //    if (rfidLineParts.Length >= 8)
                    //    {
                    //        AddRFIDLine(
                    //            new Point(int.Parse(rfidLineParts[0]), int.Parse(rfidLineParts[1])),
                    //            new Point(int.Parse(rfidLineParts[2]), int.Parse(rfidLineParts[3]))
                    //        );
                    //    }
                    //    break;

                    case "[MAP_TEXTS]":
                        var textParts = line.Split(',');
                        if (textParts.Length >= 7)
                        {
                            var text = new MapText
                            {
                                Location = new Point(int.Parse(textParts[0]), int.Parse(textParts[1])),
                                TextColor = Color.FromArgb(int.Parse(textParts[2])),
                                BackgroundColor = Color.FromArgb(int.Parse(textParts[3])),
                                Font = new Font(textParts[4], float.Parse(textParts[5])),
                                Text = string.Join(",", textParts.Skip(6)) // 텍스트에 쉼표가 포함될 수 있으므로
                            };
                            mapTexts.Add(text);
                        }
                        break;

                        //case "[CUSTOM_LINES]":
                        //    var customLineParts = line.Split(',');
                        //    if (customLineParts.Length >= 6)
                        //    {
                        //        var customLine = new CustomLine
                        //        {
                        //            StartPoint = new Point(int.Parse(customLineParts[0]), int.Parse(customLineParts[1])),
                        //            EndPoint = new Point(int.Parse(customLineParts[2]), int.Parse(customLineParts[3])),
                        //            LineColor = Color.FromArgb(int.Parse(customLineParts[4])),
                        //            LineWidth = int.Parse(customLineParts[5])
                        //        };
                        //        customLines.Add(customLine);
                        //    }
                        //    break;
                }
            }

            // RFID 연결 정보 처리
            ProcessRFIDConnections();

            this.Invalidate();

            message = sb.ToString();
            return true;
        }

        private void ProcessRFIDConnections()
        {
            return;
            Manager.rfidConnections.Clear();
            var connectionSet = new HashSet<string>();

            foreach (var line in rfidLines)
            {
                var start = line.StartPoint;
                var end = line.EndPoint;

                // 1. 선 위의 모든 RFID 포인트(시작, 끝 포함)를 projectionRatio로 정렬
                var pointsOnThisLine = Manager.RFIDPoints
                    .Where(p => IsPointOnLine(p.Location, start, end, 10f)) // 오차 허용치 넉넉히
                    .Select(p => new
                    {
                        RFID = p.Value,
                        Ratio = GetProjectionRatio(p.Location, start, end)
                    })
                    .ToList();

                //// 2. 시작/끝 RFID가 목록에 없으면 강제로 추가
                //if (!pointsOnThisLine.Any(p => p.RFID == line.StartRFID))
                //    pointsOnThisLine.Add(new { RFID = line.StartRFID, Ratio = 0f });
                //if (!pointsOnThisLine.Any(p => p.RFID == line.EndRFID))
                //    pointsOnThisLine.Add(new { RFID = line.EndRFID, Ratio = 1f });

                // 3. 정렬
                pointsOnThisLine = pointsOnThisLine.OrderBy(p => p.Ratio).ToList();

                // 4. 순서대로 1:1 연결
                for (int i = 0; i < pointsOnThisLine.Count - 1; i++)
                {
                    var from = pointsOnThisLine[i].RFID;
                    var to = pointsOnThisLine[i + 1].RFID;
                    var key = $"{Math.Min(from, to)}_{Math.Max(from, to)}";
                    if (connectionSet.Contains(key)) continue;

                    var fromItem = Manager.RFIDPoints.FirstOrDefault(p => p.Value == from);
                    var toItem = Manager.RFIDPoints.FirstOrDefault(p => p.Value == to);
                    var fromPt = fromItem?.Location ?? line.StartPoint;
                    var toPt = toItem?.Location ?? line.EndPoint;

                    Manager.rfidConnections.Add(new RFIDConnection
                    {
                        P1 = fromItem,
                        P2 = toItem,
                    });
                    connectionSet.Add(key);
                }
            }
        }



        // tolerance 인자를 받는 IsPointOnLine
        private bool IsPointOnLine(Point point, Point lineStart, Point lineEnd, float tolerance = 10.0f)
        {
            var distance = GetDistanceToLine(point, lineStart, lineEnd);
            if (distance > tolerance) return false;

            var projectionRatio = Math.Round(GetProjectionRatio(point, lineStart, lineEnd), 2);
            return projectionRatio >= 0 && projectionRatio <= 1.0;
        }

        private void DeleteNearbyRFIDLine(Point clickPoint)
        {
            const float DELETE_DISTANCE = 10.0f; // 클릭 지점으로부터의 허용 거리
            RFIDLine lineToDelete = null;
            float minDistance = float.MaxValue;

            foreach (var line in rfidLines)
            {
                float distance = GetDistanceToLine(clickPoint, line.StartPoint, line.EndPoint);
                if (distance < DELETE_DISTANCE && distance < minDistance)
                {
                    minDistance = distance;
                    lineToDelete = line;
                }
            }

            if (lineToDelete != null)
            {
                rfidLines.Remove(lineToDelete);
                this.Invalidate();
            }
        }

        private void ZoomIn()
        {
            zoom *= 1.2f;
            zoom = Math.Min(10.0f, zoom);
            this.Invalidate();
        }

        private void ZoomOut()
        {
            zoom /= 1.2f;
            zoom = Math.Max(0.1f, zoom);
            this.Invalidate();
        }

        private void ResetZoom()
        {
            zoom = 1.0f;
            offset = PointF.Empty;
            this.Invalidate();
        }

        private void DrawToolbarButton(Graphics g, Rectangle rect, string text, bool isHovering)
        {
            var color1 = isHovering ? Color.LightSkyBlue : Color.White;
            var color2 = isHovering ? Color.DeepSkyBlue : Color.WhiteSmoke;
            using (var brush = new LinearGradientBrush(rect, color1, color2, LinearGradientMode.Vertical))
            using (var pen = new Pen(Color.Gray))
            using (var font = new Font("Tahoma", 9, FontStyle.Bold))
            using (var format = new StringFormat { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center })
            {
                g.FillRectangle(Brushes.LightGray, rect.X + 2, rect.Y + 2, rect.Width, rect.Height);
                g.FillRectangle(brush, rect);
                g.DrawRectangle(pen, rect);
                g.DrawString(text, font, Brushes.Black, rect, format);
            }
        }

        /// <summary>
        /// 목적지에 깃발 표시
        /// </summary>
        /// <param name="g"></param>
        private void DrawTargetFlag(Graphics g)
        {
            //대상이없다면 진행하지 않습니다
            if (Manager.agv.TargetRFID.IsEmpty) return;

            // 바닥에 흰색 원 그리기
            using (var baseBrush = new SolidBrush(Color.Red))
            using (var basePen = new Pen(Color.Black, 1))
            {
                var baseSize = 8;
                g.FillEllipse(baseBrush,
                   Manager.agv.TargetRFID.Location.X - baseSize / 2,
                   Manager.agv.TargetRFID.Location.Y - baseSize / 2,
                    baseSize, baseSize);
                g.DrawEllipse(basePen,
                   Manager.agv.TargetRFID.Location.X - baseSize / 2,
                   Manager.agv.TargetRFID.Location.Y - baseSize / 2,
                    baseSize, baseSize);
            }

            // 깃대 그리기 (길이를 2/3로 줄임)
            using (var polePen = new Pen(Color.Brown, 3))
            {
                var poleLength = 27; // 40 * 2/3 ≈ 27
                g.DrawLine(polePen,
                    Manager.agv.TargetRFID.Location.X,
                    Manager.agv.TargetRFID.Location.Y,
                    Manager.agv.TargetRFID.Location.X,
                    Manager.agv.TargetRFID.Location.Y - poleLength);
            }

            // 깃발 그리기
            Point[] flagPoints = new Point[3];
            flagPoints[0] = new Point(Manager.agv.TargetRFID.Location.X, Manager.agv.TargetRFID.Location.Y - 27); // 깃대 길이에 맞춤
            flagPoints[1] = new Point(Manager.agv.TargetRFID.Location.X + 20, Manager.agv.TargetRFID.Location.Y - 22);
            flagPoints[2] = new Point(Manager.agv.TargetRFID.Location.X, Manager.agv.TargetRFID.Location.Y - 17);

            using (var flagBrush = new SolidBrush(Color.Red))
            using (var flagPen = new Pen(Color.DarkRed, 1))
            {
                g.FillPolygon(flagBrush, flagPoints);
                g.DrawPolygon(flagPen, flagPoints);
            }
        }



        // AGV 다음행동 예측 함수

        #endregion


        #region Rotate Rectangle Helper


        public System.Drawing.Drawing2D.GraphicsPath GetRotatedRectanglePath(PointF[] pts)
        {
            var p = new GraphicsPath();
            p.AddLine(pts[0], pts[1]);
            p.AddLine(pts[1], pts[2]);
            p.AddLine(pts[2], pts[3]);
            p.AddLine(pts[0], pts[3]);
            return p;
        }

        /// <summary>
        /// 두 점을 연결하는 선분을 중심으로 하는 회전된 사각형의 네 꼭지점을 구합니다.
        /// </summary>
        /// <param name="point1">첫 번째 점</param>
        /// <param name="point2">두 번째 점</param>
        /// <param name="height">사각형의 세로 길이 (기본값: 5)</param>
        /// <returns>사각형의 네 꼭지점 배열 (시계방향 순서)</returns>
        public PointF[] GetRotatedRectangle(PointF point1, PointF point2, float height = 5f)
        {
            // 두 점 사이의 거리 (가로 길이)
            float width = (float)Math.Sqrt(Math.Pow(point2.X - point1.X, 2) + Math.Pow(point2.Y - point1.Y, 2));

            // 두 점을 연결하는 선분의 각도 (라디안)
            float angle = (float)Math.Atan2(point2.Y - point1.Y, point2.X - point1.X);

            // 선분의 중점
            PointF center = new PointF((point1.X + point2.X) / 2f, (point1.Y + point2.Y) / 2f);

            // 사각형의 반 크기
            float halfWidth = width / 2f;
            float halfHeight = height / 2f;

            // 회전하지 않은 상태의 사각형 꼭지점들 (중점 기준)
            PointF[] localPoints = new PointF[]
            {
            new PointF(-halfWidth, -halfHeight), // 좌상단
            new PointF(halfWidth, -halfHeight),  // 우상단
            new PointF(halfWidth, halfHeight),   // 우하단
            new PointF(-halfWidth, halfHeight)   // 좌하단
            };

            // 회전 변환 적용
            PointF[] rotatedPoints = new PointF[4];
            float cos = (float)Math.Cos(angle);
            float sin = (float)Math.Sin(angle);

            for (int i = 0; i < 4; i++)
            {
                // 회전 변환 공식 적용
                float rotatedX = localPoints[i].X * cos - localPoints[i].Y * sin;
                float rotatedY = localPoints[i].X * sin + localPoints[i].Y * cos;

                // 중점으로 이동
                rotatedPoints[i] = new PointF(center.X + rotatedX, center.Y + rotatedY);
            }

            return rotatedPoints;
        }

        /// <summary>
        /// 오버로드: 개별 좌표값으로 입력받는 버전
        /// </summary>
        public PointF[] GetRotatedRectangle(float x1, float y1, float x2, float y2, float height = 5f)
        {
            return GetRotatedRectangle(new PointF(x1, y1), new PointF(x2, y2), height);
        }

        /// <summary>
        /// 회전된 사각형의 경계 사각형(Bounding Rectangle)을 구합니다.
        /// </summary>
        public static RectangleF GetBoundingRectangle(PointF[] rotatedPoints)
        {
            float minX = float.MaxValue;
            float minY = float.MaxValue;
            float maxX = float.MinValue;
            float maxY = float.MinValue;

            foreach (var point in rotatedPoints)
            {
                minX = Math.Min(minX, point.X);
                minY = Math.Min(minY, point.Y);
                maxX = Math.Max(maxX, point.X);
                maxY = Math.Max(maxY, point.Y);
            }

            return new RectangleF(minX, minY, maxX - minX, maxY - minY);
        }

        #endregion

        /// <summary>
        /// 마지막으로 읽힌 연재위치에 해당하는 RFID 포인트값을 반환 합니다
        /// </summary>
        public RFIDPoint GetCurrentPosition
        {
            get
            {
                return Manager.agv.CurrentRFID;
            }
        }

        #region 좌표 변환 및 계산


        // 화면 좌표를 실제 맵 좌표로 변환
        public Point ScreenToMap(Point screenPoint)
        {
            int adjustedX = screenPoint.X;
            return new Point(
                (int)((adjustedX - offset.X) / zoom),
                (int)((screenPoint.Y - offset.Y) / zoom)
            );
        }


        private float GetProjectionRatio(Point point, Point lineStart, Point lineEnd)
        {
            float lineLength = Manager.GetDistance(lineStart, lineEnd);
            if (lineLength == 0) return 0;

            return ((point.X - lineStart.X) * (lineEnd.X - lineStart.X) +
                    (point.Y - lineStart.Y) * (lineEnd.Y - lineStart.Y)) / (lineLength * lineLength);
        }


        private float GetDistanceToLine(Point point, Point lineStart, Point lineEnd)
        {
            float lineLength = Manager.GetDistance(lineStart, lineEnd);
            if (lineLength == 0) return Manager.GetDistance(point, lineStart);

            float t = ((point.X - lineStart.X) * (lineEnd.X - lineStart.X) +
                       (point.Y - lineStart.Y) * (lineEnd.Y - lineStart.Y)) / (lineLength * lineLength);

            t = Math.Max(0, Math.Min(1, t));

            float projectionX = lineStart.X + t * (lineEnd.X - lineStart.X);
            float projectionY = lineStart.Y + t * (lineEnd.Y - lineStart.Y);

            return Manager.GetDistance(point, new Point((int)projectionX, (int)projectionY));
        }
        #endregion
    }
}
