using CPF.Mac.Foundation;
using CPF.Mac.ObjCRuntime;
using CPF.Mac.OpenGL;
using System;
using System.ComponentModel;

namespace CPF.Mac.AppKit
{
	[Register("NSOpenGLContext", true)]
	public class NSOpenGLContext : NSObject
	{
		private static readonly IntPtr selCurrentContextHandle = Selector.GetHandle("currentContext");

		private static readonly IntPtr selCGLContextObjHandle = Selector.GetHandle("CGLContextObj");

		private static readonly IntPtr selPixelBufferHandle = Selector.GetHandle("pixelBuffer");

		private static readonly IntPtr selPixelBufferCubeMapFaceHandle = Selector.GetHandle("pixelBufferCubeMapFace");

		private static readonly IntPtr selPixelBufferMipMapLevelHandle = Selector.GetHandle("pixelBufferMipMapLevel");

		private static readonly IntPtr selViewHandle = Selector.GetHandle("view");

		private static readonly IntPtr selSetView_Handle = Selector.GetHandle("setView:");

		private static readonly IntPtr selCurrentVirtualScreenHandle = Selector.GetHandle("currentVirtualScreen");

		private static readonly IntPtr selSetCurrentVirtualScreen_Handle = Selector.GetHandle("setCurrentVirtualScreen:");

		private static readonly IntPtr selInitWithFormatShareContext_Handle = Selector.GetHandle("initWithFormat:shareContext:");

		private static readonly IntPtr selSetFullScreenHandle = Selector.GetHandle("setFullScreen");

		private static readonly IntPtr selSetOffScreenWidthHeightRowbytes_Handle = Selector.GetHandle("setOffScreen:width:height:rowbytes:");

		private static readonly IntPtr selClearDrawableHandle = Selector.GetHandle("clearDrawable");

		private static readonly IntPtr selUpdateHandle = Selector.GetHandle("update");

		private static readonly IntPtr selFlushBufferHandle = Selector.GetHandle("flushBuffer");

		private static readonly IntPtr selMakeCurrentContextHandle = Selector.GetHandle("makeCurrentContext");

		private static readonly IntPtr selClearCurrentContextHandle = Selector.GetHandle("clearCurrentContext");

		private static readonly IntPtr selCopyAttributesFromContextWithMask_Handle = Selector.GetHandle("copyAttributesFromContext:withMask:");

		private static readonly IntPtr selSetValuesForParameter_Handle = Selector.GetHandle("setValues:forParameter:");

		private static readonly IntPtr selGetValuesForParameter_Handle = Selector.GetHandle("getValues:forParameter:");

		private static readonly IntPtr selCreateTextureFromViewInternalFormat_Handle = Selector.GetHandle("createTexture:fromView:internalFormat:");

		private static readonly IntPtr selSetPixelBufferCubeMapFaceMipMapLevelCurrentVirtualScreen_Handle = Selector.GetHandle("setPixelBuffer:cubeMapFace:mipMapLevel:currentVirtualScreen:");

		private static readonly IntPtr selSetTextureImageToPixelBufferColorBuffer_Handle = Selector.GetHandle("setTextureImageToPixelBuffer:colorBuffer:");

		private static readonly IntPtr class_ptr = Class.GetHandle("NSOpenGLContext");

		private static object __mt_CurrentContext_var_static;

		private object __mt_PixelBuffer_var;

		private object __mt_View_var;

		public bool SwapRectangleEnabled
		{
			get
			{
				return GetValue(NSOpenGLContextParameter.SwapRectangleEnable) != 0;
			}
			set
			{
				SetValue(value ? 1 : 0, NSOpenGLContextParameter.SwapRectangleEnable);
			}
		}

		public bool RasterizationEnabled
		{
			get
			{
				return GetValue(NSOpenGLContextParameter.RasterizationEnable) != 0;
			}
			set
			{
				SetValue(value ? 1 : 0, NSOpenGLContextParameter.RasterizationEnable);
			}
		}

		public bool SwapInterval
		{
			get
			{
				return GetValue(NSOpenGLContextParameter.SwapInterval) != 0;
			}
			set
			{
				SetValue(value ? 1 : 0, NSOpenGLContextParameter.SwapInterval);
			}
		}

		public NSSurfaceOrder SurfaceOrder
		{
			get
			{
				int value = GetValue(NSOpenGLContextParameter.SurfaceOrder);
				if (value == -1)
				{
					return NSSurfaceOrder.BelowWindow;
				}
				return NSSurfaceOrder.AboveWindow;
			}
			set
			{
				SetValue((value != NSSurfaceOrder.BelowWindow) ? 1 : (-1), NSOpenGLContextParameter.SurfaceOrder);
			}
		}

		public bool SurfaceOpaque
		{
			get
			{
				return GetValue(NSOpenGLContextParameter.SurfaceOpacity) != 0;
			}
			set
			{
				SetValue(value ? 1 : 0, NSOpenGLContextParameter.SurfaceOpacity);
			}
		}

		public bool StateValidation
		{
			get
			{
				return GetValue(NSOpenGLContextParameter.StateValidation) != 0;
			}
			set
			{
				SetValue(value ? 1 : 0, NSOpenGLContextParameter.StateValidation);
			}
		}

		public override IntPtr ClassHandle => class_ptr;

		public static NSOpenGLContext CurrentContext
		{
			[Export("currentContext")]
			get
			{
				NSApplication.EnsureUIThread();
				return (NSOpenGLContext)(__mt_CurrentContext_var_static = (NSOpenGLContext)Runtime.GetNSObject(Messaging.IntPtr_objc_msgSend(class_ptr, selCurrentContextHandle)));
			}
		}

		[ThreadSafe]
		public virtual CGLContext CGLContext
		{
			[Export("CGLContextObj")]
			get
			{
				if (IsDirectBinding)
				{
					return new CGLContext(Messaging.IntPtr_objc_msgSend(base.Handle, selCGLContextObjHandle));
				}
				return new CGLContext(Messaging.IntPtr_objc_msgSendSuper(base.SuperHandle, selCGLContextObjHandle));
			}
		}

		public virtual NSOpenGLPixelBuffer PixelBuffer
		{
			[Export("pixelBuffer")]
			get
			{
				NSApplication.EnsureUIThread();
				return (NSOpenGLPixelBuffer)(__mt_PixelBuffer_var = ((!IsDirectBinding) ? ((NSOpenGLPixelBuffer)Runtime.GetNSObject(Messaging.IntPtr_objc_msgSendSuper(base.SuperHandle, selPixelBufferHandle))) : ((NSOpenGLPixelBuffer)Runtime.GetNSObject(Messaging.IntPtr_objc_msgSend(base.Handle, selPixelBufferHandle)))));
			}
		}

		public virtual int PixelBufferCubeMapFace
		{
			[Export("pixelBufferCubeMapFace")]
			get
			{
				NSApplication.EnsureUIThread();
				if (IsDirectBinding)
				{
					return Messaging.int_objc_msgSend(base.Handle, selPixelBufferCubeMapFaceHandle);
				}
				return Messaging.int_objc_msgSendSuper(base.SuperHandle, selPixelBufferCubeMapFaceHandle);
			}
		}

		public virtual int PixelBufferMipMapLevel
		{
			[Export("pixelBufferMipMapLevel")]
			get
			{
				NSApplication.EnsureUIThread();
				if (IsDirectBinding)
				{
					return Messaging.int_objc_msgSend(base.Handle, selPixelBufferMipMapLevelHandle);
				}
				return Messaging.int_objc_msgSendSuper(base.SuperHandle, selPixelBufferMipMapLevelHandle);
			}
		}

		public virtual NSView View
		{
			[Export("view")]
			get
			{
				NSApplication.EnsureUIThread();
				return (NSView)(__mt_View_var = ((!IsDirectBinding) ? ((NSView)Runtime.GetNSObject(Messaging.IntPtr_objc_msgSendSuper(base.SuperHandle, selViewHandle))) : ((NSView)Runtime.GetNSObject(Messaging.IntPtr_objc_msgSend(base.Handle, selViewHandle)))));
			}
			[Export("setView:")]
			set
			{
				NSApplication.EnsureUIThread();
				if (value == null)
				{
					throw new ArgumentNullException("value");
				}
				if (IsDirectBinding)
				{
					Messaging.void_objc_msgSend_IntPtr(base.Handle, selSetView_Handle, value.Handle);
				}
				else
				{
					Messaging.void_objc_msgSendSuper_IntPtr(base.SuperHandle, selSetView_Handle, value.Handle);
				}
				__mt_View_var = value;
			}
		}

		public virtual int CurrentVirtualScreen
		{
			[Export("currentVirtualScreen")]
			get
			{
				NSApplication.EnsureUIThread();
				if (IsDirectBinding)
				{
					return Messaging.int_objc_msgSend(base.Handle, selCurrentVirtualScreenHandle);
				}
				return Messaging.int_objc_msgSendSuper(base.SuperHandle, selCurrentVirtualScreenHandle);
			}
			[Export("setCurrentVirtualScreen:")]
			set
			{
				NSApplication.EnsureUIThread();
				if (IsDirectBinding)
				{
					Messaging.void_objc_msgSend_int(base.Handle, selSetCurrentVirtualScreen_Handle, value);
				}
				else
				{
					Messaging.void_objc_msgSendSuper_int(base.SuperHandle, selSetCurrentVirtualScreen_Handle, value);
				}
			}
		}

		private unsafe void SetValue(int val, NSOpenGLContextParameter par)
		{
			int* value = &val;
			SetValues((IntPtr)(void*)value, par);
		}

		private unsafe int GetValue(NSOpenGLContextParameter par)
		{
			int result = default(int);
			int* value = &result;
			GetValues((IntPtr)(void*)value, par);
			return result;
		}

		[EditorBrowsable(EditorBrowsableState.Advanced)]
		[Export("initWithCoder:")]
		public NSOpenGLContext(NSCoder coder)
			: base(NSObjectFlag.Empty)
		{
			if (IsDirectBinding)
			{
				base.Handle = Messaging.IntPtr_objc_msgSend_IntPtr(base.Handle, Selector.InitWithCoder, coder.Handle);
			}
			else
			{
				base.Handle = Messaging.IntPtr_objc_msgSendSuper_IntPtr(base.SuperHandle, Selector.InitWithCoder, coder.Handle);
			}
		}

		[EditorBrowsable(EditorBrowsableState.Advanced)]
		public NSOpenGLContext(NSObjectFlag t)
			: base(t)
		{
		}

		[EditorBrowsable(EditorBrowsableState.Advanced)]
		public NSOpenGLContext(IntPtr handle)
			: base(handle)
		{
		}

		[Export("initWithFormat:shareContext:")]
		public NSOpenGLContext(NSOpenGLPixelFormat format, NSOpenGLContext shareContext)
			: base(NSObjectFlag.Empty)
		{
			NSApplication.EnsureUIThread();
			if (format == null)
			{
				throw new ArgumentNullException("format");
			}
			if (IsDirectBinding)
			{
				base.Handle = Messaging.IntPtr_objc_msgSend_IntPtr_IntPtr(base.Handle, selInitWithFormatShareContext_Handle, format.Handle, shareContext?.Handle ?? IntPtr.Zero);
			}
			else
			{
				base.Handle = Messaging.IntPtr_objc_msgSendSuper_IntPtr_IntPtr(base.SuperHandle, selInitWithFormatShareContext_Handle, format.Handle, shareContext?.Handle ?? IntPtr.Zero);
			}
		}

		[Export("setFullScreen")]
		public virtual void SetFullScreen()
		{
			NSApplication.EnsureUIThread();
			if (IsDirectBinding)
			{
				Messaging.void_objc_msgSend(base.Handle, selSetFullScreenHandle);
			}
			else
			{
				Messaging.void_objc_msgSendSuper(base.SuperHandle, selSetFullScreenHandle);
			}
		}

		[Export("setOffScreen:width:height:rowbytes:")]
		public virtual void SetOffScreen(IntPtr baseaddr, int width, int height, int rowbytes)
		{
			NSApplication.EnsureUIThread();
			if (IsDirectBinding)
			{
				Messaging.void_objc_msgSend_IntPtr_int_int_int(base.Handle, selSetOffScreenWidthHeightRowbytes_Handle, baseaddr, width, height, rowbytes);
			}
			else
			{
				Messaging.void_objc_msgSendSuper_IntPtr_int_int_int(base.SuperHandle, selSetOffScreenWidthHeightRowbytes_Handle, baseaddr, width, height, rowbytes);
			}
		}

		[Export("clearDrawable")]
		public virtual void ClearDrawable()
		{
			NSApplication.EnsureUIThread();
			if (IsDirectBinding)
			{
				Messaging.void_objc_msgSend(base.Handle, selClearDrawableHandle);
			}
			else
			{
				Messaging.void_objc_msgSendSuper(base.SuperHandle, selClearDrawableHandle);
			}
		}

		[Export("update")]
		public virtual void Update()
		{
			NSApplication.EnsureUIThread();
			if (IsDirectBinding)
			{
				Messaging.void_objc_msgSend(base.Handle, selUpdateHandle);
			}
			else
			{
				Messaging.void_objc_msgSendSuper(base.SuperHandle, selUpdateHandle);
			}
		}

		[Export("flushBuffer")]
		[ThreadSafe]
		public virtual void FlushBuffer()
		{
			if (IsDirectBinding)
			{
				Messaging.void_objc_msgSend(base.Handle, selFlushBufferHandle);
			}
			else
			{
				Messaging.void_objc_msgSendSuper(base.SuperHandle, selFlushBufferHandle);
			}
		}

		[Export("makeCurrentContext")]
		[ThreadSafe]
		public virtual void MakeCurrentContext()
		{
			if (IsDirectBinding)
			{
				Messaging.void_objc_msgSend(base.Handle, selMakeCurrentContextHandle);
			}
			else
			{
				Messaging.void_objc_msgSendSuper(base.SuperHandle, selMakeCurrentContextHandle);
			}
		}

		[Export("clearCurrentContext")]
		public static void ClearCurrentContext()
		{
			NSApplication.EnsureUIThread();
			Messaging.void_objc_msgSend(class_ptr, selClearCurrentContextHandle);
		}

		[Export("copyAttributesFromContext:withMask:")]
		public virtual void CopyAttributes(NSOpenGLContext context, uint mask)
		{
			NSApplication.EnsureUIThread();
			if (context == null)
			{
				throw new ArgumentNullException("context");
			}
			if (IsDirectBinding)
			{
				Messaging.void_objc_msgSend_IntPtr_UInt32(base.Handle, selCopyAttributesFromContextWithMask_Handle, context.Handle, mask);
			}
			else
			{
				Messaging.void_objc_msgSendSuper_IntPtr_UInt32(base.SuperHandle, selCopyAttributesFromContextWithMask_Handle, context.Handle, mask);
			}
		}

		[Export("setValues:forParameter:")]
		public virtual void SetValues(IntPtr vals, NSOpenGLContextParameter param)
		{
			NSApplication.EnsureUIThread();
			if (IsDirectBinding)
			{
				Messaging.void_objc_msgSend_IntPtr_Int64(base.Handle, selSetValuesForParameter_Handle, vals, (long)param);
			}
			else
			{
				Messaging.void_objc_msgSendSuper_IntPtr_Int64(base.SuperHandle, selSetValuesForParameter_Handle, vals, (long)param);
			}
		}

		[Export("getValues:forParameter:")]
		public virtual void GetValues(IntPtr vals, NSOpenGLContextParameter param)
		{
			NSApplication.EnsureUIThread();
			if (IsDirectBinding)
			{
				Messaging.void_objc_msgSend_IntPtr_Int64(base.Handle, selGetValuesForParameter_Handle, vals, (long)param);
			}
			else
			{
				Messaging.void_objc_msgSendSuper_IntPtr_Int64(base.SuperHandle, selGetValuesForParameter_Handle, vals, (long)param);
			}
		}

		[Export("createTexture:fromView:internalFormat:")]
		public virtual void CreateTexture(int targetIdentifier, NSView view, int format)
		{
			NSApplication.EnsureUIThread();
			if (view == null)
			{
				throw new ArgumentNullException("view");
			}
			if (IsDirectBinding)
			{
				Messaging.void_objc_msgSend_int_IntPtr_int(base.Handle, selCreateTextureFromViewInternalFormat_Handle, targetIdentifier, view.Handle, format);
			}
			else
			{
				Messaging.void_objc_msgSendSuper_int_IntPtr_int(base.SuperHandle, selCreateTextureFromViewInternalFormat_Handle, targetIdentifier, view.Handle, format);
			}
		}

		[Export("setPixelBuffer:cubeMapFace:mipMapLevel:currentVirtualScreen:")]
		public virtual void SetPixelBuffer(NSOpenGLPixelBuffer pixelBuffer, NSGLTextureCubeMap face, int level, int screen)
		{
			NSApplication.EnsureUIThread();
			if (pixelBuffer == null)
			{
				throw new ArgumentNullException("pixelBuffer");
			}
			if (IsDirectBinding)
			{
				Messaging.void_objc_msgSend_IntPtr_int_int_int(base.Handle, selSetPixelBufferCubeMapFaceMipMapLevelCurrentVirtualScreen_Handle, pixelBuffer.Handle, (int)face, level, screen);
			}
			else
			{
				Messaging.void_objc_msgSendSuper_IntPtr_int_int_int(base.SuperHandle, selSetPixelBufferCubeMapFaceMipMapLevelCurrentVirtualScreen_Handle, pixelBuffer.Handle, (int)face, level, screen);
			}
		}

		[Export("setTextureImageToPixelBuffer:colorBuffer:")]
		public virtual void SetTextureImage(NSOpenGLPixelBuffer pixelBuffer, NSGLColorBuffer source)
		{
			NSApplication.EnsureUIThread();
			if (pixelBuffer == null)
			{
				throw new ArgumentNullException("pixelBuffer");
			}
			if (IsDirectBinding)
			{
				Messaging.void_objc_msgSend_IntPtr_int(base.Handle, selSetTextureImageToPixelBufferColorBuffer_Handle, pixelBuffer.Handle, (int)source);
			}
			else
			{
				Messaging.void_objc_msgSendSuper_IntPtr_int(base.SuperHandle, selSetTextureImageToPixelBufferColorBuffer_Handle, pixelBuffer.Handle, (int)source);
			}
		}

		protected override void Dispose(bool disposing)
		{
			base.Dispose(disposing);
			if (base.Handle == IntPtr.Zero)
			{
				__mt_PixelBuffer_var = null;
				__mt_View_var = null;
			}
		}
	}
}
