﻿using System;
using System.Runtime.InteropServices;

namespace Aidlab
{
    public static class AidlabAPI
    {
        private const string LibraryName =
#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN
            "libaidlab_sdk.dll";
#elif UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX
            "libaidlab_sdk.dylib";
#elif UNITY_IOS
            "__Internal";
#else
            "aidlab_sdk";
#endif

        public enum AidlabErrorCode : int
        {
            None = 0,
            Transport = 1000,
            Protocol = 2000,
            Sdk = 9000,
        }

        // EventType from shared.h.
        internal enum EventType : uint
        {
            WearState = 0,
            PressureWearState = 1,
            User = 2,
            Exercise = 3,
            Activity = 4,
            Ecg = 5,
            Sleep = 6,
        }

        // AidlabEvent from shared.h. The core may append fields, so read it only through the pointer it passes.
        [StructLayout(LayoutKind.Sequential)]
        internal struct AidlabEvent
        {
            public uint type;
            public int code;
            public ulong timestamp;
            public ulong endTimestamp;
            public float confidence;
            public uint modelVersion;
            public IntPtr aux;
            public sbyte subtyp;
        }

        // Data callbacks receive `past` (live vs synchronized data) as a 1-byte C bool. Without MarshalAs(I1) the
        // default bool marshaling reads a 4-byte Win32 BOOL.
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate void callbackSampleTime(IntPtr context, [MarshalAs(UnmanagedType.I1)] bool past, ulong timestamp, float value);

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate void callbackRespirationRate(IntPtr context, [MarshalAs(UnmanagedType.I1)] bool past, ulong timestamp, uint value);

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate void callbackSteps(IntPtr context, [MarshalAs(UnmanagedType.I1)] bool past, ulong timestamp, ulong steps);

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate void callbackBatteryLevel(IntPtr context, byte stateOfCharge);

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate void callbackAccelerometer(IntPtr context, [MarshalAs(UnmanagedType.I1)] bool past, ulong timestamp, float ax, float ay, float az);

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate void callbackGyroscope(IntPtr context, [MarshalAs(UnmanagedType.I1)] bool past, ulong timestamp, float gx, float gy, float gz);

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate void callbackMagnetometer(IntPtr context, [MarshalAs(UnmanagedType.I1)] bool past, ulong timestamp, float mx, float my, float mz);

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate void callbackOrientation(IntPtr context, [MarshalAs(UnmanagedType.I1)] bool past, ulong timestamp, float roll, float pitch, float yaw);

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate void callbackBodyPosition(IntPtr context, [MarshalAs(UnmanagedType.I1)] bool past, ulong timestamp, BodyPosition bodyPosition);

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate void callbackQuaternion(IntPtr context, [MarshalAs(UnmanagedType.I1)] bool past, ulong timestamp, float qw, float qx, float qy, float qz);

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate void callbackHeartRate(IntPtr context, [MarshalAs(UnmanagedType.I1)] bool past, ulong timestamp, int heartRate);

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate void callbackRr(IntPtr context, [MarshalAs(UnmanagedType.I1)] bool past, ulong timestamp, int rr);

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate void callbackSoundVolume(IntPtr context, [MarshalAs(UnmanagedType.I1)] bool past, ulong timestamp, ushort soundVolume);

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate void callbackPressure(IntPtr context, [MarshalAs(UnmanagedType.I1)] bool past, ulong timestamp, int pressure);

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate void callbackSignalQuality(IntPtr context, [MarshalAs(UnmanagedType.I1)] bool past, ulong timestamp, byte value);

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate void callbackEvent(IntPtr context, [MarshalAs(UnmanagedType.I1)] bool past, IntPtr eventPointer);

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate void callbackSyncState(IntPtr context, SyncState syncState);

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate void callbackUnsynchronizedSize(IntPtr context, uint size, float bytesPerSecond);

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate void callbackBleSend(IntPtr context, IntPtr data, int size);

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate void callbackBleReady(IntPtr context);

        // 0 for ACK, otherwise the NAK code of the V4 frame in flight; called just before callbackBleReady.
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate void callbackBleFrameResult(IntPtr context, byte result);

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate void callbackPayload(IntPtr context, ushort pid, IntPtr payload, UIntPtr payloadLength, ulong options);

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate void callbackProcessError(IntPtr context, ushort pid, IntPtr payload, UIntPtr payloadLength, ulong options);

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate void callbackError(IntPtr context, AidlabErrorCode code, IntPtr message);

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate void callbackEda(IntPtr context, [MarshalAs(UnmanagedType.I1)] bool past, ulong timestamp, float conductance);

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate void callbackGps(
            IntPtr context,
            [MarshalAs(UnmanagedType.I1)] bool past,
            ulong timestamp,
            float lat,
            float lon,
            float alt,
            float speed,
            float heading,
            float hdop);

        [DllImport(LibraryName, EntryPoint = "AidlabSDK_create", CallingConvention = CallingConvention.Cdecl)]
        public static extern IntPtr AidlabSDK_create(byte[] firmwareRevision, int size);

        [DllImport(LibraryName, EntryPoint = "AidlabSDK_destroy", CallingConvention = CallingConvention.Cdecl)]
        public static extern void AidlabSDK_destroy(IntPtr aidlabSdk);

        [DllImport(LibraryName, EntryPoint = "AidlabSDK_set_context", CallingConvention = CallingConvention.Cdecl)]
        public static extern void AidlabSDK_set_context(IntPtr context, IntPtr aidlabSdk);

        [DllImport(LibraryName, EntryPoint = "AidlabSDK_init_callbacks", CallingConvention = CallingConvention.Cdecl)]
        public static extern void AidlabSDK_init_callbacks(
            callbackSampleTime ecg,
            callbackSampleTime respiration,
            callbackSampleTime temperature,
            callbackAccelerometer accelerometer,
            callbackGyroscope gyroscope,
            callbackMagnetometer magnetometer,
            callbackBatteryLevel battery,
            callbackSteps steps,
            callbackOrientation orientation,
            callbackQuaternion quaternion,
            callbackRespirationRate respirationRate,
            callbackHeartRate heartRate,
            callbackRr rr,
            callbackSoundVolume soundVolume,
            callbackPressure pressure,
            callbackBodyPosition bodyPosition,
            callbackSignalQuality signalQuality,
            callbackEda eda,
            callbackGps gps,
            callbackEvent eventCallback,
            IntPtr aidlabSdk);

        [DllImport(LibraryName, EntryPoint = "AidlabSDK_unsupported_data_types", CallingConvention = CallingConvention.Cdecl)]
        public static extern ulong AidlabSDK_unsupported_data_types(ulong dataTypes, IntPtr aidlabSdk);

        [DllImport(LibraryName, EntryPoint = "AidlabSDK_unsupported_events", CallingConvention = CallingConvention.Cdecl)]
        public static extern ulong AidlabSDK_unsupported_events(ulong events, IntPtr aidlabSdk);

        [DllImport(LibraryName, EntryPoint = "AidlabSDK_collect_flags", CallingConvention = CallingConvention.Cdecl)]
        public static extern void AidlabSDK_collect_flags(ref ulong live, ref ulong store, ref ulong events, IntPtr aidlabSdk);

        [DllImport(LibraryName, EntryPoint = "AidlabSDK_set_events", CallingConvention = CallingConvention.Cdecl)]
        public static extern ulong AidlabSDK_set_events(ulong events, IntPtr aidlabSdk);

        [DllImport(LibraryName, EntryPoint = "AidlabSDK_init_synchronization_callbacks", CallingConvention = CallingConvention.Cdecl)]
        public static extern void AidlabSDK_init_synchronization_callbacks(
            callbackSyncState syncState,
            callbackUnsynchronizedSize unsynchronizedSize,
            IntPtr aidlabSdk);

        [DllImport(LibraryName, EntryPoint = "AidlabSDK_set_payload_callback", CallingConvention = CallingConvention.Cdecl)]
        public static extern void AidlabSDK_set_payload_callback(callbackPayload callback, IntPtr aidlabSdk);

        [DllImport(LibraryName, EntryPoint = "AidlabSDK_set_process_error_callback", CallingConvention = CallingConvention.Cdecl)]
        public static extern void AidlabSDK_set_process_error_callback(callbackProcessError callback, IntPtr aidlabSdk);

        [DllImport(LibraryName, EntryPoint = "AidlabSDK_set_error_callback", CallingConvention = CallingConvention.Cdecl)]
        public static extern void AidlabSDK_set_error_callback(callbackError callback, IntPtr context, IntPtr aidlabSdk);

        [DllImport(LibraryName, EntryPoint = "AidlabSDK_set_ble_send_callback", CallingConvention = CallingConvention.Cdecl)]
        public static extern void AidlabSDK_set_ble_send_callback(callbackBleSend callback, IntPtr aidlabSdk);

        [DllImport(LibraryName, EntryPoint = "AidlabSDK_set_ble_ready_callback", CallingConvention = CallingConvention.Cdecl)]
        public static extern void AidlabSDK_set_ble_ready_callback(callbackBleReady callback, IntPtr aidlabSdk);

        [DllImport(LibraryName, EntryPoint = "AidlabSDK_set_ble_frame_result_callback", CallingConvention = CallingConvention.Cdecl)]
        public static extern void AidlabSDK_set_ble_frame_result_callback(callbackBleFrameResult callback, IntPtr aidlabSdk);

        [DllImport(LibraryName, EntryPoint = "AidlabSDK_process_ble_chunk", CallingConvention = CallingConvention.Cdecl)]
        public static extern void AidlabSDK_process_ble_chunk(byte[] data, int size, IntPtr aidlabSdk);

        [DllImport(LibraryName, EntryPoint = "AidlabSDK_send", CallingConvention = CallingConvention.Cdecl)]
        public static extern void AidlabSDK_send(byte[] payload, int size, int processId, IntPtr aidlabSdk);

        [DllImport(LibraryName, EntryPoint = "AidlabSDK_send_process_command", CallingConvention = CallingConvention.Cdecl)]
        internal static extern void AidlabSDK_send_process_command(
            byte[] payload,
            int size,
            int processId,
            IntPtr aidlabSdk
        );
    }
}
