// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Runtime.InteropServices;
using System.Text;

namespace KeyboardManagerEditorUI.Interop
{
    public static class KeyboardManagerInterop
    {
        private const string DllName = "Powertoys.KeyboardManagerEditorLibraryWrapper.dll";
        private const CallingConvention Convention = CallingConvention.Cdecl;

        // Configuration Management
        [DllImport(DllName, CallingConvention = Convention)]
        internal static extern IntPtr CreateMappingConfiguration();

        [DllImport(DllName, CallingConvention = Convention)]
        internal static extern void DestroyMappingConfiguration(IntPtr config);

        [DllImport(DllName, CallingConvention = Convention)]
        [return: MarshalAs(UnmanagedType.Bool)]
        internal static extern bool LoadMappingSettings(IntPtr config);

        [DllImport(DllName, CallingConvention = Convention)]
        [return: MarshalAs(UnmanagedType.Bool)]
        internal static extern bool SaveMappingSettings(IntPtr config);

        // Get Mapping Functions
        [DllImport(DllName, CallingConvention = Convention)]
        internal static extern int GetSingleKeyRemapCount(IntPtr config);

        [DllImport(DllName, CallingConvention = Convention)]
        [return: MarshalAs(UnmanagedType.Bool)]
        internal static extern bool GetSingleKeyRemap(IntPtr config, int index, ref SingleKeyMapping mapping);

        [DllImport(DllName, CallingConvention = Convention)]
        internal static extern int GetSingleKeyToTextRemapCount(IntPtr config);

        [DllImport(DllName, CallingConvention = Convention)]
        [return: MarshalAs(UnmanagedType.Bool)]
        internal static extern bool GetSingleKeyToTextRemap(IntPtr config, int index, ref KeyboardTextMapping mapping);

        [DllImport(DllName, CallingConvention = Convention)]
        internal static extern int GetShortcutRemapCount(IntPtr config);

        [DllImport(DllName, CallingConvention = Convention)]
        [return: MarshalAs(UnmanagedType.Bool)]
        internal static extern bool GetShortcutRemap(IntPtr config, int index, ref ShortcutMapping mapping);

        [DllImport(DllName, CallingConvention = Convention)]
        internal static extern int GetShortcutRemapCountByType(IntPtr config, int operationType);

        [DllImport(DllName, CallingConvention = Convention)]
        [return: MarshalAs(UnmanagedType.Bool)]
        internal static extern bool GetShortcutRemapByType(IntPtr config, int operationType, int index, ref ShortcutMapping mapping);

        // Add Mapping Functions
        [DllImport(DllName, CallingConvention = Convention)]
        [return: MarshalAs(UnmanagedType.Bool)]
        internal static extern bool AddSingleKeyRemap(IntPtr config, int originalKey, int targetKey);

        [DllImport(DllName, CallingConvention = Convention, CharSet = CharSet.Unicode)]
        [return: MarshalAs(UnmanagedType.Bool)]
        internal static extern bool AddSingleKeyToTextRemap(IntPtr config, int originalKey, [MarshalAs(UnmanagedType.LPWStr)] string targetText);

        [DllImport(DllName, CallingConvention = Convention, CharSet = CharSet.Unicode)]
        [return: MarshalAs(UnmanagedType.Bool)]
        internal static extern bool AddSingleKeyToShortcutRemap(IntPtr config, int originalKey, [MarshalAs(UnmanagedType.LPWStr)] string targetKeys);

        [DllImport(DllName, CallingConvention = Convention, CharSet = CharSet.Unicode)]
        [return: MarshalAs(UnmanagedType.Bool)]
        internal static extern bool AddShortcutRemap(
            IntPtr config,
            [MarshalAs(UnmanagedType.LPWStr)] string originalKeys,
            [MarshalAs(UnmanagedType.LPWStr)] string targetKeys,
            [MarshalAs(UnmanagedType.LPWStr)] string targetApp,
            int operationType = 0,
            [MarshalAs(UnmanagedType.LPWStr)] string appPathOrUri = "",
            [MarshalAs(UnmanagedType.LPWStr)] string? args = null,
            [MarshalAs(UnmanagedType.LPWStr)] string? startDirectory = null,
            int elevation = 0,
            int ifRunningAction = 0,
            int visibility = 0);

        // Delete Mapping Functions
        [DllImport(DllName, CallingConvention = Convention)]
        [return: MarshalAs(UnmanagedType.Bool)]
        internal static extern bool DeleteSingleKeyRemap(IntPtr mappingConfiguration, int originalKey);

        [DllImport(DllName, CallingConvention = Convention)]
        [return: MarshalAs(UnmanagedType.Bool)]
        internal static extern bool DeleteSingleKeyToTextRemap(IntPtr config, int originalKey);

        [DllImport(DllName, CallingConvention = Convention, CharSet = CharSet.Unicode)]
        [return: MarshalAs(UnmanagedType.Bool)]
        internal static extern bool DeleteShortcutRemap(IntPtr mappingConfiguration, [MarshalAs(UnmanagedType.LPWStr)] string originalKeys, [MarshalAs(UnmanagedType.LPWStr)] string targetApp);

        // Key List Functions
        [DllImport(DllName, CallingConvention = Convention)]
        internal static extern int GetKeyboardKeysList([MarshalAs(UnmanagedType.Bool)] bool isShortcut, [Out] KeyNamePair[] keyList, int maxCount);

        // Key Utility Functions
        [DllImport(DllName, CallingConvention = Convention, CharSet = CharSet.Unicode)]
        internal static extern int GetKeyCodeFromName([MarshalAs(UnmanagedType.LPWStr)] string keyName);

        [DllImport(DllName, CallingConvention = Convention, CharSet = CharSet.Unicode)]
        internal static extern void GetKeyDisplayName(int keyCode, [Out] StringBuilder keyName, int maxLength);

        [DllImport(DllName, CallingConvention = Convention)]
        internal static extern int GetKeyType(int keyCode);

        // Validation Functions
        [DllImport(DllName, CallingConvention = Convention, CharSet = CharSet.Unicode)]
        [return: MarshalAs(UnmanagedType.Bool)]
        internal static extern bool IsShortcutIllegal([MarshalAs(UnmanagedType.LPWStr)] string shortcutKeys);

        [DllImport(DllName, CallingConvention = Convention, CharSet = CharSet.Unicode)]
        [return: MarshalAs(UnmanagedType.Bool)]
        internal static extern bool AreShortcutsEqual([MarshalAs(UnmanagedType.LPWStr)] string lShort, [MarshalAs(UnmanagedType.LPWStr)] string rShortcut);

        // String Management Functions
        [DllImport(DllName, CallingConvention = Convention)]
        internal static extern void FreeString(IntPtr str);

        public static string GetStringAndFree(IntPtr handle)
        {
            if (handle == IntPtr.Zero)
            {
                return string.Empty;
            }

            string? result = Marshal.PtrToStringUni(handle);
            FreeString(handle);
            return result ?? string.Empty;
        }
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    public struct SingleKeyMapping
    {
        public int OriginalKey;
        public IntPtr TargetKey;
        [MarshalAs(UnmanagedType.Bool)]
        public bool IsShortcut;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    public struct KeyboardTextMapping
    {
        public int OriginalKey;
        public IntPtr TargetText;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    public struct ShortcutMapping
    {
        public IntPtr OriginalKeys;
        public IntPtr TargetKeys;
        public IntPtr TargetApp;
        public int OperationType;
        public IntPtr TargetText;
        public IntPtr ProgramPath;
        public IntPtr ProgramArgs;
        public IntPtr UriToOpen;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    public struct KeyNamePair
    {
        public int KeyCode;

        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
        public string KeyName;
    }
}
