package com.binho.cosmicsdk.examples.spi;

import com.binho.cosmicsdk.CosmicSdk;
import com.binho.cosmicsdk.ResultCodes;
import com.binho.cosmicsdk.Types.SpiBitOrder;
import com.binho.cosmicsdk.Types.SpiChipSelect;
import com.binho.cosmicsdk.Types.SpiChipSelectPolarity;
import com.binho.cosmicsdk.Types.SpiDataWidth;
import com.binho.cosmicsdk.Types.SpiMode;

import java.util.Random;

import static com.binho.cosmicsdk.examples.ExampleHelpers.assertStatus;
import static com.binho.cosmicsdk.examples.ExampleHelpers.printDeviceInfo;

public final class SpiExample {
    private SpiExample() {
    }

    public static void main(String[] args) {
        try (CosmicSdk sdk = new CosmicSdk(2000)) {
            if (!sdk.connect()) {
                throw new IllegalStateException("Failed to connect to a Binho device.");
            }

            var infoResult = sdk.getDeviceInfo();
            assertStatus(infoResult.status(), "getDeviceInfo");
            printDeviceInfo(infoResult.value());

            assertStatus(sdk.spiSetBusVoltage(3300, false), "spiSetBusVoltage");
            assertStatus(
                    sdk.spiControllerInit(
                            SpiBitOrder.SPI_BIT_ORDER_MSB,
                            SpiMode.SPI_MODE_0,
                            SpiDataWidth.SPI_DATA_WIDTH_8_BITS,
                            SpiChipSelect.SPI_CHIP_SELECT_0,
                            SpiChipSelectPolarity.SPI_CS_ACTIVE_LOW,
                            10_000_000
                    ),
                    "spiControllerInit",
                    ResultCodes.SUCCESS,
                    ResultCodes.FW_INTERFACE_ALREADY_INITIALIZED
            );

            var jedec = sdk.spiTransfer(new byte[]{(byte) 0x9F}, 4);
            assertStatus(jedec.status(), "read JEDEC ID");
            if (jedec.value().length < 4
                    || Byte.toUnsignedInt(jedec.value()[1]) != 0xEF
                    || Byte.toUnsignedInt(jedec.value()[2]) != 0x40
                    || Byte.toUnsignedInt(jedec.value()[3]) != 0x18) {
                throw new IllegalStateException("Unexpected JEDEC ID");
            }

            assertStatus(sdk.spiTransfer(new byte[]{0x06}, 1).status(), "write enable");
            assertStatus(sdk.spiTransfer(new byte[]{0x20, 0x00, 0x20, 0x00}, 4).status(), "sector erase");

            while (true) {
                var status = sdk.spiTransfer(new byte[]{0x05}, 2);
                assertStatus(status.status(), "read status register");
                if ((status.value()[1] & 0x01) == 0) {
                    break;
                }
            }

            assertStatus(sdk.spiTransfer(new byte[]{0x06}, 1).status(), "write enable (again)");

            Random random = new Random();
            int payloadLength = random.nextInt(256) + 1;
            byte[] payload = new byte[payloadLength];
            random.nextBytes(payload);

            byte[] programCommand = new byte[4 + payloadLength];
            programCommand[0] = 0x02;
            programCommand[1] = 0x00;
            programCommand[2] = 0x20;
            programCommand[3] = 0x00;
            System.arraycopy(payload, 0, programCommand, 4, payloadLength);
            assertStatus(sdk.spiTransfer(programCommand, programCommand.length).status(), "page program");

            while (true) {
                var status = sdk.spiTransfer(new byte[]{0x05}, 2);
                assertStatus(status.status(), "read status register (after program)");
                if ((status.value()[1] & 0x01) == 0) {
                    break;
                }
            }

            var readback = sdk.spiTransfer(new byte[]{0x03, 0x00, 0x20, 0x00}, 4 + payloadLength);
            assertStatus(readback.status(), "read data");
            for (int i = 0; i < payloadLength; i++) {
                if (readback.value()[4 + i] != payload[i]) {
                    throw new IllegalStateException("Readback verification failed.");
                }
            }

            System.out.println("SPI flash write/read verification successful (" + payloadLength + " bytes)");
        }
    }
}
