#include <Arduino.h>

#define PREFIX_LENGTH 4
#define MAX_MESSAGE_LENGTH 255
#define TRANSMITTER_ADDRESS 0x01
#define RECEIVER_ADDRESS 0x02

// Global variables
uint8_t messageBuffer[PREFIX_LENGTH + 1 + 1 + MAX_MESSAGE_LENGTH + 1]; // Buffer for constructed message
uint8_t receivedBuffer[MAX_MESSAGE_LENGTH + 1]; // Buffer for received message
volatile size_t messageIndex = 0; // Index for the message buffer
volatile size_t receivedIndex = 0; // Index for the received buffer
volatile bool newDataAvailable = false; // Flag to indicate new data
volatile bool timerStarted = false; // Flag to indicate if timer has started
unsigned long startTime = 0; // Timer for receiving data
int timerSimulator = 0; // Timer simulator

// Function to calculate CRC by XORing all bytes of the message
uint8_t calculate_crc(uint8_t *data, size_t length) {
    uint8_t crc = 0; // Initialize CRC
    for (size_t i = 0; i < length; i++) {
        crc ^= data[i]; // XOR with each byte
    }
    return crc;
}

// Function to construct the message
void construct_message() {
    const char *dataToSend = "Either lower the delay or reduce the message length for the algorithm to finish the execution in time"; // Example data to send
    size_t length = strlen(dataToSend); // Calculate length of actual data

    if (length > MAX_MESSAGE_LENGTH) {
        Serial.println("Error: Message length exceeds maximum limit.");
        return;
    }

    // Construct the message
    uint8_t prefix[PREFIX_LENGTH] = {0x01, 0x02, 0x03, 0x04}; // Prefix
    memcpy(messageBuffer, prefix, PREFIX_LENGTH); // Copy prefix to buffer
    messageBuffer[PREFIX_LENGTH] = (uint8_t)length; // Message length
    messageBuffer[PREFIX_LENGTH + 1] = TRANSMITTER_ADDRESS; // Transmitter address
    messageBuffer[PREFIX_LENGTH + 2] = RECEIVER_ADDRESS; // Receiver address

    memcpy(&messageBuffer[PREFIX_LENGTH + 3], dataToSend, length); // Actual data

    // Calculate CRC for entire payload including prefix and data
    messageBuffer[PREFIX_LENGTH + 3 + length] = calculate_crc(messageBuffer, PREFIX_LENGTH + 3 + length); // CRC for entire payload

    // Loop through each character in the constructed message and send it with a fixed delay
    for (size_t i = 0; i < PREFIX_LENGTH + 3 + length + 1; i++) {
        delay(50); // Irrelevant Fixed delay
        
        Serial.print("Sending byte: ");
        Serial.println(messageBuffer[i], HEX);
        
        receivedBuffer[receivedIndex++] = messageBuffer[i]; // Simulate writing to received buffer
        
        if (receivedIndex >= sizeof(receivedBuffer)) {
            break;
        }
        
        newDataAvailable = true; // Set flag to indicate new data is available
        
        timerSimulator += random(10, 101); // Increment timer simulator by a random value between 10 and 100
        
        if (timerSimulator > 1000) { // Check if timer exceeds 1000 ms
            Serial.println("Error: Timeout waiting for complete message.");
            return;
        }
        
        // Simulate receiving the character immediately after sending it.
        Serial.print("Received byte: ");
        Serial.println(receivedBuffer[receivedIndex - 1], HEX);
        
        if (receivedIndex >= sizeof(receivedBuffer)) {
            break;
        }
    }
}

// Receive function
void receive_message() {
    if (!newDataAvailable) {
        return; // No new data available
    }

    unsigned long currentTime = millis(); // Get current time

    if (!timerStarted) {
        startTime = currentTime; // Start timer on first execution of receive_message
        timerStarted = true;
    }

    if (receivedBuffer[0] != 0x01 || receivedBuffer[1] != 0x02 || 
        receivedBuffer[2] != 0x03 || receivedBuffer[3] != 0x04) {
        Serial.println("Error: Invalid prefix.");
        return; // Invalid prefix, refuse to accept more data
    }

    uint8_t length = receivedBuffer[PREFIX_LENGTH]; // Message length

    if (length > MAX_MESSAGE_LENGTH) {
        Serial.println("Error: Message length exceeds maximum limit.");
        return;
    }

    uint8_t received_crc = receivedBuffer[PREFIX_LENGTH + 3 + length]; // Received CRC
    uint8_t calculated_crc = calculate_crc(receivedBuffer, PREFIX_LENGTH + 3 + length); // Calculate expected CRC

    if (calculated_crc != received_crc) {
        Serial.print("Error: CRC mismatch. Expected: ");
        Serial.print(calculated_crc, HEX);
        Serial.print(", Received: ");
        Serial.println(received_crc, HEX);
        return;
    }

    Serial.println("=== RECEIVER ===");
    Serial.print("Message received successfully:\n");
    Serial.print("Length: ");
    Serial.println(length);
    
    char finalReceivedData[MAX_MESSAGE_LENGTH + 1];
    memcpy(finalReceivedData, &receivedBuffer[PREFIX_LENGTH + 3], length); // Extract actual data
    finalReceivedData[length] = '\0'; // Null-terminate the string

    Serial.print("Data: '");
    Serial.print(finalReceivedData);
    Serial.println("'");

    Serial.print("Received CRC: ");
    Serial.println(received_crc, HEX);
    
    Serial.print("Calculated CRC: ");
    Serial.println(calculated_crc, HEX);

    newDataAvailable = false; // Reset flag after processing the data
}

void setup() {
   Serial.begin(115200);
   delay(1000); // Allow time for serial monitor to open

   construct_message(); // Construct and transmit the complete message
   
   receive_message(); // Attempt to receive the transmitted data
}

void loop() {
   // Not needed here as everything is handled in setup.
}