using CPF.Mac.CoreFoundation;
using CPF.Mac.Foundation;
using CPF.Mac.ObjCRuntime;
using System;
using System.Runtime.InteropServices;
using System.Security.Cryptography.X509Certificates;

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

		public string SubjectSummary
		{
			get
			{
				if (handle == IntPtr.Zero)
				{
					throw new ObjectDisposedException("SecCertificate");
				}
				IntPtr obj = SecCertificateCopySubjectSummary(handle);
				string result = CFString.FetchString(obj);
				CFObject.CFRelease(obj);
				return result;
			}
		}

		public NSData DerData
		{
			get
			{
				if (handle == IntPtr.Zero)
				{
					throw new ObjectDisposedException("SecCertificate");
				}
				IntPtr value = SecCertificateCopyData(handle);
				if (value == IntPtr.Zero)
				{
					throw new ArgumentException("Not a valid certificate");
				}
				return new NSData(value);
			}
		}

		public IntPtr Handle => handle;

		internal SecCertificate(IntPtr handle)
			: this(handle, owns: false)
		{
		}

		[Preserve(Conditional = true)]
		internal SecCertificate(IntPtr handle, bool owns)
		{
			if (handle == IntPtr.Zero)
			{
				throw new Exception("Invalid handle");
			}
			this.handle = handle;
			if (!owns)
			{
				CFObject.CFRetain(handle);
			}
		}

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

		[DllImport("/System/Library/Frameworks/Security.framework/Security")]
		private static extern IntPtr SecCertificateCreateWithData(IntPtr allocator, IntPtr cfData);

		public SecCertificate(NSData data)
		{
			if (data == null)
			{
				throw new ArgumentNullException("data");
			}
			Initialize(data);
		}

		public SecCertificate(byte[] data)
		{
			if (data == null)
			{
				throw new ArgumentNullException("data");
			}
			using (NSData data2 = NSData.FromArray(data))
			{
				Initialize(data2);
			}
		}

		public SecCertificate(X509Certificate certificate)
		{
			if (certificate == null)
			{
				throw new ArgumentNullException("certificate");
			}
			using (NSData data = NSData.FromArray(certificate.Export(X509ContentType.Cert)))
			{
				Initialize(data);
			}
		}

		public SecCertificate(X509Certificate2 certificate)
		{
			if (certificate == null)
			{
				throw new ArgumentNullException("certificate");
			}
			using (NSData data = NSData.FromArray(certificate.RawData))
			{
				Initialize(data);
			}
		}

		private void Initialize(NSData data)
		{
			handle = SecCertificateCreateWithData(IntPtr.Zero, data.Handle);
			if (handle == IntPtr.Zero)
			{
				throw new ArgumentException("Not a valid DER-encoded X.509 certificate");
			}
		}

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

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

		~SecCertificate()
		{
			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;
			}
		}
	}
}
