// 
// CoreFoundation.cs
//  
// Author:
//       Michael Hutchinson <mhutchinson@novell.com>
//       Miguel de Icaza
// 
// Copyright (c) 2009 Novell, Inc. (http://www.novell.com)
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

using System;
using System.Runtime.InteropServices;

namespace Pinta.MacInterop;

internal static partial class CoreFoundation
{
	const string CFLib = "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation";
	const string LSLib = "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices";

	[DllImport (CFLib)]
	private static extern IntPtr CFStringCreateWithCString (IntPtr alloc, string str, int encoding);

	public static IntPtr CreateString (string s)
	{
		// The magic value is "kCFStringENcodingUTF8"
		return CFStringCreateWithCString (IntPtr.Zero, s, 0x08000100);
	}

	[LibraryImport (CFLib, EntryPoint = "CFRelease")]
	public static partial void Release (IntPtr cfRef);

	struct CFRange
	{
		public IntPtr Location, Length;
		public CFRange (int l, int len)
		{
			Location = l;
			Length = len;
		}
	}

	[LibraryImport (CFLib)]
	private static partial int CFStringGetLength (IntPtr handle);

	[LibraryImport (CFLib)]
	private static partial IntPtr CFStringGetCharactersPtr (IntPtr handle);

	[LibraryImport (CFLib)]
	private static partial IntPtr CFStringGetCharacters (IntPtr handle, CFRange range, IntPtr buffer);

	public static string? FetchString (IntPtr handle)
	{
		if (handle == IntPtr.Zero)
			return null;

		string str;

		int l = CFStringGetLength (handle);
		IntPtr u = CFStringGetCharactersPtr (handle);
		IntPtr buffer = IntPtr.Zero;
		if (u == IntPtr.Zero) {
			CFRange r = new CFRange (0, l);
			buffer = Marshal.AllocCoTaskMem (l * 2);
			CFStringGetCharacters (handle, r, buffer);
			u = buffer;
		}
		unsafe {
			str = new string ((char*) u, 0, l);
		}

		if (buffer != IntPtr.Zero)
			Marshal.FreeCoTaskMem (buffer);

		return str;
	}

	public static string? FSRefToString (ref FSRef fsref)
	{
		IntPtr url = IntPtr.Zero;
		IntPtr str = IntPtr.Zero;
		try {
			url = CFURLCreateFromFSRef (IntPtr.Zero, ref fsref);
			if (url == IntPtr.Zero)
				return null;
			str = CFURLCopyFileSystemPath (url, CFUrlPathStyle.Posix);
			if (str == IntPtr.Zero)
				return null;
			return FetchString (str);
		} finally {
			if (url != IntPtr.Zero)
				Release (url);
			if (str != IntPtr.Zero)
				Release (str);
		}
	}

	[LibraryImport (CFLib)]
	private static partial IntPtr CFURLCreateFromFSRef (IntPtr allocator, ref FSRef fsref);

	[LibraryImport (CFLib)]
	private static partial IntPtr CFURLCopyFileSystemPath (IntPtr urlRef, CFUrlPathStyle pathStyle);

	enum CFUrlPathStyle
	{
		Posix = 0,
		Hfs = 1,
		Windows = 2
	};

	[LibraryImport (CFLib)]
	private static partial IntPtr CFURLCreateWithFileSystemPath (IntPtr allocator, IntPtr filePathString,
		CFUrlPathStyle pathStyle, [MarshalAs (UnmanagedType.Bool)] bool isDirectory);

	[LibraryImport (LSLib)]
	private static partial IntPtr LSCopyApplicationURLsForURL (IntPtr urlRef, LSRolesMask roleMask); //CFArrayRef

	[LibraryImport (LSLib)]
	private static partial int LSGetApplicationForURL (IntPtr url, LSRolesMask roleMask, IntPtr fsRefZero,
		ref IntPtr appUrl);

	[LibraryImport (CFLib)]
	private static partial int CFArrayGetCount (IntPtr theArray);

	[LibraryImport (CFLib)]
	private static partial IntPtr CFArrayGetValueAtIndex (IntPtr theArray, int idx);

	[Flags]
	public enum LSRolesMask : uint
	{
		None = 0x00000001,
		Viewer = 0x00000002,
		Editor = 0x00000004,
		Shell = 0x00000008,
		All = 0xFFFFFFFF
	}
}
