using CosmicDriver;

using var sdk = new CosmicSdk(2000);
if (!sdk.Connect()) throw new Exception("Failed to connect to a Binho device.");

var (status, info) = sdk.GetDeviceInfo();
AssertStatus(status, "GetDeviceInfo");
PrintDeviceInfo(info!);

Console.WriteLine("Setting I2C VTARG to 3.3V...");
AssertStatus(sdk.I2cSetBusVoltage(3300), "I2cSetBusVoltage");
Console.WriteLine("Initializing I2C controller (bus A, 200kHz, 330 Ohm pull-up)...");
AssertStatus(sdk.I2cControllerInit(I2cBus.I2C_BUS_A, 200000, I2cPullUp.I2C_PULLUP_330Ohm), "I2cControllerInit", ResultCode.SUCCESS, ResultCode.FW_INTERFACE_ALREADY_INITIALIZED);
Console.WriteLine("Updating I2C parameters...");
AssertStatus(sdk.I2cSetParameters(I2cBus.I2C_BUS_A, 200000, I2cPullUp.I2C_PULLUP_330Ohm), "I2cSetParameters");
Console.WriteLine("Configuring I2C pull-up resistors...");
AssertStatus(sdk.I2cSetPullUpResistors(I2cBus.I2C_BUS_A, I2cPullUp.I2C_PULLUP_1_5kOhm), "I2cSetPullUpResistors");

byte targetAddress = 0x50;
byte[] subAddress = [0x01, 0x00];
byte[] payload = Enumerable.Repeat((byte)0xAA, 512).ToArray();

Console.WriteLine("Running non-stop write/read verification...");
AssertStatus(sdk.I2cWrite(I2cBus.I2C_BUS_A, targetAddress, subAddress, payload, true), "I2cWrite(non-stop)");
var (readStatusNs, readPayloadNs) = sdk.I2cRead(I2cBus.I2C_BUS_A, targetAddress, subAddress, 512, true);
AssertStatus(readStatusNs, "I2cRead(non-stop)");
if (readPayloadNs is null || !payload.SequenceEqual(readPayloadNs)) throw new Exception("Non-stop write/read verification failed.");
Console.WriteLine($"Non-stop payload verified ({readPayloadNs.Length} bytes)");
PrintPayloadPreview("Non-stop payload preview", readPayloadNs);

payload = Enumerable.Repeat((byte)0xFF, 512).ToArray();
Console.WriteLine("Running standard write/read verification...");
AssertStatus(sdk.I2cWrite(I2cBus.I2C_BUS_A, targetAddress, subAddress, payload, false), "I2cWrite");
var (readStatus, readPayload) = sdk.I2cRead(I2cBus.I2C_BUS_A, targetAddress, subAddress, 512, false);
AssertStatus(readStatus, "I2cRead");
if (readPayload is null || !payload.SequenceEqual(readPayload)) throw new Exception("Write/read verification failed.");
Console.WriteLine($"Standard payload verified ({readPayload.Length} bytes)");
PrintPayloadPreview("Standard payload preview", readPayload);

Console.WriteLine("Scanning I2C bus...");
var (scanStatus, scanResult) = sdk.I2cScanBus(I2cBus.I2C_BUS_A, true);
AssertStatus(scanStatus, "I2cScanBus", ResultCode.SUCCESS, ResultCode.FW_I2C_BUS_WITH_NO_TARGETS_CONNECTED);
if (scanResult is not null)
{
    Console.WriteLine("7-bit Address devices:");
    foreach (var address in scanResult.Addresses7Bit)
    {
        Console.WriteLine($"\t0x{address:X2}");
    }
    Console.WriteLine("10-bit Address devices:");
    foreach (var address in scanResult.Addresses10Bit)
    {
        Console.WriteLine($"\t0x{address:X4}");
    }
}

static void PrintDeviceInfo(DeviceInfo info)
{
    Console.WriteLine($"Manufacturer: {info.Manufacturer}");
    Console.WriteLine($"Product name: {info.ProductName}");
    Console.WriteLine($"Serial number: {info.SerialNumber}");
    Console.WriteLine($"FW version: {info.FwVersion}");
    Console.WriteLine($"HW version: {info.HwVersion}");
}

static void PrintPayloadPreview(string label, byte[] payload)
{
    var previewLength = Math.Min(payload.Length, 16);
    var preview = payload.Take(previewLength).Select(b => $"0x{b:X2}");
    Console.WriteLine($"{label}: {string.Join(" ", preview)}{(payload.Length > previewLength ? " ..." : string.Empty)}");
}

static void AssertStatus(ResultCode status, string operation, params ResultCode[] expected)
{
    if (expected.Length == 0) expected = [ResultCode.SUCCESS];
    if (!expected.Contains(status))
    {
        throw new Exception($"{operation} failed. STATUS=0x{(ushort)status:X4}. Expected: {string.Join(", ", expected.Select(e => $"0x{(ushort)e:X4}"))}");
    }
}
