using CosmicDriver;

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

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

Console.WriteLine("Initializing I3C bus...");
AssertStatus(sdk.I3cInitBus(), "I3cInitBus");

Console.WriteLine("SETDASA flow...");
AssertStatus(sdk.I3cResetBus(), "I3cResetBus before SETDASA");
AssertStatus(sdk.I3cCccSetdasa(0x69, 0x0B), "I3cCccSetdasa(0x69 -> 0x0B)");
AssertStatus(sdk.I3cCccSetdasa(0x6B, 0x0C), "I3cCccSetdasa(0x6B -> 0x0C)");
PrintTargetTable(sdk, "after SETDASA");

Console.WriteLine("SETAASA flow...");
AssertStatus(sdk.I3cResetBus(), "I3cResetBus before SETAASA");
AssertStatus(sdk.I3cCccSetaasa([0x69, 0x6B]), "I3cCccSetaasa");
PrintTargetTable(sdk, "after SETAASA");

Console.WriteLine("ENTDAA flow...");
AssertStatus(sdk.I3cResetBus(), "I3cResetBus before ENTDAA");
AssertStatus(sdk.I3cCccEntdaa(), "I3cCccEntdaa");
PrintTargetTable(sdk, "after ENTDAA");

Console.WriteLine("SETNEWDA flow...");
AssertStatus(sdk.I3cResetBus(), "I3cResetBus before SETNEWDA");
AssertStatus(sdk.I3cInitBus(), "I3cInitBus before SETNEWDA");
AssertStatus(sdk.I3cCccSetnewda(0x08, 0x0B), "I3cCccSetnewda(0x08 -> 0x0B)");
AssertStatus(sdk.I3cCccSetnewda(0x09, 0x0C), "I3cCccSetnewda(0x09 -> 0x0C)");
PrintTargetTable(sdk, "after SETNEWDA");

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 PrintTargetTable(CosmicSdk sdk, string stage)
{
    var (tableStatus, targets) = sdk.I3cGetDeviceTable();
    AssertStatus(tableStatus, $"I3cGetDeviceTable({stage})");
    Console.WriteLine($"Target table {stage}: {targets!.Count} target(s)");
    foreach (var target in targets)
    {
        Console.WriteLine($"  static=0x{target.StaticAddress:X2} dynamic=0x{target.DynamicAddress:X2} pid={string.Join(" ", target.Pid.Select(b => $"0x{b:X2}"))} bcr=0x{target.Bcr.Byte:X2} dcr=0x{target.Dcr:X2} mwl={target.Mwl} mrl={target.Mrl} maxIbi={target.MaxIbiPayloadLength}");
    }
}

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}"))}");
    }
}
