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("Setting I3C VTARG to 3.3V...");
AssertStatus(sdk.I3cSetBusVoltage(3300), "I3cSetBusVoltage");

Console.WriteLine("Initializing I3C controller...");
AssertStatus(
    sdk.I3cControllerInit(
        I3cPushPullRate.PUSH_PULL_10_MHz_DC_40,
        I3cOpenDrainTransferRate.OPEN_DRAIN_400_KHz,
        I2cTransferRate.FAST_MODE,
        I3cDriveStrength.FAST),
    "I3cControllerInit",
    ResultCode.SUCCESS,
    ResultCode.FW_INTERFACE_ALREADY_INITIALIZED);

PrintTargetTable(sdk, "before init");

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

var (pidStatus, pid) = sdk.I3cCccGetpid(0x09);
AssertStatus(pidStatus, "I3cCccGetpid");
Console.WriteLine($"PID: {ToHex(pid!)}");

var (bcrStatus, bcr) = sdk.I3cCccGetbcr(0x09);
AssertStatus(bcrStatus, "I3cCccGetbcr");
Console.WriteLine($"BCR (0x09): {bcr}");

var (dcrStatus, dcr) = sdk.I3cCccGetdcr(0x09);
AssertStatus(dcrStatus, "I3cCccGetdcr");
Console.WriteLine($"DCR (0x09): {dcr}");

var (capsStatus, caps) = sdk.I3cCccGetcaps(0x08);
AssertStatus(capsStatus, "I3cCccGetcaps");
Console.WriteLine($"CAPS (0x08): {ToHex(caps!)}");

var (mrlStatus09, mrl09) = sdk.I3cCccGetmrl(0x09);
AssertStatus(mrlStatus09, "I3cCccGetmrl(0x09)");
Console.WriteLine($"MRL (0x09): {mrl09!.MaxReadLength}, IBI length: {mrl09.IbiLength}");

AssertStatus(sdk.I3cCccSetmrlBroadcast(0x0F03, 0x03), "I3cCccSetmrlBroadcast");
var (mrlStatus08, mrl08) = sdk.I3cCccGetmrl(0x08);
AssertStatus(mrlStatus08, "I3cCccGetmrl(0x08)");
Console.WriteLine($"MRL (0x08): {mrl08!.MaxReadLength}, IBI length: {mrl08.IbiLength}");

var (mrlStatus09AfterBc, mrl09AfterBc) = sdk.I3cCccGetmrl(0x09);
AssertStatus(mrlStatus09AfterBc, "I3cCccGetmrl(0x09) after broadcast");
Console.WriteLine($"MRL (0x09) after broadcast: {mrl09AfterBc!.MaxReadLength}, IBI length: {mrl09AfterBc.IbiLength}");

AssertStatus(sdk.I3cCccSetmrlDirect(0x09, 0x0200), "I3cCccSetmrlDirect");

var (mwlStatus09, mwl09) = sdk.I3cCccGetmwl(0x09);
AssertStatus(mwlStatus09, "I3cCccGetmwl(0x09)");
Console.WriteLine($"MWL (0x09): {mwl09}");

AssertStatus(sdk.I3cCccSetmwlBroadcast(0x0F03), "I3cCccSetmwlBroadcast");
var (mwlStatus08, mwl08) = sdk.I3cCccGetmwl(0x08);
AssertStatus(mwlStatus08, "I3cCccGetmwl(0x08)");
Console.WriteLine($"MWL (0x08): {mwl08}");

AssertStatus(sdk.I3cCccSetmwlDirect(0x08, 0x0200), "I3cCccSetmwlDirect");

AssertStatus(sdk.I3cCccDisecBroadcast(I3cEvent.IBI | I3cEvent.CR | I3cEvent.HJ), "I3cCccDisecBroadcast");
AssertStatus(sdk.I3cCccDisecDirect(0x09, I3cEvent.CR | I3cEvent.HJ), "I3cCccDisecDirect");
AssertStatus(sdk.I3cCccEnecBroadcast(I3cEvent.IBI | I3cEvent.CR | I3cEvent.HJ), "I3cCccEnecBroadcast");
AssertStatus(sdk.I3cCccEnecDirect(0x09, I3cEvent.CR | I3cEvent.HJ), "I3cCccEnecDirect");

var (rstactReadStatus, rstactReadValue) = sdk.I3cCccRstactDirect(0x08, I3cTargetResetDefByte.NO_RESET, I3cTransferDirection.READ);
AssertStatus(rstactReadStatus, "I3cCccRstactDirect(read)");
Console.WriteLine($"RSTACT read value: {rstactReadValue}");

AssertStatus(sdk.I3cCccRstactBroadcast(I3cTargetResetDefByte.NO_RESET), "I3cCccRstactBroadcast");
AssertStatus(sdk.I3cCccRstactDirect(0x09, I3cTargetResetDefByte.RESET_WHOLE_TARGET, I3cTransferDirection.WRITE).Status, "I3cCccRstactDirect(write)");

AssertStatus(sdk.I3cTargetResetPattern(), "I3cTargetResetPattern");
AssertStatus(sdk.I3cHdrExitPattern(), "I3cHdrExitPattern");

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

static string ToHex(byte[] data) => string.Join(" ", data.Select(b => $"0x{b:X2}"));

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