using PCHMI;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace CowainHmi
{
    public enum LineDirection
    {
        左上右下,
        左下右上,
        水平,
        垂直
    }

    public enum ArrowType
    {
        无箭头,
        结束箭头,
        开始箭头,
        双箭头
    }

    public partial class ucArrowLine : Control
    {

        // 私有字段  
        private int _lineWidth = 2;
        private int _arrowWidth = 10;
        private int _arrowHeight = 5;
        private LineDirection _angle = LineDirection.水平;
        private ArrowType _arrowType = ArrowType.无箭头;
        private Color _lineColor = Color.Teal;
        private PointF _startPoint = new PointF(50, 50);
        private PointF _endPoint = new PointF(200, 50);

        // 公共属性  
        [Browsable(true), EditorBrowsable(EditorBrowsableState.Always), DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public int LineWidth
        {
            get { return _lineWidth; }
            set
            {
                _lineWidth = value;
                Draw(); // 触发重绘  
            }
        }

        // 公共属性  
        [Browsable(true), EditorBrowsable(EditorBrowsableState.Always), DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public Color LineColor
        {
            get { return _lineColor; }
            set
            {
                _lineColor = value;
                Draw(); // 触发重绘  
            }
        }

        [Browsable(true), EditorBrowsable(EditorBrowsableState.Always), DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public int ArrowWidth
        {
            get { return _arrowWidth; }
            set
            {
                _arrowWidth = value;
                Draw(); // 触发重绘  
            }
        }

        [Browsable(true), EditorBrowsable(EditorBrowsableState.Always), DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public int ArrowHeight
        {
            get { return _arrowHeight; }
            set
            {
                _arrowHeight = value;
                Draw(); // 触发重绘  
            }
        }

        [Browsable(true), EditorBrowsable(EditorBrowsableState.Always), DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public LineDirection Angle
        {
            get { return _angle; }
            set
            {
                _angle = value;
                CalculatePoint(); // 重新计算终点位置  
                Draw(); // 触发重绘  
            }
        }

        [Browsable(true), EditorBrowsable(EditorBrowsableState.Always), DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public ArrowType ArrowType
        {
            get { return _arrowType; }
            set
            {
                _arrowType = value;
                CalculatePoint(); // 重新计算终点位置  
                Draw(); // 触发重绘  
            }
        }

        // 构造函数  
        public ucArrowLine()
        {
            InitializeComponent();
            SetStyle(ControlStyles.ResizeRedraw | ControlStyles.SupportsTransparentBackColor, value: true);
            SetStyle(ControlStyles.Selectable, value: false);
            BackColor = Color.Transparent;
            this.Resize += new EventHandler(this.ArrowLineControl_Resize);
            this.Size = new Size(300, 100); // 设置控件大小  
        }


        // 控件大小改变时调整  
        private void ArrowLineControl_Resize(object sender, EventArgs e)
        {
            CalculatePoint();
            Draw();
        }

        // 根据方向更新起点和终点  
        private void CalculatePoint()
        {
            switch (Angle)
            {
                case LineDirection.水平:
                    _startPoint = new PointF(0, this.Height / 2);
                    _endPoint = new PointF(this.Width - 4, this.Height / 2);
                    break;
                case LineDirection.垂直:
                    _startPoint = new PointF(this.Width / 2, 0);
                    _endPoint = new PointF(this.Width / 2, this.Height);
                    break;
                case LineDirection.左上右下:
                    _startPoint = new PointF(0, 0);
                    _endPoint = new PointF(this.Width, this.Height);
                    break;
                case LineDirection.左下右上:
                    _startPoint = new PointF(0, this.Height);
                    _endPoint = new PointF(this.Width, 0);
                    break;
            }
        }
        private Bitmap bmp;
        // 重写OnPaint方法  
        private void Draw()
        {
            bmp = new Bitmap(base.Width, base.Height);
            Graphics graphics = Graphics.FromImage(bmp);
            graphics.SmoothingMode = SmoothingMode.AntiAlias;
            graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
            // 绘制线段  
            Pen linePen = new Pen(LineColor, _lineWidth);
            graphics.DrawLine(linePen, _startPoint, _endPoint);
            switch (_arrowType)
            {
                case ArrowType.结束箭头:
                    graphics.FillPolygon(linePen.Brush, DrawArrow(graphics, _startPoint, _endPoint, ArrowWidth, ArrowHeight));
                    break;
                case ArrowType.开始箭头:
                    graphics.FillPolygon(linePen.Brush, DrawArrow(graphics, _endPoint, _startPoint, ArrowWidth, ArrowHeight));
                    break;
                case ArrowType.双箭头:
                    graphics.FillPolygon(linePen.Brush, DrawArrow(graphics, _startPoint, _endPoint, ArrowWidth, ArrowHeight));
                    graphics.FillPolygon(linePen.Brush, DrawArrow(graphics, _endPoint, _startPoint, ArrowWidth, ArrowHeight));
                    break;
                default:
                    break;
            }
            BackgroundImage = bmp;
            //设置显示区域，区域外透明
            RegionControl(_startPoint, _endPoint);
            linePen.Dispose();
            graphics.Dispose();
        }

        /// <summary>
        /// 获取箭头的三个点
        /// </summary>
        /// <returns></returns>
        private PointF[] DrawArrow(Graphics g, PointF start, PointF end, int arrowWidth, int arrowHeigth)
        {

            double angle = Math.Atan2(end.Y - start.Y, end.X - start.X);
            double left_x = end.X - arrowWidth * Math.Cos(angle);
            double left_y = end.Y - arrowWidth * Math.Sin(angle);
            PointF p1 = end;
            PointF p2 = new PointF
            {
                X = (float)(left_x + (arrowHeigth / 2) * Math.Cos(angle + Math.PI / 2)),
                Y = (float)(left_y + (arrowHeigth / 2) * Math.Sin(angle + Math.PI / 2))
            };
            PointF p3 = new PointF
            {
                X = (float)(left_x - (arrowHeigth / 2) * Math.Cos(angle + Math.PI / 2)),
                Y = (float)(left_y - (arrowHeigth / 2) * Math.Sin(angle + Math.PI / 2))
            };
            return new PointF[] { p1, p2, p3 };
        }
        private void RegionControl(PointF start, PointF end)
        {
            PointF[] points = new PointF[] { };
            //设置区域，区域外透明

            switch (_arrowType)
            {
                case ArrowType.无箭头:
                    points = GetNoArrowRegion(_startPoint, _endPoint);
                    break;
                case ArrowType.结束箭头:
                    points = GetEndArrowRegion(_startPoint, _endPoint);
                    break;
                case ArrowType.开始箭头:
                    points = GetEndArrowRegion(_endPoint, _startPoint);
                    break;
                case ArrowType.双箭头:
                    points = GetBothArrowRegion(_startPoint, _endPoint);
                    break;
                default:
                    break;
            }

            GraphicsPath graphicsPath = new GraphicsPath();
            graphicsPath.AddPolygon(points);
            Region region = new Region(graphicsPath);
            this.Region = region;
        }

        private PointF[] GetNoArrowRegion(PointF start, PointF end)
        {
            double angle = Math.Atan2(end.Y - start.Y, end.X - start.X);
            PointF p1 = new PointF
            {
                X = (float)(start.X - (_lineWidth / 2) * Math.Cos(angle + Math.PI / 2)),
                Y = (float)(start.Y - (_lineWidth / 2) * Math.Sin(angle + Math.PI / 2))
            };
            PointF p2 = new PointF
            {
                X = (float)(start.X + (_lineWidth / 2) * Math.Cos(angle + Math.PI / 2)),
                Y = (float)(start.Y + (_lineWidth / 2) * Math.Sin(angle + Math.PI / 2))
            };
            PointF p3 = new PointF
            {
                X = (float)(end.X + (_lineWidth / 2) * Math.Cos(angle + Math.PI / 2)),
                Y = (float)(end.Y + (_lineWidth / 2) * Math.Sin(angle + Math.PI / 2))
            };
            PointF p4 = new PointF
            {
                X = (float)(end.X - (_lineWidth / 2) * Math.Cos(angle + Math.PI / 2)),
                Y = (float)(end.Y - (_lineWidth / 2) * Math.Sin(angle + Math.PI / 2))
            };
            return new PointF[] { p1, p2, p3, p4 };
        }

        private PointF[] GetEndArrowRegion(PointF start, PointF end)
        {
            double length = Math.Sqrt((end.Y - start.Y) * (end.Y - start.Y) + (end.X - start.X) * (end.X - start.X));
            double angle = Math.Atan2(end.Y - start.Y, end.X - start.X);
            double left_x = end.X - ArrowWidth * Math.Cos(angle);
            double left_y = end.Y - ArrowWidth * Math.Sin(angle);
            PointF p1 = new PointF
            {
                X = (float)(start.X - (_lineWidth / 2) * Math.Cos(angle + Math.PI / 2)),
                Y = (float)(start.Y - (_lineWidth / 2) * Math.Sin(angle + Math.PI / 2))
            };
            PointF p2 = new PointF
            {
                X = (float)(start.X + (_lineWidth / 2) * Math.Cos(angle + Math.PI / 2)),
                Y = (float)(start.Y + (_lineWidth / 2) * Math.Sin(angle + Math.PI / 2))
            };
            PointF p3 = new PointF
            {
                X = (float)(left_x + (_lineWidth / 2) * Math.Cos(angle + Math.PI / 2)),
                Y = (float)(left_y + (_lineWidth / 2) * Math.Sin(angle + Math.PI / 2))
            };
            PointF p4 = new PointF
            {
                X = (float)(left_x + (ArrowHeight / 2) * Math.Cos(angle + Math.PI / 2)),
                Y = (float)(left_y + (ArrowHeight / 2) * Math.Sin(angle + Math.PI / 2))
            };
            PointF p5 = end;
            PointF p6 = new PointF
            {
                X = (float)(left_x - (ArrowHeight / 2) * Math.Cos(angle + Math.PI / 2)),
                Y = (float)(left_y - (ArrowHeight / 2) * Math.Sin(angle + Math.PI / 2))
            };
            PointF p7 = new PointF
            {
                X = (float)(left_x - (_lineWidth / 2) * Math.Cos(angle + Math.PI / 2)),
                Y = (float)(left_y - (_lineWidth / 2) * Math.Sin(angle + Math.PI / 2))
            };
            return new PointF[] { p1, p2, p3, p4, p5, p6, p7 };
        }

        private PointF[] GetBothArrowRegion(PointF start, PointF end)
        {
            double length = Math.Sqrt((end.Y - start.Y) * (end.Y - start.Y) + (end.X - start.X) * (end.X - start.X));
            double angle = Math.Atan2(end.Y - start.Y, end.X - start.X);
            double left_end_x = end.X - ArrowWidth * Math.Cos(angle);
            double left_end_y = end.Y - ArrowWidth * Math.Sin(angle);
            double left_start_x = start.X + ArrowWidth * Math.Cos(angle);
            double left_start_y = start.Y + ArrowWidth * Math.Sin(angle);
            PointF p1 = new PointF
            {
                X = (float)(left_start_x - (_lineWidth / 2) * Math.Cos(angle + Math.PI / 2)),
                Y = (float)(left_start_y - (_lineWidth / 2) * Math.Sin(angle + Math.PI / 2))
            };
            PointF p2 = new PointF
            {
                X = (float)(left_start_x - (ArrowHeight / 2) * Math.Cos(angle + Math.PI / 2)),
                Y = (float)(left_start_y - (ArrowHeight / 2) * Math.Sin(angle + Math.PI / 2))
            };
            PointF p3 = start;
            PointF p4 = new PointF
            {
                X = (float)(left_start_x + (ArrowHeight / 2) * Math.Cos(angle + Math.PI / 2)),
                Y = (float)(left_start_y + (ArrowHeight / 2) * Math.Sin(angle + Math.PI / 2))
            };
            PointF p5 = new PointF
            {
                X = (float)(left_start_x + (_lineWidth / 2) * Math.Cos(angle + Math.PI / 2)),
                Y = (float)(left_start_y + (_lineWidth / 2) * Math.Sin(angle + Math.PI / 2))
            };
            PointF p6 = new PointF
            {
                X = (float)(left_end_x + (_lineWidth / 2) * Math.Cos(angle + Math.PI / 2)),
                Y = (float)(left_end_y + (_lineWidth / 2) * Math.Sin(angle + Math.PI / 2))
            };
            PointF p7 = new PointF
            {
                X = (float)(left_end_x + (ArrowHeight / 2) * Math.Cos(angle + Math.PI / 2)),
                Y = (float)(left_end_y + (ArrowHeight / 2) * Math.Sin(angle + Math.PI / 2))
            };
            PointF p8 = end;
            PointF p9 = new PointF
            {
                X = (float)(left_end_x - (ArrowHeight / 2) * Math.Cos(angle + Math.PI / 2)),
                Y = (float)(left_end_y - (ArrowHeight / 2) * Math.Sin(angle + Math.PI / 2))
            };
            PointF p10 = new PointF
            {
                X = (float)(left_end_x - (_lineWidth / 2) * Math.Cos(angle + Math.PI / 2)),
                Y = (float)(left_end_y - (_lineWidth / 2) * Math.Sin(angle + Math.PI / 2))
            };
            return new PointF[] { p1, p2, p3, p4, p5, p6, p7, p8, p9, p10 };
        }
        private void RegionControl()
        {
            //设置区域，区域外透明
            Point[] points = new Point[4]
                            {
                                new Point(0, this.Height / 2 + _lineWidth/2),
                                new Point(0, this.Height / 2 - _lineWidth/2),
                                new Point(this.Width, this.Height / 2 - _lineWidth/2),
                                new Point(this.Width, this.Height / 2 + _lineWidth/2)
                            };
            switch (Angle)
            {
                case LineDirection.水平:
                    points = new Point[4]
                            {
                                new Point(0, this.Height / 2 + _lineWidth/2),
                                new Point(0, this.Height / 2 - _lineWidth/2),
                                new Point(this.Width, this.Height / 2 - _lineWidth/2),
                                new Point(this.Width, this.Height / 2 + _lineWidth/2)
                            };
                    break;
                case LineDirection.垂直:
                    points = new Point[4]
                            {
                                new Point(this.Width/2 - _lineWidth/2, 0),
                                new Point(this.Width/2 + _lineWidth/2, 0),
                                new Point(this.Width/2 + _lineWidth/2, this.Height),
                                new Point(this.Width/2 - _lineWidth/2, this.Height)
                            };
                    break;
                case LineDirection.左上右下:
                    points = new Point[4]
                            {
                                new Point(0, base.Height / 2 + _lineWidth/2),
                                new Point(0, base.Height / 2 - _lineWidth/2),
                                new Point(base.Width, base.Height / 2 - _lineWidth/2),
                                new Point(base.Width, base.Height / 2 + _lineWidth/2)
                            };
                    break;
                case LineDirection.左下右上:
                    points = new Point[4]
                            {
                                new Point(0, base.Height / 2 + _lineWidth/2),
                                new Point(0, base.Height / 2 - _lineWidth/2),
                                new Point(base.Width, base.Height / 2 - _lineWidth/2),
                                new Point(base.Width, base.Height / 2 + _lineWidth/2)
                            };
                    break;
            }

            GraphicsPath graphicsPath = new GraphicsPath();
            graphicsPath.AddPolygon(points);
            Region region = new Region(graphicsPath);
            this.Region = region;
        }

    }
}
