using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Inspectron.Hawkeye.Vision.Geometry;
using OpenCvSharp;
using OpenCvSharp.Extensions;
using Point = System.Drawing.Point;

namespace Lindt.Candybox.Demo
{
    public partial class MaskControl : UserControl
    {
        private Bitmap _backBuffer;
        private GrahamConvexHull _hull;
        private List<VectorLine> _vectorLines;
        private EditMode _currentMode = EditMode.Brush;
        private bool _isDrawingLine = false;
        private Point _lineStartPoint;
        private VectorLine _selectedLine;
        private bool _isDraggingLine = false;
        private bool _isDraggingVertex = false;
        private bool _isDraggingStartVertex = false;
        private Point _dragOffset;

        public MaskControl()
        {
            InitializeComponent();
            _backBuffer = new Bitmap(1800, 1408);
            _hull = new GrahamConvexHull();
            _vectorLines = new List<VectorLine>();
        }

        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        public EditMode CurrentMode 
        { 
            get => _currentMode; 
            set 
            { 
                _currentMode = value;
                // Clear any ongoing operations when mode changes
                _isDrawingLine = false;
                _isDraggingLine = false;
                _isDraggingVertex = false;
                _selectedLine = null;
                Invalidate();
            } 
        }

        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        public List<VectorLine> VectorLines
        {
            get => _vectorLines;
            set => _vectorLines = value;
        }

        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        public Bitmap Mask { get; set; } = new Bitmap(1800, 1408);
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        public Bitmap Original { get; set; } = new Bitmap(1800, 1408);

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

        private bool _isDown = false;
        private Point _lastMouse;

        protected override void OnMouseDown(MouseEventArgs e)
        {
            _isDown = true;
            _lastMouse = ConvertCoordinates(new Point(e.X, e.Y));

            switch (_currentMode)
            {
                case EditMode.DrawLine:
                    if (!_isDrawingLine)
                    {
                        _isDrawingLine = true;
                        _lineStartPoint = _lastMouse;
                    }
                    else
                    {
                        // Finish drawing line
                        var newLine = new VectorLine(_lineStartPoint, _lastMouse);
                        _vectorLines.Add(newLine);
                        _isDrawingLine = false;
                        Invalidate();
                    }
                    break;

                case EditMode.RemoveLine:
                    var lineToRemove = _vectorLines.FirstOrDefault(line => line.IsPointOnLine(_lastMouse));
                    if (lineToRemove != null)
                    {
                        _vectorLines.Remove(lineToRemove);
                        Invalidate();
                    }
                    break;

                case EditMode.MoveLine:
                    _selectedLine = _vectorLines.FirstOrDefault(line => line.IsPointOnLine(_lastMouse));
                    if (_selectedLine != null)
                    {
                        _isDraggingLine = true;
                        _dragOffset = new Point(_lastMouse.X - _selectedLine.StartPoint.X, _lastMouse.Y - _selectedLine.StartPoint.Y);
                    }
                    break;

                case EditMode.MoveVertices:
                    foreach (var line in _vectorLines)
                    {
                        if (line.IsPointNearVertex(_lastMouse, out bool isStartVertex))
                        {
                            _selectedLine = line;
                            _isDraggingVertex = true;
                            _isDraggingStartVertex = isStartVertex;
                            break;
                        }
                    }
                    break;
            }
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            _isDown = false;
            _isDraggingLine = false;
            _isDraggingVertex = false;
        }

        private Pen _pen = new Pen(Color.White, 30);
        private Pen _penBlack = new Pen(Color.Black, 30);
        
        protected override void OnMouseMove(MouseEventArgs e)
        {
            var currentPoint = ConvertCoordinates(new Point(e.X, e.Y));

            // Handle brush mode (existing functionality)
            if (_currentMode == EditMode.Brush)
            {
                if (_isDown && e.Button == MouseButtons.Left)
                {
                    using (Graphics g = Graphics.FromImage(Mask))
                    {
                        g.DrawLine(_pen, currentPoint.X, currentPoint.Y, _lastMouse.X, _lastMouse.Y);
                        g.FillEllipse(Brushes.White, _lastMouse.X - 15, _lastMouse.Y - 15, 30, 30);
                    }
                }
                if (_isDown && e.Button == MouseButtons.Right)
                {
                    using (Graphics g = Graphics.FromImage(Mask))
                    {
                        g.DrawLine(_penBlack, currentPoint.X, currentPoint.Y, _lastMouse.X, _lastMouse.Y);
                        g.FillEllipse(Brushes.Black, _lastMouse.X - 15, _lastMouse.Y - 15, 30, 30);
                    }
                }
            }
            // Handle line dragging
            else if (_currentMode == EditMode.MoveLine && _isDraggingLine && _selectedLine != null)
            {
                var deltaX = currentPoint.X - _lastMouse.X;
                var deltaY = currentPoint.Y - _lastMouse.Y;
                
                _selectedLine.StartPoint = new Point(_selectedLine.StartPoint.X + deltaX, _selectedLine.StartPoint.Y + deltaY);
                _selectedLine.EndPoint = new Point(_selectedLine.EndPoint.X + deltaX, _selectedLine.EndPoint.Y + deltaY);
            }
            // Handle vertex dragging
            else if (_currentMode == EditMode.MoveVertices && _isDraggingVertex && _selectedLine != null)
            {
                if (_isDraggingStartVertex)
                {
                    _selectedLine.StartPoint = currentPoint;
                }
                else
                {
                    _selectedLine.EndPoint = currentPoint;
                }
            }

            _lastMouse = currentPoint;
            Invalidate();
        }

        private Pen _contourPen = new Pen(Color.GreenYellow, 3);
        private Pen _vectorLinePen = new Pen(Color.DeepPink, 12);
        private Brush _vertexBrush = new SolidBrush(Color.Orange);

        
        protected override void OnPaint(PaintEventArgs e)
        {
            
        

            OnPaintInternal(e);
        }

        void OnPaintInternal(PaintEventArgs e)
        {
            var mat = Mask.ToMat().CvtColor(ColorConversionCodes.BGR2GRAY);
            var contours = mat.FindContoursAsArray(RetrievalModes.External, ContourApproximationModes.ApproxNone);
            LastContours = contours;

            using (Graphics g = Graphics.FromImage(_backBuffer))
            {
                g.Clear(Color.Transparent);
                g.DrawImage(Original, 0, 0);

                // Draw contours
                for (int i = 0; i < contours.Length; i++)
                {
                    if (contours[i].Length > 1)
                    {
                        var points = contours[i].Select(x => new System.Drawing.Point(x.X, x.Y)).ToArray();
                        g.DrawPolygon(_contourPen, points);
                    }
                }

                // Draw vector lines
                foreach (var line in _vectorLines)
                {
                    // Convert coordinates back to display coordinates
                    var startDisplay = new Point(line.StartPoint.X, line.StartPoint.Y);
                    var endDisplay = new Point(line.EndPoint.X, line.EndPoint.Y);

                    g.DrawLine(_vectorLinePen, startDisplay, endDisplay);

                    // Draw vertices when in move vertices mode
                    if (_currentMode == EditMode.MoveVertices)
                    {
                        g.FillEllipse(_vertexBrush, startDisplay.X - 5, startDisplay.Y - 5, 10, 10);
                        g.FillEllipse(_vertexBrush, endDisplay.X - 5, endDisplay.Y - 5, 10, 10);
                    }
                }

                // Draw temporary line when drawing
                if (_currentMode == EditMode.DrawLine && _isDrawingLine)
                {
                    var startDisplay = new Point(_lineStartPoint.X, _lineStartPoint.Y);
                    var endDisplay = new Point(_lastMouse.X, _lastMouse.Y);
                    using (var tempPen = new Pen(Color.DeepPink, 12) { DashStyle = System.Drawing.Drawing2D.DashStyle.Dash })
                    {
                        g.DrawLine(tempPen, startDisplay, endDisplay);
                    }
                }

                // Draw mouse cursor indicator for brush mode
                if (_currentMode == EditMode.Brush)
                {
                    var cursorDisplay = new Point(_lastMouse.X, _lastMouse.Y);
                    g.DrawEllipse(Pens.Gray, cursorDisplay.X - 15, cursorDisplay.Y - 15, 30, 30);
                }
            }

            e.Graphics.DrawImage(_backBuffer, 0, 0, Width, Height);
        }

        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
        public OpenCvSharp.Point[][] LastContours { get; set; }

        Point ConvertCoordinates(Point coords)
        {
            return new Point(coords.X * 2, coords.Y * 2);
        }

        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);
        }
    }
    public enum EditMode
    {
        Brush,
        DrawLine,
        RemoveLine,
        MoveLine,
        MoveVertices
    }

    public class VectorLine
    {
        public Point StartPoint { get; set; }
        public Point EndPoint { get; set; }
        public bool Selected { get; set; }

        public VectorLine()
        {
        }

        public VectorLine Scale(float sX, float sY)
        {
            return new VectorLine(new Point((int)(StartPoint.X * sX), (int)(StartPoint.Y * sY)),
                                  new Point((int)(EndPoint.X * sX), (int)(EndPoint.Y * sY)));
        }

        public VectorLine Translate(int tX, int tY)
        {
            return new VectorLine(new Point(StartPoint.X + tX, StartPoint.Y + tY),
                                  new Point(EndPoint.X + tX, EndPoint.Y + tY));
        }

        public VectorLine Translate(Point point)
        {
            return Translate(point.X, point.Y);
        }

        public VectorLine Sub(int tX, int tY)
        {
            return new VectorLine(new Point(StartPoint.X - tX, StartPoint.Y - tY),
                                  new Point(EndPoint.X - tX, EndPoint.Y - tY));
        }

        public VectorLine Sub(Point point)
        {
            return Sub(point.X, point.Y);
        }


        public VectorLine(Point start, Point end)
        {
            StartPoint = start;
            EndPoint = end;
            Selected = false;
        }

        public bool IsPointOnLine(Point point, int tolerance = 10)
        {
            // Calculate distance from point to line segment
            double A = point.X - StartPoint.X;
            double B = point.Y - StartPoint.Y;
            double C = EndPoint.X - StartPoint.X;
            double D = EndPoint.Y - StartPoint.Y;

            double dot = A * C + B * D;
            double lenSq = C * C + D * D;

            if (lenSq == 0) return Math.Sqrt(A * A + B * B) <= tolerance;

            double param = dot / lenSq;

            double xx, yy;
            if (param < 0)
            {
                xx = StartPoint.X;
                yy = StartPoint.Y;
            }
            else if (param > 1)
            {
                xx = EndPoint.X;
                yy = EndPoint.Y;
            }
            else
            {
                xx = StartPoint.X + param * C;
                yy = StartPoint.Y + param * D;
            }

            double dx = point.X - xx;
            double dy = point.Y - yy;
            return Math.Sqrt(dx * dx + dy * dy) <= tolerance;
        }

        public bool IsPointNearVertex(Point point, out bool isStartVertex, int tolerance = 15)
        {
            isStartVertex = false;
            double distToStart = Math.Sqrt(Math.Pow(point.X - StartPoint.X, 2) + Math.Pow(point.Y - StartPoint.Y, 2));
            double distToEnd = Math.Sqrt(Math.Pow(point.X - EndPoint.X, 2) + Math.Pow(point.Y - EndPoint.Y, 2));

            if (distToStart <= tolerance)
            {
                isStartVertex = true;
                return true;
            }
            if (distToEnd <= tolerance)
            {
                isStartVertex = false;
                return true;
            }
            return false;
        }
    }
}
