#include "main.h"
#include "escape.h"

// Define o buffer de recepção
byte rxData[SPI_DATA_SIZE];
byte txData[SPI_DATA_SIZE];

unsigned long startSendTime = 0;

void setup() {
  // Inicia a serial
  Serial.begin(115200);
  Serial.println("Iniciando SPI...");

  // Inicializa a SPI
  SPI.begin(SCK_PIN, MISO_PIN, MOSI_PIN, SS_PIN);

  // Configura o modo de operação da SPI
  SPI.beginTransaction(SPISettings(4000000, MSBFIRST, SPI_MODE0));

  // Adiciona os dados no buffer para envio
  addHexData(txData, SPI_DATA_SIZE);
}

void loop() {
  SPIsender(txData, SPI_DATA_SIZE, SPI_INTERVAL, SPI_TIMEOUT);
}

void SPIsender(byte* txData, int dataSize, int interval, int timeout) {
  static unsigned long startSenderTime = 0;

  // Verifica se é para enviar mais dados
  if (millis() - startSenderTime >= interval) {
    // Aguarda até que o intervalo de tempo tenha passado
    startSenderTime = millis();

    // Envia dados pela SPI
    bool success = SPItransfer(txData, dataSize, timeout);

    // Verifica se a transmissão foi bem-sucedida
    if (success) {
      Serial.printf(" %d bytes enviados com sucesso pela SPI.\r\n", dataSize);

      // Calcula o tempo de envio
      unsigned long sendTime = millis() - startSenderTime;

      // Imprime o tempo de envio
      Serial.printf(" Tempo de envio: %lu ms\r\n", sendTime);
    } else {
      Serial.println(" Erro na transmissão de dados pela SPI.");
    }
  }
}

bool SPItransfer(const byte* txData, int dataSize, int timeout) {
  // Seleciona o dispositivo SPI
  digitalWrite(SS_PIN, LOW);

  // Armazena o tempo de início da transferência SPI
  unsigned long startTime = millis();

  // Envia dados pela SPI
  for (int i = 0; i < dataSize; i++) {
    // Verifica se o tempo de espera definido em timeout foi atingido
    if (millis() - startTime > timeout) {
      // Se sim, interrompe a transmissão e retorna false
      digitalWrite(SS_PIN, HIGH);
      return false;
    }

    // Envia o byte pela SPI
    SPI.transfer(txData[i]);

    // Imprime o valor em hexadecimal com a cor apropriada
    Serial.printf("%s 0x%02X", ANSI_GREEN, txData[i]);
    Serial.printf("%s", ANSI_RESET);

    // Verifica se já foram impressos 8 bytes na linha atual
    if ((i + 1) % 8 == 0) {
      Serial.println(); // Quebra linha
    }
  }

  // Desativando o dispositivo SPI
  digitalWrite(SS_PIN, HIGH);

  return true;
}

// Recebe dados pela SPI com verificação de erros e timeout
bool SPIreceiver(byte* rxData, int dataSize, int timeout) {
  // Seleciona o dispositivo SPI
  digitalWrite(SS_PIN, LOW);

  // Armazena o tempo de início da transferência SPI
  unsigned long startTime = millis();

  // Recebe dados pela SPI
  for (int i = 0; i < dataSize; i++) {
    // Verifica se o tempo de espera definido em timeout foi atingido
    if (millis() - startTime > timeout) {
      // Se sim, interrompe a recepção e retorna false
      digitalWrite(SS_PIN, HIGH);
      return false;
    }
    // Recebe o byte da SPI e armazena em rxData
    rxData[i] = SPI.transfer(0x00);
  }

  // Desativando o dispositivo SPI
  digitalWrite(SS_PIN, HIGH);

  return true;
}

void addHexData(byte* txData, int dataSize) {
  for (int i = 0; i < dataSize; i++) {
    // Converte a string hexadecimal em um byte e adiciona ao buffer de envio
    txData[i] = i;

    // Imprime o valor em hexadecimal com a cor apropriada
    Serial.printf("%s 0x%02X", ANSI_RED, txData[i]);
    Serial.printf("%s", ANSI_RESET);

    // Verifica se já foram impressos 8 bytes na linha atual
    if ((i + 1) % 8 == 0) {
      Serial.println(); // Quebra linha
    }
  }

  Serial.printf(" %d bytes carregados no buffer de envio.\r\n", dataSize);
}
SPI FlashBreakout