#if Net4
using System;
using System.Collections.Generic;
using System.Drawing.Drawing2D;
using System.Text;
using CPF.Drawing;

namespace CPF.GDIPlus
{
    public class GDIPlusPath : IPathImpl
    {
        System.Drawing.Drawing2D.GraphicsPath path;
        public GDIPlusPath()
        {
            path = new System.Drawing.Drawing2D.GraphicsPath();
        }
        public GDIPlusPath(System.Drawing.Drawing2D.GraphicsPath path)
        {
            this.path = path;
        }
        static System.Drawing.StringFormat stringFormat = System.Drawing.StringFormat.GenericTypographic;
        public GDIPlusPath(in Font font, string text)
        {
            path = new System.Drawing.Drawing2D.GraphicsPath();
            var f = font.AdapterFont as FontStruct;
            path.AddString(text, f.Font.FontFamily, (int)f.Font.Style, f.Font.Size, new System.Drawing.PointF(), stringFormat);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="point">终点</param>
        /// <param name="size"></param>
        /// <param name="rotationAngle"></param>
        /// <param name="isClockwise"></param>
        /// <param name="isLargerArc"></param>
        public void ArcTo(Point point, Size size, float rotationAngle, bool isClockwise, bool isLargerArc)
        {
            if (_lastPoint == point)
            {
                // If the endpoints (x, y) and (x0, y0) are identical, then this
                // is equivalent to omitting the elliptical arc segment entirely.
            }
            else if (size.Width == 0 || size.Height == 0)
            {
                // Ensure radii are valid
                path.AddLine(_lastPoint.X, _lastPoint.Y, point.X, point.Y);
            }
            else
            {
                CalculatedArcValues calcValues = GetCalculatedArcValues(_lastPoint, point, size, rotationAngle, isClockwise, isLargerArc);

                GraphicsPath subPath = new GraphicsPath();
                subPath.StartFigure();
                subPath.AddArc((float)(calcValues.Cx - calcValues.CorrRx),
                    (float)(calcValues.Cy - calcValues.CorrRy),
                    (float)calcValues.CorrRx * 2, (float)calcValues.CorrRy * 2,
                    (float)calcValues.AngleStart, (float)calcValues.AngleExtent);

                System.Drawing.Drawing2D.Matrix matrix = new System.Drawing.Drawing2D.Matrix();
                matrix.Translate(-(float)calcValues.Cx, -(float)calcValues.Cy);
                subPath.Transform(matrix);

                matrix = new System.Drawing.Drawing2D.Matrix();
                matrix.Rotate((float)rotationAngle);
                subPath.Transform(matrix);

                matrix = new System.Drawing.Drawing2D.Matrix();
                matrix.Translate((float)calcValues.Cx, (float)calcValues.Cy);
                subPath.Transform(matrix);

                path.AddPath(subPath, true);
            }

            _lastPoint = point;
            //throw new NotImplementedException("我曹，太麻烦了");
        }

        private Point _lastPoint;

        public System.Drawing.Drawing2D.GraphicsPath Path
        {
            get { return path; }
        }

        public FillRule FillRule
        {
            get => path.FillMode == System.Drawing.Drawing2D.FillMode.Alternate ? FillRule.EvenOdd : FillRule.NonZero;
            set => path.FillMode = value == FillRule.NonZero ? System.Drawing.Drawing2D.FillMode.Winding : System.Drawing.Drawing2D.FillMode.Alternate;
        }

        public void LineTo(float x, float y)
        {
            path.AddLine(_lastPoint.X, _lastPoint.Y, x, y);
            _lastPoint = new Point(x, y);
        }

        public void BeginFigure(float x, float y)
        {
            _lastPoint = new Point(x, y);
        }

        public void EndFigure(bool closeFigure)
        {
            if (closeFigure)
            {
                path.CloseFigure();
            }
        }

        public void Dispose()
        {
            if (path != null)
            {
                path.Dispose();
                path = null;
            }
        }

        public Rect GetBounds()
        {
            return path.GetBounds().ToRect();
        }

        public void CubicTo(Point p1, Point p2, Point p3)
        {
            path.AddBezier(_lastPoint.ToGdiPoint(), p1.ToGdiPoint(), p2.ToGdiPoint(), p3.ToGdiPoint());
            _lastPoint = p3;
        }

        public void QuadTo(Point p1, Point p2)
        {
            path.AddBezier(_lastPoint.ToGdiPoint(), _lastPoint.ToGdiPoint(), p1.ToGdiPoint(), p2.ToGdiPoint());
            _lastPoint = p2;
        }

        CalculatedArcValues GetCalculatedArcValues(Point lastPoint, Point point, Size size, float rotationAngle, bool isClockwise, bool isLargerArc)
        {
            CalculatedArcValues calcVal = new CalculatedArcValues();

            /*
             *	This algorithm is taken from the Batik source. All cudos to the Batik crew.
             */

            var startPoint = lastPoint;
            var endPoint = point;

            double x0 = startPoint.X;
            double y0 = startPoint.Y;

            double x = endPoint.X;
            double y = endPoint.Y;

            // Compute the half distance between the current and the final point
            double dx2 = (x0 - x) / 2.0;
            double dy2 = (y0 - y) / 2.0;

            // Convert angle from degrees to radians
            double radAngle = rotationAngle * Math.PI / 180;
            double cosAngle = Math.Cos(radAngle);
            double sinAngle = Math.Sin(radAngle);

            //
            // Step 1 : Compute (x1, y1)
            //
            double x1 = (cosAngle * dx2 + sinAngle * dy2);
            double y1 = (-sinAngle * dx2 + cosAngle * dy2);

            // Ensure radii are large enough
            double rx = Math.Abs(size.Width);
            double ry = Math.Abs(size.Height);

            double Prx = rx * rx;
            double Pry = ry * ry;
            double Px1 = x1 * x1;
            double Py1 = y1 * y1;

            // check that radii are large enough
            double radiiCheck = Px1 / Prx + Py1 / Pry;
            if (radiiCheck > 1)
            {
                rx = Math.Sqrt(radiiCheck) * rx;
                ry = Math.Sqrt(radiiCheck) * ry;
                Prx = rx * rx;
                Pry = ry * ry;
            }

            //
            // Step 2 : Compute (cx1, cy1)
            //
            double sign = (isLargerArc == isClockwise) ? -1 : 1;
            double sq = ((Prx * Pry) - (Prx * Py1) - (Pry * Px1)) / ((Prx * Py1) + (Pry * Px1));
            sq = (sq < 0) ? 0 : sq;
            double coef = (sign * Math.Sqrt(sq));
            double cx1 = coef * ((rx * y1) / ry);
            double cy1 = coef * -((ry * x1) / rx);

            //
            // Step 3 : Compute (cx, cy) from (cx1, cy1)
            //
            double sx2 = (x0 + x) / 2.0;
            double sy2 = (y0 + y) / 2.0;
            double cx = sx2 + (cosAngle * cx1 - sinAngle * cy1);
            double cy = sy2 + (sinAngle * cx1 + cosAngle * cy1);

            //
            // Step 4 : Compute the angleStart (angle1) and the angleExtent (dangle)
            //
            double ux = (x1 - cx1); // rx;
            double uy = (y1 - cy1); // ry;
            double vx = (-x1 - cx1); // rx;
            double vy = (-y1 - cy1); // ry;
            double p, n;
            // Compute the angle start
            n = Math.Sqrt((ux * ux) + (uy * uy));
            p = ux; // (1 * ux) + (0 * uy)
            sign = (uy < 0) ? -1d : 1d;
            double angleStart = sign * Math.Acos(p / n);
            angleStart = angleStart * 180 / Math.PI;

            // Compute the angle extent
            n = Math.Sqrt((ux * ux + uy * uy) * (vx * vx + vy * vy));
            p = ux * vx + uy * vy;
            sign = (ux * vy - uy * vx < 0) ? -1d : 1d;
            double angleExtent = sign * Math.Acos(p / n);
            angleExtent = angleExtent * 180 / Math.PI;

            if (!isClockwise && angleExtent > 0)
            {
                angleExtent -= 360f;
            }
            else if (isClockwise && angleExtent < 0)
            {
                angleExtent += 360f;
            }
            angleExtent %= 360f;
            angleStart %= 360f;

            calcVal.CorrRx = rx;
            calcVal.CorrRy = ry;
            calcVal.Cx = cx;
            calcVal.Cy = cy;
            calcVal.AngleStart = angleStart;
            calcVal.AngleExtent = angleExtent;

            return calcVal;
        }

        public void Transform(Drawing.Matrix matrix)
        {
            path.Transform(matrix.ToMatrix());
        }

        public object Clone()
        {
            return new GDIPlusPath((GraphicsPath)path.Clone());
        }

        public void AddPath(PathGeometry path, bool connect)
        {
            this.path.AddPath((path.PathIml as GDIPlusPath).path, connect);
        }

        public bool Contains(float x, float y)
        {
            return path.IsVisible(x, y);
        }

        public IPathImpl CreateStrokePath(float strokeWidth)
        {
            var p = (GraphicsPath)path.Clone();
            p.Widen(new System.Drawing.Pen(System.Drawing.Color.Black, strokeWidth));
            return new GDIPlusPath(p);
        }

        ~GDIPlusPath()
        {
            Dispose();
        }
    }

    public struct CalculatedArcValues
    {
        public double CorrRx;
        public double CorrRy;
        public double Cx;
        public double Cy;
        public double AngleStart;
        public double AngleExtent;

        public CalculatedArcValues(double rx, double ry, double cx, double cy,
            double angleStart, double angleExtent)
        {
            this.CorrRx = rx;
            this.CorrRy = ry;
            this.Cx = cx;
            this.Cy = cy;
            this.AngleStart = angleStart;
            this.AngleExtent = angleExtent;
        }
    }
}
#endif