#include "lpspi_master_driver.h"
#include "Cpu.h"
#include "clockMan1.h"
#include "lpspiCom1.h"
#include "lpspi_shared_function.h"
#include "stdio.h"
#include "string.h"
#include "ff.h"

#define LPSPI_MASTER_INSTANCE   0
#define SD_CS_PORT        PTB
#define SD_CS_PIN           5

lpspi_master_config_t spiConfig;
lpspi_state_t lpspiState;
int exit_code = 0;

void SPI_init() {
    // Initialize clock
    CLOCK_DRV_Init(&clockMan1_InitConfig0);

    // Initialize pins
    PINS_DRV_Init(NUM_OF_CONFIGURED_PINS, g_pin_mux_InitConfigArr);

    // Initialize LPSPI Master
    LPSPI_DRV_MasterGetDefaultConfig(&spiConfig);

    // Initialize the LPSPI master module
    LPSPI_DRV_MasterInit(LPSPI_MASTER_INSTANCE, &lpspiState, &spiConfig);

    /* Set CS pin as output */
    PINS_DRV_SetPinsDirection(SD_CS_PORT, 1 << SD_CS_PIN);

    /* Set CS pin high (inactive) */
    PINS_DRV_SetPins(SD_CS_PORT, 1 << SD_CS_PIN);
}

void CS_ENABLE() {
    /* Set CS pin low (active) */
    PINS_DRV_ClearPins(SD_CS_PORT, 1 << SD_CS_PIN);
}

void CS_DISABLE() {
    /* Set CS pin high (inactive) */
    PINS_DRV_SetPins(SD_CS_PORT, 1 << SD_CS_PIN);
}

uint8_t SPI_transfer(uint8_t data) {

    uint8_t receiveData;

    /* Enable CS before starting transfer */
    CS_ENABLE();

    /* Perform SPI transfer */
    LPSPI_DRV_MasterTransferBlocking(LPSPI_MASTER_INSTANCE, &data, &receiveData, 1, 100);

    /* Disable CS after transfer */
    CS_DISABLE();

    return receiveData;
}

void SD_powerUpSeq() {
    // Make sure card is deselected
    CS_DISABLE();

    // Give SD card time to power up
    OSIF_TimeDelay(1);

    // Send 80 clock cycles to synchronize
    for (uint8_t i = 0; i < 10; i++)
        SPI_transfer(0xFF);

    // Deselect SD card
    CS_DISABLE();
    //SPI_transfer(0xFF);
}

void SD_command(uint8_t cmd, uint32_t arg, uint8_t crc) {
    // Transmit command to SD card
    SPI_transfer(cmd | 0x40);

    // Transmit argument
    SPI_transfer((uint8_t)(arg >> 24));
    SPI_transfer((uint8_t)(arg >> 16));
    SPI_transfer((uint8_t)(arg >> 8));
    SPI_transfer((uint8_t)(arg));

    // Transmit crc
    SPI_transfer(crc | 0x01);
}

uint8_t SD_readRes1() {
    uint8_t i = 0, res1;

    // Keep polling until actual data received
    while ((res1 = SPI_transfer(0xFF)) == 0xFF) {
        i++;

        // If no data received for 8 bytes, break
        if (i > 8) break;
    }

    return res1;
}

uint8_t SD_goIdleState() {

    // Assert chip select
    SPI_transfer(0xFF);
    CS_ENABLE();
    SPI_transfer(0xFF);

    // Send CMD0
    SD_command(0x40, 0x00000000, 0x95);

    // Read response
    uint8_t res1 = SD_readRes1();

    // Deassert chip select
    SPI_transfer(0xFF);
    CS_DISABLE();
    SPI_transfer(0xFF);

    return res1;
}

uint8_t SD_sendCmd8() {

    // Assert chip select
    SPI_transfer(0xFF);
    CS_ENABLE();
    SPI_transfer(0xFF);

    // Send CMD8 with voltage range and check pattern
    SD_command(0x48, 0x000001AA, 0x87);

    // Read response
    uint8_t res1 = SD_readRes1();

    // Deassert chip select
    SPI_transfer(0xFF);
    CS_DISABLE();
    SPI_transfer(0xFF);

    return res1;
}

uint8_t SD_sendCmd58() {

    // Assert chip select
    SPI_transfer(0xFF);
    CS_ENABLE();
    SPI_transfer(0xFF);

    // Send CMD58
    SD_command(0x58, 0x00000000, 0x00);

    // Read response
    uint8_t res1 = SD_readRes1();

    // Deassert chip select
    SPI_transfer(0xFF);
    CS_DISABLE();
    SPI_transfer(0xFF);

    return res1;
}

uint8_t SD_sendAppCmd() {

    // Assert chip select
    SPI_transfer(0xFF);
    CS_ENABLE();
    SPI_transfer(0xFF);

    // Send CMD55 (Application Command)
    SD_command(0x55, 0x00000000, 0x00);

    // Read response
    uint8_t res1 = SD_readRes1();

    // Deassert chip select
    SPI_transfer(0xFF);
    CS_DISABLE();
    SPI_transfer(0xFF);

    return res1;
}

uint8_t SD_sendOpCond() {

    // Assert chip select
    SPI_transfer(0xFF);
    CS_ENABLE();
    SPI_transfer(0xFF);

    // Send ACMD41 (Send Operating Condition)
    SD_command(0x41, 0x40000000, 0x00);

    // Read response
    uint8_t res1 = SD_readRes1();

    // Deassert chip select
    SPI_transfer(0xFF);
    CS_DISABLE();
    SPI_transfer(0xFF);

    return res1;
}

FRESULT createFile() {
    FRESULT result;
    FIL file;

    // Open or create a file named "data.txt"
    result = f_open(&file, "data.txt", FA_CREATE_ALWAYS | FA_WRITE);

    if (result == FR_OK) {
        // File created successfully
        f_close(&file);
        return FR_OK;
    } else {
        // Error creating the file
        return result;
    }
}

FRESULT writeToFile(const char* data) {
    FRESULT result;
    FIL file;
    UINT bytesWritten;

    // Open the file for writing
    result = f_open(&file, "data.txt", FA_WRITE);

    if (result == FR_OK) {
        // Write data to the file
        result = f_write(&file, data, strlen(data), &bytesWritten);
        if (result == FR_OK) {
            // Data written successfully
            f_close(&file);
            return FR_OK;
        }
    }

    // Error writing data
    return result;
}


 int main() {

     /*** Processor Expert internal initialization. DON'T REMOVE THIS CODE!!! ***/
    #ifdef PEX_RTOS_INIT
        PEX_RTOS_INIT(); /* Initialization of the selected RTOS. Macro is defined by the RTOS component. */
    #endif
    /*** End of Processor Expert internal initialization. ***/

    // Initialize SPI
    SPI_init();

    // Perform power-up sequence
    SD_powerUpSeq();

        // Go to idle state
        uint8_t idleResponse = SD_goIdleState();
        printf("Idle Response: %d\n", idleResponse);

        // Send CMD8
        uint8_t cmd8Response = SD_sendCmd8();
        printf("CMD8 Response: %d\n", cmd8Response);

        // Send CMD58
        uint8_t cmd58Response = SD_sendCmd58();
        printf("CMD58 Response: %d\n", cmd58Response);

        // Send CMD55 (Application Command)
        uint8_t cmd55Response = SD_sendAppCmd();
        printf("CMD55 Response: %d\n", cmd55Response);

        // Send ACMD41 (Send Operating Condition)
        uint8_t acmd41Response = SD_sendOpCond();
        printf("ACMD41 Response: %d\n", acmd41Response);

        // Create the file
        FRESULT createResult = createFile();
        if (createResult == FR_OK) {
            printf("File created successfully.\n");

            // Write data to the file
            const char* dataToWrite = "Hello, SD card!";
            FRESULT writeResult = writeToFile(dataToWrite);
            if (writeResult == FR_OK) {
                printf("Data written to the file.\n");
            } else {
                printf("Error writing data to the file: %d\n", writeResult);
            }
        } else {
            printf("Error creating file: %d\n", createResult);
        }

  /*** Don't write any code pass this line, or it will be deleted during code generation. ***/
  /*** RTOS startup code. Macro PEX_RTOS_START is defined by the RTOS component. DON'T MODIFY THIS CODE!!! ***/
  #ifdef PEX_RTOS_START
    PEX_RTOS_START();                  /* Startup of the selected RTOS. Macro is defined by the RTOS component. */
  #endif
  /*** End of RTOS startup code.  ***/
  /*** Processor Expert end of main routine. DON'T MODIFY THIS CODE!!! ***/
  for(;;) {
    if(exit_code != 0) {
      break;
    }
  }
  return exit_code;
  /*** Processor Expert end of main routine. DON'T WRITE CODE BELOW!!! ***/
}
