using CPF.Mac.Foundation;
using CPF.Mac.ObjCRuntime;
using System;
using System.Runtime.InteropServices;

namespace CPF.Mac.CoreGraphics
{
	public class CGPath : INativeObject, IDisposable
	{
		public delegate void ApplierFunction(CGPathElement element);

		private delegate void CGPathApplierFunction(IntPtr info, IntPtr CGPathElementPtr);

		internal IntPtr handle;

		public IntPtr Handle => handle;

		public bool IsEmpty => CGPathIsEmpty(handle) != 0;

		public CGPoint CurrentPoint => CGPathGetCurrentPoint(handle);

		public CGRect BoundingBox => CGPathGetBoundingBox(handle);

		public CGRect PathBoundingBox => CGPathGetPathBoundingBox(handle);

		[DllImport("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/CoreGraphics.framework/CoreGraphics")]
		private static extern IntPtr CGPathCreateMutable();

		public CGPath()
		{
			handle = CGPathCreateMutable();
		}

		[DllImport("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/CoreGraphics.framework/CoreGraphics")]
		private static extern IntPtr CGPathCreateMutableCopy(IntPtr path);

		public CGPath(CGPath basePath)
		{
			if (basePath == null)
			{
				throw new ArgumentNullException("basePath");
			}
			handle = CGPathCreateMutableCopy(basePath.handle);
		}

		public CGPath(IntPtr handle)
		{
			CGPathRetain(handle);
			this.handle = handle;
		}

		[Preserve(Conditional = true)]
		internal CGPath(IntPtr handle, bool owns)
		{
			if (!owns)
			{
				CGPathRetain(handle);
			}
			this.handle = handle;
		}

		~CGPath()
		{
			Dispose(disposing: false);
		}

		public void Dispose()
		{
			Dispose(disposing: true);
			GC.SuppressFinalize(this);
		}

		[DllImport("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/CoreGraphics.framework/CoreGraphics")]
		private static extern void CGPathRelease(IntPtr handle);

		[DllImport("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/CoreGraphics.framework/CoreGraphics")]
		private static extern void CGPathRetain(IntPtr handle);

		protected virtual void Dispose(bool disposing)
		{
			if (handle != IntPtr.Zero)
			{
				CGPathRelease(handle);
				handle = IntPtr.Zero;
			}
		}

		[DllImport("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/CoreGraphics.framework/CoreGraphics")]
		private static extern bool CGPathEqualToPath(IntPtr path1, IntPtr path2);

		public static bool operator ==(CGPath path1, CGPath path2)
		{
			return object.Equals(path1, path2);
		}

		public static bool operator !=(CGPath path1, CGPath path2)
		{
			return !object.Equals(path1, path2);
		}

		public override int GetHashCode()
		{
			return handle.GetHashCode();
		}

		public override bool Equals(object o)
		{
			CGPath cGPath = o as CGPath;
			if (cGPath == null)
			{
				return false;
			}
			return CGPathEqualToPath(handle, cGPath.handle);
		}

		[DllImport("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/CoreGraphics.framework/CoreGraphics")]
		private static extern void CGPathMoveToPoint(IntPtr path, ref CGAffineTransform m, double x, double y);

		[DllImport("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/CoreGraphics.framework/CoreGraphics")]
		private static extern void CGPathMoveToPoint(IntPtr path, IntPtr zero, double x, double y);

		public void MoveToPoint(double x, double y)
		{
			CGPathMoveToPoint(handle, IntPtr.Zero, x, y);
		}

		public void MoveToPoint(CGPoint point)
		{
			CGPathMoveToPoint(handle, IntPtr.Zero, point.X, point.Y);
		}

		public void MoveToPoint(CGAffineTransform transform, double x, double y)
		{
			CGPathMoveToPoint(handle, ref transform, x, y);
		}

		public void MoveToPoint(CGAffineTransform transform, CGPoint point)
		{
			CGPathMoveToPoint(handle, ref transform, point.X, point.Y);
		}

		[DllImport("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/CoreGraphics.framework/CoreGraphics")]
		private static extern void CGPathAddLineToPoint(IntPtr path, ref CGAffineTransform m, double x, double y);

		[DllImport("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/CoreGraphics.framework/CoreGraphics")]
		private static extern void CGPathAddLineToPoint(IntPtr path, IntPtr m, double x, double y);

		[Advice("Use AddLineToPoint instead")]
		public void CGPathAddLineToPoint(double x, double y)
		{
			AddLineToPoint(x, y);
		}

		public void AddLineToPoint(double x, double y)
		{
			CGPathAddLineToPoint(handle, IntPtr.Zero, x, y);
		}

		public void AddLineToPoint(CGPoint point)
		{
			CGPathAddLineToPoint(handle, IntPtr.Zero, point.X, point.Y);
		}

		[Advice("Use AddLineToPoint instead")]
		public void CGPathAddLineToPoint(CGAffineTransform transform, double x, double y)
		{
			AddLineToPoint(transform, x, y);
		}

		public void AddLineToPoint(CGAffineTransform transform, double x, double y)
		{
			CGPathAddLineToPoint(handle, ref transform, x, y);
		}

		public void AddLineToPoint(CGAffineTransform transform, CGPoint point)
		{
			CGPathAddLineToPoint(handle, ref transform, point.X, point.Y);
		}

		[DllImport("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/CoreGraphics.framework/CoreGraphics")]
		private static extern void CGPathAddQuadCurveToPoint(IntPtr path, ref CGAffineTransform m, double cpx, double cpy, double x, double y);

		[DllImport("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/CoreGraphics.framework/CoreGraphics")]
		private static extern void CGPathAddQuadCurveToPoint(IntPtr path, IntPtr zero, double cpx, double cpy, double x, double y);

		public void AddQuadCurveToPoint(double cpx, double cpy, double x, double y)
		{
			CGPathAddQuadCurveToPoint(handle, IntPtr.Zero, cpx, cpy, x, y);
		}

		public void AddQuadCurveToPoint(CGAffineTransform transform, double cpx, double cpy, double x, double y)
		{
			CGPathAddQuadCurveToPoint(handle, ref transform, cpx, cpy, x, y);
		}

		[DllImport("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/CoreGraphics.framework/CoreGraphics")]
		private static extern void CGPathAddCurveToPoint(IntPtr path, ref CGAffineTransform m, double cp1x, double cp1y, double cp2x, double cp2y, double x, double y);

		public void AddCurveToPoint(CGAffineTransform transform, double cp1x, double cp1y, double cp2x, double cp2y, double x, double y)
		{
			CGPathAddCurveToPoint(handle, ref transform, cp1x, cp1y, cp2x, cp2y, x, y);
		}

		public void AddCurveToPoint(CGAffineTransform transform, CGPoint cp1, CGPoint cp2, CGPoint point)
		{
			CGPathAddCurveToPoint(handle, ref transform, cp1.X, cp1.Y, cp2.X, cp2.Y, point.X, point.Y);
		}

		[DllImport("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/CoreGraphics.framework/CoreGraphics")]
		private static extern void CGPathAddCurveToPoint(IntPtr path, IntPtr zero, double cp1x, double cp1y, double cp2x, double cp2y, double x, double y);

		public void AddCurveToPoint(double cp1x, double cp1y, double cp2x, double cp2y, double x, double y)
		{
			CGPathAddCurveToPoint(handle, IntPtr.Zero, cp1x, cp1y, cp2x, cp2y, x, y);
		}

		public void AddCurveToPoint(CGPoint cp1, CGPoint cp2, CGPoint point)
		{
			CGPathAddCurveToPoint(handle, IntPtr.Zero, cp1.X, cp1.Y, cp2.X, cp2.Y, point.X, point.Y);
		}

		[DllImport("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/CoreGraphics.framework/CoreGraphics")]
		private static extern void CGPathCloseSubpath(IntPtr path);

		public void CloseSubpath()
		{
			CGPathCloseSubpath(handle);
		}

		[DllImport("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/CoreGraphics.framework/CoreGraphics")]
		private static extern void CGPathAddRect(IntPtr path, ref CGAffineTransform m, CGRect rect);

		public void AddRect(CGAffineTransform transform, CGRect rect)
		{
			CGPathAddRect(handle, ref transform, rect);
		}

		[DllImport("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/CoreGraphics.framework/CoreGraphics")]
		private static extern void CGPathAddRect(IntPtr path, IntPtr zero, CGRect rect);

		public void AddRect(CGRect rect)
		{
			CGPathAddRect(handle, IntPtr.Zero, rect);
		}

		[DllImport("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/CoreGraphics.framework/CoreGraphics")]
		private static extern void CGPathAddRects(IntPtr path, ref CGAffineTransform m, CGRect[] rects, IntPtr size_t_count);

		public void AddRects(CGAffineTransform m, CGRect[] rects)
		{
			CGPathAddRects(handle, ref m, rects, new IntPtr(rects.Length));
		}

		public void AddRects(CGAffineTransform m, CGRect[] rects, long count)
		{
			if (count > rects.Length)
			{
				throw new ArgumentException("counts");
			}
			CGPathAddRects(handle, ref m, rects, new IntPtr(count));
		}

		[DllImport("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/CoreGraphics.framework/CoreGraphics")]
		private static extern void CGPathAddRects(IntPtr path, IntPtr Zero, CGRect[] rects, IntPtr size_t_count);

		public void AddRects(CGRect[] rects)
		{
			CGPathAddRects(handle, IntPtr.Zero, rects, new IntPtr(rects.Length));
		}

		public void AddRects(CGRect[] rects, long count)
		{
			if (count > rects.Length)
			{
				throw new ArgumentException("count");
			}
			CGPathAddRects(handle, IntPtr.Zero, rects, new IntPtr(count));
		}

		[DllImport("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/CoreGraphics.framework/CoreGraphics")]
		private static extern void CGPathAddLines(IntPtr path, ref CGAffineTransform m, CGPoint[] points, IntPtr size_t_count);

		public void AddLines(CGAffineTransform m, CGPoint[] points)
		{
			CGPathAddLines(handle, ref m, points, new IntPtr(points.Length));
		}

		public void AddLines(CGAffineTransform m, CGPoint[] points, long count)
		{
			if (count > points.Length)
			{
				throw new ArgumentException("count");
			}
			CGPathAddLines(handle, ref m, points, new IntPtr(count));
		}

		[DllImport("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/CoreGraphics.framework/CoreGraphics")]
		private static extern void CGPathAddLines(IntPtr path, IntPtr zero, CGPoint[] points, IntPtr size_t_count);

		public void AddLines(CGPoint[] points)
		{
			AddLines(points, points.Length);
		}

		public void AddLines(CGPoint[] points, long count)
		{
			if (count > points.Length)
			{
				throw new ArgumentException("count");
			}
			CGPathAddLines(handle, IntPtr.Zero, points, new IntPtr(count));
		}

		[DllImport("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/CoreGraphics.framework/CoreGraphics")]
		private static extern void CGPathAddEllipseInRect(IntPtr path, ref CGAffineTransform m, CGRect rect);

		public void AddEllipseInRect(CGAffineTransform m, CGRect rect)
		{
			CGPathAddEllipseInRect(handle, ref m, rect);
		}

		[Obsolete("Use AddEllipseInRect instead")]
		public void AddElipseInRect(CGAffineTransform m, CGRect rect)
		{
			AddEllipseInRect(m, rect);
		}

		[DllImport("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/CoreGraphics.framework/CoreGraphics")]
		private static extern void CGPathAddEllipseInRect(IntPtr path, IntPtr zero, CGRect rect);

		public void AddElipseInRect(CGRect rect)
		{
			CGPathAddEllipseInRect(handle, IntPtr.Zero, rect);
		}

		[DllImport("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/CoreGraphics.framework/CoreGraphics")]
		private static extern void CGPathAddArc(IntPtr path, ref CGAffineTransform m, double x, double y, double radius, double startAngle, double endAngle, bool clockwise);

		public void AddArc(CGAffineTransform m, double x, double y, double radius, double startAngle, double endAngle, bool clockwise)
		{
			CGPathAddArc(handle, ref m, x, y, radius, startAngle, endAngle, clockwise);
		}

		[DllImport("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/CoreGraphics.framework/CoreGraphics")]
		private static extern void CGPathAddArc(IntPtr path, IntPtr zero, double x, double y, double radius, double startAngle, double endAngle, bool clockwise);

		public void AddArc(double x, double y, double radius, double startAngle, double endAngle, bool clockwise)
		{
			CGPathAddArc(handle, IntPtr.Zero, x, y, radius, startAngle, endAngle, clockwise);
		}

		[DllImport("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/CoreGraphics.framework/CoreGraphics")]
		private static extern void CGPathAddArcToPoint(IntPtr path, ref CGAffineTransform m, double x1, double y1, double x2, double y2, double radius);

		public void AddArcToPoint(CGAffineTransform m, double x1, double y1, double x2, double y2, double radius)
		{
			CGPathAddArcToPoint(handle, ref m, x1, y1, x2, y2, radius);
		}

		[DllImport("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/CoreGraphics.framework/CoreGraphics")]
		private static extern void CGPathAddArcToPoint(IntPtr path, IntPtr zero, double x1, double y1, double x2, double y2, double radius);

		public void AddArcToPoint(double x1, double y1, double x2, double y2, double radius)
		{
			CGPathAddArcToPoint(handle, IntPtr.Zero, x1, y1, x2, y2, radius);
		}

		[DllImport("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/CoreGraphics.framework/CoreGraphics")]
		private static extern void CGPathAddRelativeArc(IntPtr path, ref CGAffineTransform m, double x, double y, double radius, double startAngle, double delta);

		public void AddRelativeArc(CGAffineTransform m, double x, double y, double radius, double startAngle, double delta)
		{
			CGPathAddRelativeArc(handle, ref m, x, y, radius, startAngle, delta);
		}

		[DllImport("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/CoreGraphics.framework/CoreGraphics")]
		private static extern void CGPathAddRelativeArc(IntPtr path, IntPtr zero, double x, double y, double radius, double startAngle, double delta);

		public void AddRelativeArc(double x, double y, double radius, double startAngle, double delta)
		{
			CGPathAddRelativeArc(handle, IntPtr.Zero, x, y, radius, startAngle, delta);
		}

		[DllImport("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/CoreGraphics.framework/CoreGraphics")]
		private static extern void CGPathAddPath(IntPtr path1, ref CGAffineTransform m, IntPtr path2);

		public void AddPath(CGAffineTransform t, CGPath path2)
		{
			if (path2 == null)
			{
				throw new ArgumentNullException("path2");
			}
			CGPathAddPath(handle, ref t, path2.handle);
		}

		[DllImport("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/CoreGraphics.framework/CoreGraphics")]
		private static extern void CGPathAddPath(IntPtr path1, IntPtr zero, IntPtr path2);

		public void AddPath(CGPath path2)
		{
			if (path2 == null)
			{
				throw new ArgumentNullException("path2");
			}
			CGPathAddPath(handle, IntPtr.Zero, path2.handle);
		}

		[DllImport("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/CoreGraphics.framework/CoreGraphics")]
		private static extern int CGPathIsEmpty(IntPtr path);

		[DllImport("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/CoreGraphics.framework/CoreGraphics")]
		private static extern int CGPathIsRect(IntPtr path, out CGRect rect);

		public bool IsRect(out CGRect rect)
		{
			return CGPathIsRect(handle, out rect) != 0;
		}

		[DllImport("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/CoreGraphics.framework/CoreGraphics")]
		private static extern CGPoint CGPathGetCurrentPoint(IntPtr path);

		[DllImport("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/CoreGraphics.framework/CoreGraphics")]
		private static extern CGRect CGPathGetBoundingBox(IntPtr path);

		[DllImport("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/CoreGraphics.framework/CoreGraphics")]
		private static extern CGRect CGPathGetPathBoundingBox(IntPtr path);

		[DllImport("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/CoreGraphics.framework/CoreGraphics")]
		private static extern bool CGPathContainsPoint(IntPtr path, ref CGAffineTransform m, CGPoint point, bool eoFill);

		public bool ContainsPoint(CGAffineTransform m, CGPoint point, bool eoFill)
		{
			return CGPathContainsPoint(handle, ref m, point, eoFill);
		}

		[DllImport("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/CoreGraphics.framework/CoreGraphics")]
		private static extern bool CGPathContainsPoint(IntPtr path, IntPtr zero, CGPoint point, bool eoFill);

		public bool ContainsPoint(CGPoint point, bool eoFill)
		{
			return CGPathContainsPoint(handle, IntPtr.Zero, point, eoFill);
		}

		private unsafe static void ApplierCallback(IntPtr info, IntPtr element_ptr)
		{
			GCHandle gCHandle = GCHandle.FromIntPtr(info);
			CGPathElement element = new CGPathElement(Marshal.ReadInt32(element_ptr, 0));
			ApplierFunction applierFunction = (ApplierFunction)gCHandle.Target;
			IntPtr intPtr = Marshal.ReadIntPtr(element_ptr, sizeof(IntPtr));
			int num = Marshal.SizeOf(typeof(CGPoint));
			switch (element.Type)
			{
			case CGPathElementType.MoveToPoint:
			case CGPathElementType.AddLineToPoint:
				element.Point1 = (CGPoint)Marshal.PtrToStructure(intPtr, typeof(CGPoint));
				break;
			case CGPathElementType.AddQuadCurveToPoint:
				element.Point1 = (CGPoint)Marshal.PtrToStructure(intPtr, typeof(CGPoint));
				element.Point2 = (CGPoint)Marshal.PtrToStructure((IntPtr)((long)intPtr + num), typeof(CGPoint));
				break;
			case CGPathElementType.AddCurveToPoint:
				element.Point1 = (CGPoint)Marshal.PtrToStructure(intPtr, typeof(CGPoint));
				element.Point2 = (CGPoint)Marshal.PtrToStructure((IntPtr)((long)intPtr + num), typeof(CGPoint));
				element.Point3 = (CGPoint)Marshal.PtrToStructure((IntPtr)((long)intPtr + 2 * num), typeof(CGPoint));
				break;
			}
			applierFunction(element);
		}

		[DllImport("/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/CoreGraphics.framework/CoreGraphics")]
		private static extern void CGPathApply(IntPtr path, IntPtr info, CGPathApplierFunction function);

		public void Apply(ApplierFunction func)
		{
			GCHandle value = GCHandle.Alloc(func);
			CGPathApply(handle, GCHandle.ToIntPtr(value), ApplierCallback);
			value.Free();
		}

		private static CGPath MakeMutable(IntPtr source)
		{
			IntPtr intPtr = CGPathCreateMutableCopy(source);
			CGPathRelease(source);
			return new CGPath(intPtr, owns: true);
		}
	}
}
