using CPF.Mac.CoreFoundation;
using CPF.Mac.ObjCRuntime;
using System;
using System.ComponentModel;

namespace CPF.Mac.Foundation
{
	[Register("NSRunLoop", true)]
	public class NSRunLoop : NSObject
	{
		private static readonly IntPtr selCurrentRunLoopHandle = Selector.GetHandle("currentRunLoop");

		private static readonly IntPtr selMainRunLoopHandle = Selector.GetHandle("mainRunLoop");

		private static readonly IntPtr selCurrentModeHandle = Selector.GetHandle("currentMode");

		private static readonly IntPtr selGetCFRunLoopHandle = Selector.GetHandle("getCFRunLoop");

		private static readonly IntPtr selAddTimerForMode_Handle = Selector.GetHandle("addTimer:forMode:");

		private static readonly IntPtr selLimitDateForMode_Handle = Selector.GetHandle("limitDateForMode:");

		private static readonly IntPtr selAcceptInputForModeBeforeDate_Handle = Selector.GetHandle("acceptInputForMode:beforeDate:");

		private static readonly IntPtr selRunHandle = Selector.GetHandle("run");

		private static readonly IntPtr selRunUntilDate_Handle = Selector.GetHandle("runUntilDate:");

		private static readonly IntPtr selRunModeBeforeDate_Handle = Selector.GetHandle("runMode:beforeDate:");

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

		[ThreadStatic]
		private static object __mt_Current_var_static;

		private static object __mt_Main_var_static;

		private object __mt_CurrentMode_var;

		private static NSString _NSDefaultRunLoopMode;

		private static NSString _NSRunLoopCommonModes;

		private static NSString _NSRunLoopConnectionReplyMode;

		private static NSString _NSRunLoopModalPanelMode;

		private static NSString _NSRunLoopEventTracking;

		public NSRunLoopMode CurrentRunLoopMode
		{
			get
			{
				NSString currentMode = CurrentMode;
				if (currentMode == NSDefaultRunLoopMode)
				{
					return NSRunLoopMode.Default;
				}
				if (currentMode == NSRunLoopCommonModes)
				{
					return NSRunLoopMode.Common;
				}
				if (currentMode == NSRunLoopConnectionReplyMode)
				{
					return NSRunLoopMode.ConnectionReply;
				}
				if (currentMode == NSRunLoopModalPanelMode)
				{
					return NSRunLoopMode.ModalPanel;
				}
				if (currentMode == NSRunLoopEventTracking)
				{
					return NSRunLoopMode.EventTracking;
				}
				return NSRunLoopMode.Other;
			}
		}

		public override IntPtr ClassHandle => class_ptr;

		public static NSRunLoop Current
		{
			[Export("currentRunLoop")]
			get
			{
				return (NSRunLoop)(__mt_Current_var_static = (NSRunLoop)Runtime.GetNSObject(Messaging.IntPtr_objc_msgSend(class_ptr, selCurrentRunLoopHandle)));
			}
		}

		public static NSRunLoop Main
		{
			[Export("mainRunLoop")]
			get
			{
				return (NSRunLoop)(__mt_Main_var_static = (NSRunLoop)Runtime.GetNSObject(Messaging.IntPtr_objc_msgSend(class_ptr, selMainRunLoopHandle)));
			}
		}

		public virtual NSString CurrentMode
		{
			[Export("currentMode")]
			get
			{
				return (NSString)(__mt_CurrentMode_var = ((!IsDirectBinding) ? ((NSString)Runtime.GetNSObject(Messaging.IntPtr_objc_msgSendSuper(base.SuperHandle, selCurrentModeHandle))) : ((NSString)Runtime.GetNSObject(Messaging.IntPtr_objc_msgSend(base.Handle, selCurrentModeHandle)))));
			}
		}

		[Field("NSDefaultRunLoopMode", "Foundation")]
		public static NSString NSDefaultRunLoopMode
		{
			get
			{
				if (_NSDefaultRunLoopMode == null)
				{
					_NSDefaultRunLoopMode = Dlfcn.GetStringConstant(Libraries.Foundation.Handle, "NSDefaultRunLoopMode");
				}
				return _NSDefaultRunLoopMode;
			}
		}

		[Field("NSRunLoopCommonModes", "Foundation")]
		public static NSString NSRunLoopCommonModes
		{
			get
			{
				if (_NSRunLoopCommonModes == null)
				{
					_NSRunLoopCommonModes = Dlfcn.GetStringConstant(Libraries.Foundation.Handle, "NSRunLoopCommonModes");
				}
				return _NSRunLoopCommonModes;
			}
		}

		[Field("NSConnectionReplyMode", "Foundation")]
		public static NSString NSRunLoopConnectionReplyMode
		{
			get
			{
				if (_NSRunLoopConnectionReplyMode == null)
				{
					_NSRunLoopConnectionReplyMode = Dlfcn.GetStringConstant(Libraries.Foundation.Handle, "NSConnectionReplyMode");
				}
				return _NSRunLoopConnectionReplyMode;
			}
		}

		[Field("NSModalPanelRunLoopMode", "AppKit")]
		public static NSString NSRunLoopModalPanelMode
		{
			get
			{
				if (_NSRunLoopModalPanelMode == null)
				{
					_NSRunLoopModalPanelMode = Dlfcn.GetStringConstant(Libraries.AppKit.Handle, "NSModalPanelRunLoopMode");
				}
				return _NSRunLoopModalPanelMode;
			}
		}

		[Field("NSEventTrackingRunLoopMode", "AppKit")]
		public static NSString NSRunLoopEventTracking
		{
			get
			{
				if (_NSRunLoopEventTracking == null)
				{
					_NSRunLoopEventTracking = Dlfcn.GetStringConstant(Libraries.AppKit.Handle, "NSEventTrackingRunLoopMode");
				}
				return _NSRunLoopEventTracking;
			}
		}

		private static NSString GetRealMode(string mode)
		{
			if (mode == (string)NSDefaultRunLoopMode)
			{
				return NSDefaultRunLoopMode;
			}
			if (mode == (string)NSRunLoopCommonModes)
			{
				return NSRunLoopCommonModes;
			}
			return new NSString(mode);
		}

		private static NSString FromEnum(NSRunLoopMode mode)
		{
			switch (mode)
			{
			case NSRunLoopMode.Common:
				return NSRunLoopCommonModes;
			case NSRunLoopMode.ConnectionReply:
				return NSRunLoopConnectionReplyMode;
			case NSRunLoopMode.ModalPanel:
				return NSRunLoopModalPanelMode;
			case NSRunLoopMode.EventTracking:
				return NSRunLoopEventTracking;
			default:
				return NSDefaultRunLoopMode;
			}
		}

		[Advice("Use AddTimer (NSTimer, NSRunLoopMode)")]
		public void AddTimer(NSTimer timer, string forMode)
		{
			AddTimer(timer, GetRealMode(forMode));
		}

		public void AddTimer(NSTimer timer, NSRunLoopMode forMode)
		{
			AddTimer(timer, FromEnum(forMode));
		}

		[Advice("Use LimitDateForMode (NSRunLoopMode) instead")]
		public NSDate LimitDateForMode(string mode)
		{
			return LimitDateForMode(GetRealMode(mode));
		}

		public NSDate LimitDateForMode(NSRunLoopMode mode)
		{
			return LimitDateForMode(FromEnum(mode));
		}

		[Advice("Use AcceptInputForMode (NSRunLoopMode, NSDate)")]
		public void AcceptInputForMode(string mode, NSDate limitDate)
		{
			AcceptInputForMode(GetRealMode(mode), limitDate);
		}

		public void AcceptInputForMode(NSRunLoopMode mode, NSDate limitDate)
		{
			AcceptInputForMode(FromEnum(mode), limitDate);
		}

		public void Stop()
		{
			GetCFRunLoop().Stop();
		}

		public void WakeUp()
		{
			GetCFRunLoop().WakeUp();
		}

		public bool RunUntil(NSRunLoopMode mode, NSDate limitDate)
		{
			return RunUntil(FromEnum(mode), limitDate);
		}

		[EditorBrowsable(EditorBrowsableState.Advanced)]
		[Export("initWithCoder:")]
		public NSRunLoop(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 NSRunLoop(NSObjectFlag t)
			: base(t)
		{
		}

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

		[Export("getCFRunLoop")]
		public virtual CFRunLoop GetCFRunLoop()
		{
			if (IsDirectBinding)
			{
				return new CFRunLoop(Messaging.IntPtr_objc_msgSend(base.Handle, selGetCFRunLoopHandle));
			}
			return new CFRunLoop(Messaging.IntPtr_objc_msgSendSuper(base.SuperHandle, selGetCFRunLoopHandle));
		}

		[Export("addTimer:forMode:")]
		public virtual void AddTimer(NSTimer timer, NSString forMode)
		{
			if (timer == null)
			{
				throw new ArgumentNullException("timer");
			}
			if (forMode == null)
			{
				throw new ArgumentNullException("forMode");
			}
			if (IsDirectBinding)
			{
				Messaging.void_objc_msgSend_IntPtr_IntPtr(base.Handle, selAddTimerForMode_Handle, timer.Handle, forMode.Handle);
			}
			else
			{
				Messaging.void_objc_msgSendSuper_IntPtr_IntPtr(base.SuperHandle, selAddTimerForMode_Handle, timer.Handle, forMode.Handle);
			}
		}

		[Export("limitDateForMode:")]
		public virtual NSDate LimitDateForMode(NSString mode)
		{
			if (mode == null)
			{
				throw new ArgumentNullException("mode");
			}
			if (IsDirectBinding)
			{
				return (NSDate)Runtime.GetNSObject(Messaging.IntPtr_objc_msgSend_IntPtr(base.Handle, selLimitDateForMode_Handle, mode.Handle));
			}
			return (NSDate)Runtime.GetNSObject(Messaging.IntPtr_objc_msgSendSuper_IntPtr(base.SuperHandle, selLimitDateForMode_Handle, mode.Handle));
		}

		[Export("acceptInputForMode:beforeDate:")]
		public virtual void AcceptInputForMode(NSString mode, NSDate limitDate)
		{
			if (mode == null)
			{
				throw new ArgumentNullException("mode");
			}
			if (limitDate == null)
			{
				throw new ArgumentNullException("limitDate");
			}
			if (IsDirectBinding)
			{
				Messaging.void_objc_msgSend_IntPtr_IntPtr(base.Handle, selAcceptInputForModeBeforeDate_Handle, mode.Handle, limitDate.Handle);
			}
			else
			{
				Messaging.void_objc_msgSendSuper_IntPtr_IntPtr(base.SuperHandle, selAcceptInputForModeBeforeDate_Handle, mode.Handle, limitDate.Handle);
			}
		}

		[Export("run")]
		public virtual void Run()
		{
			if (IsDirectBinding)
			{
				Messaging.void_objc_msgSend(base.Handle, selRunHandle);
			}
			else
			{
				Messaging.void_objc_msgSendSuper(base.SuperHandle, selRunHandle);
			}
		}

		[Export("runUntilDate:")]
		public virtual void RunUntil(NSDate date)
		{
			if (date == null)
			{
				throw new ArgumentNullException("date");
			}
			if (IsDirectBinding)
			{
				Messaging.void_objc_msgSend_IntPtr(base.Handle, selRunUntilDate_Handle, date.Handle);
			}
			else
			{
				Messaging.void_objc_msgSendSuper_IntPtr(base.SuperHandle, selRunUntilDate_Handle, date.Handle);
			}
		}

		[Export("runMode:beforeDate:")]
		public virtual bool RunUntil(NSString runLoopMode, NSDate limitdate)
		{
			if (runLoopMode == null)
			{
				throw new ArgumentNullException("runLoopMode");
			}
			if (limitdate == null)
			{
				throw new ArgumentNullException("limitdate");
			}
			if (IsDirectBinding)
			{
				return Messaging.bool_objc_msgSend_IntPtr_IntPtr(base.Handle, selRunModeBeforeDate_Handle, runLoopMode.Handle, limitdate.Handle);
			}
			return Messaging.bool_objc_msgSendSuper_IntPtr_IntPtr(base.SuperHandle, selRunModeBeforeDate_Handle, runLoopMode.Handle, limitdate.Handle);
		}

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