using System;
using System.Runtime.InteropServices;

namespace OkonaPad
{
    /// <summary>
    /// Thin bridge to the JavaScript side. In a WebGL build these resolve to the
    /// functions in <c>Plugins/OkonaInterop.jslib</c>:
    ///   OkonaReadState      — copies the shell's snapshot (window.__okonaPad) straight
    ///                         into this buffer's WebAssembly memory (zero copies/strings).
    ///   OkonaRumble         — forwards a rumble request to window.okonaOnRumble.
    ///   OkonaGetPairVersion — phone-pairing change counter (0 = none available).
    ///   OkonaGetPairInfo    — "url\ncaption" for the current pairing.
    ///   OkonaGetPairQr      — PNG bytes of the shell's pre-rendered pairing QR.
    ///   OkonaSetJoinUi      — show/hide the shell's own join screen.
    ///   OkonaSetPlayerIdentity — push a player's name/icon/accent to their phone pad.
    /// In the Editor (and any non-WebGL player) there's no JS layer, so reads come
    /// from <see cref="EditorInject"/> (used by tests/simulators), pairing serves a
    /// clearly-marked stub so join screens can be laid out in Play Mode, and rumble
    /// and the join-UI switch are no-ops.
    /// </summary>
    internal static class OkonaInterop
    {
#if UNITY_WEBGL && !UNITY_EDITOR
        [DllImport("__Internal")] private static extern int OkonaReadState(byte[] buffer, int maxLen);
        [DllImport("__Internal")] private static extern void OkonaRumble(int slot, float low, float high);
        [DllImport("__Internal")] private static extern int OkonaGetPairVersion();
        [DllImport("__Internal")] private static extern int OkonaGetPairInfo(byte[] buffer, int maxLen);
        [DllImport("__Internal")] private static extern int OkonaGetPairQr(byte[] buffer, int maxLen);
        [DllImport("__Internal")] private static extern void OkonaSetJoinUi(int visible);
        [DllImport("__Internal")] private static extern void OkonaSetPlayerIdentity(int slot, string name, string icon, string color);

        // readonly, not const: a const would let the compiler fold Pairing's
        // guard into `if (false)` and warn about unreachable code.
        public static readonly bool HasJsLayer = true;

        public static int ReadState(byte[] buffer) => OkonaReadState(buffer, buffer.Length);
        public static void Rumble(int slot, float low, float high) => OkonaRumble(slot, low, high);
        public static int GetPairVersion() => OkonaGetPairVersion();
        public static int GetPairInfo(byte[] buffer) => OkonaGetPairInfo(buffer, buffer.Length);
        public static int GetPairQr(byte[] buffer) => OkonaGetPairQr(buffer, buffer.Length);
        public static void SetJoinUi(bool visible) => OkonaSetJoinUi(visible ? 1 : 0);
        public static void SetPlayerIdentity(int slot, string name, string icon, string color)
            => OkonaSetPlayerIdentity(slot, name ?? "", icon ?? "", color ?? "");
#else
        /// <summary>Editor/test injection point — set this to a snapshot buffer to simulate the JS layer.</summary>
        public static byte[] EditorInject;

        public static readonly bool HasJsLayer = false;

        // Obviously-fake pairing so an in-game join screen can be built and
        // previewed without a WebGL build. Never scannable — see Pairing.GetQrTexture.
        const string EditorPairInfo =
            "https://okonaonline.com/pad/?r=EDITOR&t=stub\nEditor stub — not scannable";

        public static int ReadState(byte[] buffer)
        {
            if (EditorInject == null) return 0;
            int n = Math.Min(buffer.Length, EditorInject.Length);
            Array.Copy(EditorInject, buffer, n);
            return n;
        }

        public static void Rumble(int slot, float low, float high) { /* no JS layer outside WebGL */ }

        public static int GetPairVersion() => 1;

        public static int GetPairInfo(byte[] buffer)
        {
            var bytes = System.Text.Encoding.UTF8.GetBytes(EditorPairInfo);
            if (bytes.Length + 1 > buffer.Length) return -(bytes.Length + 1);
            Array.Copy(bytes, buffer, bytes.Length);
            return bytes.Length;
        }

        /// <summary>No shell to render one outside WebGL — Pairing draws a placeholder instead.</summary>
        public static int GetPairQr(byte[] buffer) => 0;

        public static void SetJoinUi(bool visible) { /* no JS layer outside WebGL */ }

        public static void SetPlayerIdentity(int slot, string name, string icon, string color)
        { /* no JS layer outside WebGL */ }
#endif
    }
}
