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

namespace CPF.Mac.Security
{
	public class SecKey : INativeObject, IDisposable
	{
		internal IntPtr handle;

		private long BlockSize
		{
			get
			{
				if (handle == IntPtr.Zero)
				{
					throw new ObjectDisposedException("SecKey");
				}
				return (long)SecKeyGetBlockSize(handle);
			}
		}

		public IntPtr Handle => handle;

		public SecKey(IntPtr handle)
			: this(handle, owns: false)
		{
		}

		[Preserve(Conditional = true)]
		public SecKey(IntPtr handle, bool owns)
		{
			this.handle = handle;
			if (!owns)
			{
				CFObject.CFRetain(handle);
			}
		}

		[DllImport("/System/Library/Frameworks/Security.framework/Security", EntryPoint = "SecKeyGetTypeID")]
		public static extern int GetTypeID();

		[DllImport("/System/Library/Frameworks/Security.framework/Security")]
		private static extern SecStatusCode SecKeyGeneratePair(IntPtr dictHandle, out IntPtr pubKey, out IntPtr privKey);

		public static SecStatusCode GenerateKeyPair(NSDictionary parameters, out SecKey publicKey, out SecKey privateKey)
		{
			if (parameters == null)
			{
				throw new ArgumentNullException("parameters");
			}
			IntPtr pubKey;
			IntPtr privKey;
			SecStatusCode num = SecKeyGeneratePair(parameters.Handle, out pubKey, out privKey);
			if (num == SecStatusCode.Success)
			{
				publicKey = new SecKey(pubKey, owns: true);
				privateKey = new SecKey(privKey, owns: true);
				return num;
			}
			publicKey = (privateKey = null);
			return num;
		}

		[DllImport("/System/Library/Frameworks/Security.framework/Security")]
		private static extern IntPtr SecKeyGetBlockSize(IntPtr handle);

		[DllImport("/System/Library/Frameworks/Security.framework/Security")]
		private static extern SecStatusCode SecKeyRawSign(IntPtr handle, SecPadding padding, IntPtr dataToSign, IntPtr dataToSignLen, IntPtr sig, IntPtr sigLen);

		private unsafe SecStatusCode RawSign(SecPadding padding, IntPtr dataToSign, int dataToSignLen, out byte[] result)
		{
			if (handle == IntPtr.Zero)
			{
				throw new ObjectDisposedException("SecKey");
			}
			result = new byte[(int)SecKeyGetBlockSize(handle)];
			fixed (byte* value = &result[0])
			{
				return SecKeyRawSign(handle, padding, dataToSign, (IntPtr)dataToSignLen, (IntPtr)(void*)value, (IntPtr)result.Length);
			}
		}

		private unsafe SecStatusCode RawSign(SecPadding padding, byte[] dataToSign, out byte[] result)
		{
			if (handle == IntPtr.Zero)
			{
				throw new ObjectDisposedException("SecKey");
			}
			if (dataToSign == null)
			{
				throw new ArgumentNullException("dataToSign");
			}
			result = new byte[(int)SecKeyGetBlockSize(handle)];
			fixed (byte* value = &dataToSign[0])
			{
				fixed (byte* value2 = &result[0])
				{
					return SecKeyRawSign(handle, padding, (IntPtr)(void*)value, (IntPtr)dataToSign.Length, (IntPtr)(void*)value2, (IntPtr)result.Length);
				}
			}
		}

		[DllImport("/System/Library/Frameworks/Security.framework/Security")]
		private static extern SecStatusCode SecKeyRawVerify(IntPtr handle, SecPadding padding, IntPtr signedData, IntPtr signedLen, IntPtr sign, IntPtr signLen);

		public SecStatusCode RawVerify(SecPadding padding, IntPtr signedData, int signedDataLen, IntPtr signature, int signatureLen)
		{
			if (handle == IntPtr.Zero)
			{
				throw new ObjectDisposedException("SecKey");
			}
			return SecKeyRawVerify(handle, padding, signedData, (IntPtr)signedDataLen, signature, (IntPtr)signatureLen);
		}

		public unsafe SecStatusCode RawVerify(SecPadding padding, byte[] signedData, byte[] signature)
		{
			if (handle == IntPtr.Zero)
			{
				throw new ObjectDisposedException("SecKey");
			}
			if (signature == null)
			{
				throw new ArgumentNullException("signature");
			}
			if (signedData == null)
			{
				throw new ArgumentNullException("signedData");
			}
			fixed (byte* value2 = &signature[0])
			{
				fixed (byte* value = &signedData[0])
				{
					return SecKeyRawVerify(handle, padding, (IntPtr)(void*)value, (IntPtr)signedData.Length, (IntPtr)(void*)value2, (IntPtr)signature.Length);
				}
			}
		}

		[DllImport("/System/Library/Frameworks/Security.framework/Security")]
		private static extern SecStatusCode SecKeyEncrypt(IntPtr handle, SecPadding padding, IntPtr plainText, IntPtr playLen, IntPtr cipherText, IntPtr cipherLen);

		public SecStatusCode Encrypt(SecPadding padding, IntPtr plainText, int playLen, IntPtr cipherText, int cipherLen)
		{
			if (handle == IntPtr.Zero)
			{
				throw new ObjectDisposedException("SecKey");
			}
			return SecKeyEncrypt(handle, padding, plainText, (IntPtr)playLen, cipherText, (IntPtr)cipherLen);
		}

		public unsafe SecStatusCode Encrypt(SecPadding padding, byte[] plainText, byte[] cipherText)
		{
			if (handle == IntPtr.Zero)
			{
				throw new ObjectDisposedException("SecKey");
			}
			if (cipherText == null)
			{
				throw new ArgumentNullException("cipherText");
			}
			if (plainText == null)
			{
				throw new ArgumentNullException("plainText");
			}
			fixed (byte* value2 = &cipherText[0])
			{
				fixed (byte* value = &plainText[0])
				{
					return SecKeyEncrypt(handle, padding, (IntPtr)(void*)value, (IntPtr)plainText.Length, (IntPtr)(void*)value2, (IntPtr)cipherText.Length);
				}
			}
		}

		[DllImport("/System/Library/Frameworks/Security.framework/Security")]
		private static extern SecStatusCode SecKeyDecrypt(IntPtr handle, SecPadding padding, IntPtr cipherText, IntPtr cipherLen, IntPtr plainText, IntPtr playLen);

		public SecStatusCode Decrypt(SecPadding padding, IntPtr cipherText, int cipherLen, IntPtr plainText, int playLen)
		{
			if (handle == IntPtr.Zero)
			{
				throw new ObjectDisposedException("SecKey");
			}
			return SecKeyDecrypt(handle, padding, cipherText, (IntPtr)cipherLen, plainText, (IntPtr)playLen);
		}

		public unsafe SecStatusCode Decrypt(SecPadding padding, byte[] cipherText, byte[] plainText)
		{
			if (handle == IntPtr.Zero)
			{
				throw new ObjectDisposedException("SecKey");
			}
			if (cipherText == null)
			{
				throw new ArgumentNullException("cipherText");
			}
			if (plainText == null)
			{
				throw new ArgumentNullException("plainText");
			}
			fixed (byte* value = &cipherText[0])
			{
				fixed (byte* value2 = &plainText[0])
				{
					return SecKeyDecrypt(handle, padding, (IntPtr)(void*)value, (IntPtr)cipherText.Length, (IntPtr)(void*)value2, (IntPtr)plainText.Length);
				}
			}
		}

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

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

		public virtual void Dispose(bool disposing)
		{
			if (handle != IntPtr.Zero)
			{
				CFObject.CFRelease(handle);
				handle = IntPtr.Zero;
			}
		}
	}
}
