/* This file demonstrates how to access a Reader via C#.
 *
 * As currently Baltech does not provide language bindings for C#
 * the bindings for the required functions have to be declared manually.
 *
 * IMPORTANT - Type Mappings:
 * brp_time is defined as 'unsigned long' in brp_lib, which has different sizes
 * depending on the platform's data model:
 *
 * - Windows (LLP64): unsigned long = 32 bits (even on x64)
 *   -> Works correctly on both 32-bit and 64-bit Windows
 *
 * - Linux/macOS (LP64): unsigned long = 64 bits on x64, 32 bits on x86
 *   -> Requires UIntPtr for portability
 *
 * This sample uses UIntPtr which works correctly on all platforms and
 * all C# versions. In C# 9.0+, you can use 'nuint' instead, which is
 * an alias for UIntPtr.
 */ 

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

using brp_protocol = System.IntPtr;
using brp_errcode = System.UInt32;
using brp_time = System.UIntPtr;  // Native-sized uint, use 'nuint' in C# 9.0+
using size_t = System.UIntPtr;

namespace csharp_sample
{
    class Program
    {
        const int BRP_OK = 0x00000000;

        [DllImport("brp_lib.dll", CallingConvention=CallingConvention.Cdecl)]
        static extern brp_protocol brp_create();

        [DllImport("brp_lib.dll", CallingConvention = CallingConvention.Cdecl)]
        static extern brp_protocol brp_create_usb_hid(long snr);

        [DllImport("brp_lib.dll", CallingConvention = CallingConvention.Cdecl)]
        static extern brp_errcode brp_set_io(brp_protocol protocol, brp_protocol io_protocol);

        [DllImport("brp_lib.dll", CallingConvention = CallingConvention.Cdecl)]
        static extern brp_errcode brp_open(brp_protocol protocol);

        [DllImport("brp_lib.dll", CallingConvention = CallingConvention.Cdecl)]
        static extern brp_errcode brp_close(brp_protocol protocol);

        [DllImport("brp_lib.dll", CallingConvention = CallingConvention.Cdecl)]
        static extern brp_errcode brp_exec_cmd(
            brp_protocol protocol, int cmd_code, byte[] param_buf, size_t param_len,
            byte[] resp_buf, size_t max_resp_len, ref size_t resp_len, brp_time cmd_timeout);

        static void Main()
        {
            brp_protocol brpDev = brp_create();
            if (brp_set_io(brpDev, brp_create_usb_hid(0)) != BRP_OK)
                throw new Exception("Failed to create communication object");

            if (brp_open(brpDev) != BRP_OK)
                throw new Exception("Failed to open connection");

            size_t respLen = (size_t)0;
            byte[] respBuf = new byte[100];
            if (brp_exec_cmd(brpDev, 0x0004, new byte[1], (size_t)0,
                             respBuf, (size_t)respBuf.Length, ref respLen, (brp_time)100)
               != BRP_OK)
                throw new Exception("Failed to execute command");
            if (respLen.ToUInt32() != 41)
                throw new Exception("Expected Sys.GetInfo to return 41 Bytes");

            if (brp_close(brpDev) != BRP_OK)
                throw new Exception("Failed to close connection");

            Console.WriteLine("Firmwarestring: " + Encoding.ASCII.GetString(respBuf));
        }
    }
}
