bool sendCharInterruptible(char c)
{
  if (generatorShouldStop()) {
    return false;
  }

  uint8_t code;

  if (c >= 'a' && c <= 'z') {
    c = c - 'a' + 'A';
  }

  if (findInTable(c, lettersTable, sizeof(lettersTable) / sizeof(BaudotEntry), code)) {
    if (figuresMode) {
      sendCode(LTRS);
      figuresMode = false;
      if (generatorShouldStop()) return false;
    }
    sendCode(code);
    return !generatorShouldStop();
  }

  if (findInTable(c, figuresTable, sizeof(figuresTable) / sizeof(BaudotEntry), code)) {
    if (!figuresMode) {
      sendCode(FIGS);
      figuresMode = true;
      if (generatorShouldStop()) return false;
    }
    sendCode(code);
    return !generatorShouldStop();
  }

  return true;
}

bool sendStringInterruptible(const char *s)
{
  while (*s) {
    if (!sendCharInterruptible(*s++)) {
      return false;
    }
  }

  if (figuresMode) {
    if (generatorShouldStop()) return false;
    sendCode(LTRS);
    figuresMode = false;
  }

  return !generatorShouldStop();
}

bool newLineInterruptible()
{
  if (generatorShouldStop()) return false;
  sendCode(CR);
  if (generatorShouldStop()) return false;
  sendCode(LF);
  return !generatorShouldStop();
}

bool readCode(uint8_t &code)
{
  bool currentState = digitalRead(baudotRxPin);

  if (!(lastRxLineState == HIGH && currentState == LOW)) {
    lastRxLineState = currentState;
    return false;
  }

  lastRxLineState = currentState;

  preciseDelay(bitTimeUs + bitTimeUs / 2);

  uint8_t value = 0;

  for (int i = 0; i < 5; i++) {
    if (digitalRead(baudotRxPin) == HIGH) {
      value |= (1 << i);
    }
    preciseDelay(bitTimeUs);
  }

  bool stopBitOk = (digitalRead(baudotRxPin) == HIGH);
  preciseDelay(bitTimeUs / 2);

  if (!stopBitOk) {
    Serial.print("<FRAMING>");
    return false;
  }

  code = value;
  return true;
}

void decodeAndShowCode(uint8_t code)
{
  if (code == LTRS) {
    rxFiguresMode = false;
    displayNeedsUpdate = true;
    Serial.print("<LTRS>");
    return;
  }

  if (code == FIGS) {
    rxFiguresMode = true;
    displayNeedsUpdate = true;
    Serial.print("<FIGS>");
    return;
  }

  if (code == CR) {
    Serial.print("\r");
    pushRxHistoryChar(' ');
    return;
  }

  if (code == LF) {
    Serial.print("\n");
    pushRxHistoryChar(' ');
    return;
  }

  if (rxFiguresMode && code == BELL) {
    Serial.print("<BELL>");
    setLastReceivedChar('*');
    pushRxHistoryChar('*');
    return;
  }

  if (rxFiguresMode && code == MSTOP) {
    Serial.print("<MSTOP>");
    setLastReceivedChar('M');
    pushRxHistoryChar('M');
    return;
  }

  char c;
  if (rxFiguresMode) {
    c = decodeFromTable(code, figuresTable, sizeof(figuresTable) / sizeof(BaudotEntry));
  } else {
    c = decodeFromTable(code, lettersTable, sizeof(lettersTable) / sizeof(BaudotEntry));
  }

  if (c != '\0') {
    Serial.print(c);
    setLastReceivedChar(c);
    pushRxHistoryChar(c);
  } else {
    Serial.print("<0x");
    if (code < 16) Serial.print("0");
    Serial.print(code, HEX);
    Serial.print(">");
    setLastReceivedChar('?');
    pushRxHistoryChar('?');
  }
}

void runReceiveMonitorStep()
{
  if (!afskActive) {
    afskStart();   // una sola volta
  }

  uint8_t code;
  if (readCode(code)) {
    decodeAndShowCode(code);
    sendCodeAfskOnly(code);
  }
}

void runAfskTestStep()
{
  static bool markState = true;
  static unsigned long lastToggleMs = 0;
  const unsigned long toggleIntervalMs = 3000;

  if (!afskActive) {
    afskStart();
    markState = true;
    afskSetTone(markState);
    lastToggleMs = millis();
  }

  if (millis() - lastToggleMs >= toggleIntervalMs) {
    lastToggleMs = millis();
    markState = !markState;
    afskSetTone(markState);

    Serial.print("AFSK test tone: ");
    Serial.println(markState ? "MARK" : "SPACE");
  }
}

void runGeneratorOnce()
{
  afskStart();

  if (!sendStringInterruptible("AP NEW YORK NY JUL 21")) { afskStop(); return; }
  if (!newLineInterruptible()) { afskStop(); return; }

  if (!sendStringInterruptible("FLASH")) { afskStop(); return; }
  if (!newLineInterruptible()) { afskStop(); return; }

  if (!sendStringInterruptible("WASHINGTON (AP) -- APOLLO 11 ASTRONAUTS NEIL ARMSTRONG")) { afskStop(); return; }
  if (!newLineInterruptible()) { afskStop(); return; }

  if (!sendStringInterruptible("AND EDWIN ALDRIN TODAY WALKED ON THE SURFACE OF THE MOON")) { afskStop(); return; }
  if (!newLineInterruptible()) { afskStop(); return; }

  if (!sendStringInterruptible("WHILE MILLIONS OF PERSONS THROUGHOUT THE WORLD WATCHED")) { afskStop(); return; }
  if (!newLineInterruptible()) { afskStop(); return; }

  if (!sendStringInterruptible("THE EVENT ON TELEVISION.")) { afskStop(); return; }
  if (!newLineInterruptible()) { afskStop(); return; }

  if (!sendStringInterruptible("ARMSTRONG STEPPED FROM THE LUNAR MODULE EAGLE AT 10:56 PM")) { afskStop(); return; }
  if (!newLineInterruptible()) { afskStop(); return; }

  if (!sendStringInterruptible("EDT AND SAID, QUOTE, THAT'S ONE SMALL STEP FOR MAN, ONE")) { afskStop(); return; }
  if (!newLineInterruptible()) { afskStop(); return; }

  if (!sendStringInterruptible("GIANT LEAP FOR MANKIND, END QUOTE.")) { afskStop(); return; }
  if (!newLineInterruptible()) { afskStop(); return; }

  if (!sendStringInterruptible("ALDRIN FOLLOWED MINUTES LATER. THE TWO ASTRONAUTS SET UP")) { afskStop(); return; }
  if (!newLineInterruptible()) { afskStop(); return; }

  if (!sendStringInterruptible("SCIENTIFIC INSTRUMENTS, TOOK PHOTOGRAPHS AND COLLECTED")) { afskStop(); return; }
  if (!newLineInterruptible()) { afskStop(); return; }

  if (!sendStringInterruptible("SAMPLES OF LUNAR ROCKS.")) { afskStop(); return; }
  if (!newLineInterruptible()) { afskStop(); return; }

  if (!sendStringInterruptible("NNNN")) { afskStop(); return; }
  if (!newLineInterruptible()) { afskStop(); return; }

  if (generatorShouldStop()) { afskStop(); return; }
  sendCode(FIGS);
  figuresMode = true;

  if (generatorShouldStop()) { afskStop(); return; }
  sendCode(BELL);

  if (generatorShouldStop()) { afskStop(); return; }
  sendCode(MSTOP);

  unsigned long t0 = millis();
  while (millis() - t0 < 5000) {
    if (generatorShouldStop()) { afskStop(); return; }
    delay(10);
  }

  if (generatorShouldStop()) { afskStop(); return; }
  sendMotorStartPulse();

  t0 = millis();
  while (millis() - t0 < 500) {
    if (generatorShouldStop()) { afskStop(); return; }
    delay(10);
  }

  if (generatorShouldStop()) { afskStop(); return; }
  sendCode(CR);

  if (generatorShouldStop()) { afskStop(); return; }
  sendCode(LTRS);
  figuresMode = false;

  if (generatorShouldStop()) { afskStop(); return; }
  sendCode(LF);

  afskStop();
}