// AkiProtect SDK — C# P/Invoke bindings.
//
// Add this file to your C# project and reference the matching DLL:
//   x64 → AkiProtectSDK64.dll     (default)
//   x86 → AkiProtectSDK32.dll     (define AKIPROTECT_X86 in project)
//
// After packing with BinaryProtector, the SDK import is stripped —
// do not ship the DLL with the protected binary.

using System;
using System.Runtime.InteropServices;

namespace AkiProtect
{
#if AKIPROTECT_X86
    using CC = CallingConvention;
#endif

    public enum LicenseState : int
    {
        NotSet    = 0,
        Ok        = 1,
        Expired   = 2,
        HwidFail  = 3,
        BadSerial = 4,
        DateFail  = 5,
    }

    public static class SDK
    {
#if AKIPROTECT_X86
        private const string Dll = "AkiProtectSDK32";
        private const CallingConvention Conv = CallingConvention.StdCall;
#else
        private const string Dll = "AkiProtectSDK64";
        private const CallingConvention Conv = CallingConvention.Cdecl;
#endif

        // ---- Protection markers ----

        [DllImport(Dll, CallingConvention = Conv, CharSet = CharSet.Ansi)]
        public static extern void AkiProtectBegin(string markerName);

        [DllImport(Dll, CallingConvention = Conv, CharSet = CharSet.Ansi)]
        public static extern void AkiProtectBeginVirtualization(string markerName);

        [DllImport(Dll, CallingConvention = Conv, CharSet = CharSet.Ansi)]
        public static extern void AkiProtectBeginMutation(string markerName);

        [DllImport(Dll, CallingConvention = Conv, CharSet = CharSet.Ansi)]
        public static extern void AkiProtectBeginUltra(string markerName);

        [DllImport(Dll, CallingConvention = Conv, CharSet = CharSet.Ansi)]
        public static extern void AkiProtectBeginVirtualizationLockByKey(string markerName);

        [DllImport(Dll, CallingConvention = Conv, CharSet = CharSet.Ansi)]
        public static extern void AkiProtectBeginUltraLockByKey(string markerName);

        [DllImport(Dll, CallingConvention = Conv)]
        public static extern void AkiProtectEnd();

        // ---- Status queries ----

        [DllImport(Dll, CallingConvention = Conv)]
        public static extern int AkiProtectIsProtected();

        [DllImport(Dll, CallingConvention = Conv)]
        public static extern int AkiProtectIsDebuggerPresent(int checkKernel);

        [DllImport(Dll, CallingConvention = Conv)]
        public static extern int AkiProtectIsVirtualMachinePresent();

        [DllImport(Dll, CallingConvention = Conv)]
        public static extern int AkiProtectIsValidImageCRC();

        // ---- String decrypt ----

        [DllImport(Dll, CallingConvention = Conv, CharSet = CharSet.Ansi)]
        public static extern IntPtr AkiProtectDecryptStringA(string value);

        [DllImport(Dll, CallingConvention = Conv, CharSet = CharSet.Unicode)]
        public static extern IntPtr AkiProtectDecryptStringW(string value);

        [DllImport(Dll, CallingConvention = Conv)]
        public static extern int AkiProtectFreeString(IntPtr value);

        // ---- Virtual files ----

        [DllImport(Dll, CallingConvention = Conv, CharSet = CharSet.Ansi)]
        public static extern IntPtr AkiProtectFindVirtualFileA(string name, out uint outSize);

        // ---- Licensing ----

        [DllImport(Dll, CallingConvention = Conv, CharSet = CharSet.Ansi)]
        public static extern int AkiProtectSetSerialNumber(string serial);

        [DllImport(Dll, CallingConvention = Conv)]
        public static extern int AkiProtectGetSerialNumberState();

        [DllImport(Dll, CallingConvention = Conv)]
        public static extern int AkiProtectGetSerialNumberData(IntPtr data, int size);

        [DllImport(Dll, CallingConvention = Conv, CharSet = CharSet.Ansi)]
        public static extern int AkiProtectGetCurrentHWID(
            [MarshalAs(UnmanagedType.LPStr)] System.Text.StringBuilder hwid, int size);

        // ---- Convenience wrappers ----

        public static bool IsProtected()
        {
            return AkiProtectIsProtected() != 0;
        }

        public static bool IsDebuggerPresent(bool checkKernel = false)
        {
            return AkiProtectIsDebuggerPresent(checkKernel ? 1 : 0) != 0;
        }

        public static LicenseState SetSerial(string serial)
        {
            return (LicenseState)AkiProtectSetSerialNumber(serial);
        }

        public static LicenseState GetSerialState()
        {
            return (LicenseState)AkiProtectGetSerialNumberState();
        }

        public static string GetCurrentHWID()
        {
            var sb = new System.Text.StringBuilder(64);
            AkiProtectGetCurrentHWID(sb, sb.Capacity);
            return sb.ToString();
        }

        public static string DecryptString(string value)
        {
            IntPtr ptr = AkiProtectDecryptStringA(value);
            return Marshal.PtrToStringAnsi(ptr);
        }

        public static byte[] FindVirtualFile(string name)
        {
            uint size;
            IntPtr ptr = AkiProtectFindVirtualFileA(name, out size);
            if (ptr == IntPtr.Zero) return null;
            byte[] buf = new byte[size];
            Marshal.Copy(ptr, buf, 0, (int)size);
            return buf;
        }
    }

    // RAII-style scopes (use with `using` statement).
    //   using (new AkiProtect.ProtectScope("MyFunc")) { ... }

    public sealed class ProtectScope : IDisposable
    {
        public ProtectScope(string name) { SDK.AkiProtectBegin(name); }
        public void Dispose() { SDK.AkiProtectEnd(); }
    }

    public sealed class VirtualizeScope : IDisposable
    {
        public VirtualizeScope(string name) { SDK.AkiProtectBeginVirtualization(name); }
        public void Dispose() { SDK.AkiProtectEnd(); }
    }

    public sealed class MutateScope : IDisposable
    {
        public MutateScope(string name) { SDK.AkiProtectBeginMutation(name); }
        public void Dispose() { SDK.AkiProtectEnd(); }
    }

    public sealed class UltraScope : IDisposable
    {
        public UltraScope(string name) { SDK.AkiProtectBeginUltra(name); }
        public void Dispose() { SDK.AkiProtectEnd(); }
    }
}
