#include <avr/pgmspace.h>
#include <stdint.h>

// Pin where the LED is connected
const int ledPin = 9;

// Time interval (in microseconds) for each bit
const unsigned long bitDuration = 50; // in microseconds

// Preloaded message stored in an array of uint64_t
const uint64_t binaryMessage[] PROGMEM = {
    0b1101100010100111101101110111000100010111010111000100101010001100,
    0b1000100010011110010101110111010101001100110100010110101010011100,
    0b0110101011011010101011010010010011010101011101010111010010101010,
    0b1111000011110000111100001111000011110000111100001111000011110000,
    0b1010101010101010101010101010101010101010101010101010101010101010,
    0b0101010101010101010101010101010101010101010101010101010101010101,
    0b1110001110001110001110001110001110001110001110001110001110001110,
    0b0001110001110001110001110001110001110001110001110001110001110001,
    0b1010010110100101101001011010010110100101101001011010010110100101,
    0b0101101001011010010110100101101001011010010110100101101001011010
};

const int messageLength = sizeof(binaryMessage) / sizeof(binaryMessage[0]);

// Variables to manage timing and state
unsigned long previousMicros = 0; // Tracks the last time a bit was processed
int currentBitIndex = 0;          // Tracks the current bit being transmitted
int currentArrayIndex = 0;        // Tracks which uint64_t is being transmitted
int messageCount = 0;             // Tracks the number of times the message is sent
bool transmitting = false;        // Whether transmission has started

void setup() {
  pinMode(ledPin, OUTPUT);
  Serial.begin(115200); // Set Serial Monitor speed to 115200
  Serial.println("Type S to begin transmission.");
}

void loop() {
  // Check for command to start transmission
  if (!transmitting && Serial.available()) {
    String command = Serial.readStringUntil('\n');
    command.trim();
    if (command.equalsIgnoreCase("S")) {
      transmitting = true;
      Serial.println("Starting transmission...");

      // Send a long 1 (LED ON) for 5 seconds
      digitalWrite(ledPin, HIGH);
      delay(2000);

      // Send a long 0 (LED OFF) for 1 seconds
      digitalWrite(ledPin, LOW);
      delay(1000);

      previousMicros = micros(); // Reset timing
    }
  }

  // Transmit message if started
  if (transmitting) {
    unsigned long currentMicros = micros();

    // Check if it's time to process the next bit
    if (currentMicros - previousMicros >= bitDuration) {
      previousMicros = currentMicros; // Update the timestamp

      // Transmit the current bit
      if (currentArrayIndex < messageLength) {
        // Read the current uint64_t from program memory
        uint64_t currentWord = pgm_read_qword_near(binaryMessage + currentArrayIndex);

        // Extract the current bit
        int bitValue = (currentWord >> (63 - currentBitIndex)) & 1;

        // Set the LED state based on the bit value
        digitalWrite(ledPin, bitValue);

        // Print the bit to the Serial Monitor
        // Serial.print(bitValue);

        // Move to the next bit
        currentBitIndex++;
        if (currentBitIndex >= 64) {
          currentBitIndex = 0; // Reset to the first bit of the next uint64_t
          currentArrayIndex++; // Move to the next uint64_t
        }
      } else {
        // End of message: reset for next transmission or stop
        Serial.println(); // Newline after full message
        currentBitIndex = 0;
        currentArrayIndex = 0;
        messageCount++;

        if (messageCount >= 5) { // Change this value for multiple transmissions
          Serial.println("Transmission complete.");
          transmitting = false; // Stop transmitting
        }
      }
    }
  }
}

// Helper function to read 64-bit integers from PROGMEM
uint64_t pgm_read_qword_near(const void* addr) {
  uint64_t value = 0;
  for (int i = 0; i < 8; i++) {
    value |= ((uint64_t)pgm_read_byte_near((const uint8_t*)addr + i)) << (i * 8);
  }
  return value;
}