// 20260413 ChatGPT
// Exercise 20: microphone recorder

#include <Arduino.h>

#include "MicrophoneRecorder.h"

#ifndef NODE_LABEL
#define NODE_LABEL "MIC"
#endif

#ifndef MIC_RECORD_SECONDS
#define MIC_RECORD_SECONDS 30
#endif

#ifndef MIC_IDLE_SECONDS
#define MIC_IDLE_SECONDS 30
#endif

#ifndef MIC_STARTUP_WAIT_SECONDS
#define MIC_STARTUP_WAIT_SECONDS 10
#endif

namespace {

constexpr uint32_t kSerialDelayMs = 1500;
constexpr uint32_t kBetweenRecordingsMs = MIC_IDLE_SECONDS * 1000UL;

MicrophoneRecorder g_recorder(Serial);
bool g_started = false;
bool g_finished = false;
size_t g_caseIndex = 0;
uint32_t g_nextActionMs = 0;

struct SweepCase {
  const char* label;
  MicrophoneRecorder::MicRuntimeConfig cfg;
};

SweepCase g_cases[] = {
    {"base", {.dataPin = MIC_DATA_PIN, .clkPin = MIC_CLK_PIN, .selectPin = MIC_SELECT_PIN, .selectLeft = (MIC_SELECT_LEFT != 0)}},
    {"flip_lr", {.dataPin = MIC_DATA_PIN, .clkPin = MIC_CLK_PIN, .selectPin = MIC_SELECT_PIN, .selectLeft = (MIC_SELECT_LEFT == 0)}},
    {"swap_pins", {.dataPin = MIC_CLK_PIN, .clkPin = MIC_DATA_PIN, .selectPin = MIC_SELECT_PIN, .selectLeft = (MIC_SELECT_LEFT != 0)}},
    {"swap_flip", {.dataPin = MIC_CLK_PIN, .clkPin = MIC_DATA_PIN, .selectPin = MIC_SELECT_PIN, .selectLeft = (MIC_SELECT_LEFT == 0)}},
};

void printWhatToSay(const SweepCase& sweepCase) {
  Serial.println();
  Serial.println("Speak this sentence clearly 3 times during the test:");
  Serial.println("\"Testing T-Beam microphone case "
                 + String(sweepCase.label)
                 + ". One two three four five.\"");
  Serial.println("Look for console lines where:");
  Serial.println("  status=active");
  Serial.println("  min and max change over time and are not equal");
  Serial.println("  avgabs is not stuck at one repeated value");
  Serial.println("If status=flat with min=max every second, that case is bad.");
}

}  // namespace

void setup() {
  Serial.begin(115200);
  delay(kSerialDelayMs);

  Serial.println();
  Serial.println("============================================================");
  Serial.printf("Exercise 20 Microphone Recorder [%s]\r\n", NODE_LABEL);
  Serial.println("============================================================");

  if (!g_recorder.begin()) {
    Serial.println("Recorder init failed; loop will retry.");
  }

  Serial.printf("Waiting %u seconds before the first recording.\r\n",
                (unsigned)MIC_STARTUP_WAIT_SECONDS);
  g_nextActionMs = millis() + MIC_STARTUP_WAIT_SECONDS * 1000UL;
}

void loop() {
  if (!g_recorder.isSdReady() || g_finished) {
    delay(1000);
    return;
  }

  const uint32_t now = millis();
  if ((int32_t)(now - g_nextActionMs) < 0) {
    delay(100);
    return;
  }

  if (g_caseIndex >= (sizeof(g_cases) / sizeof(g_cases[0]))) {
    Serial.println("Microphone sweep complete. No more recordings will be started.");
    g_finished = true;
    return;
  }

  SweepCase& sweepCase = g_cases[g_caseIndex];
  g_recorder.setRuntimeConfig(sweepCase.cfg);

  char path[96];
  snprintf(path,
           sizeof(path),
           "/recordings/mic_%s_%lu.wav",
           sweepCase.label,
           (unsigned long)time(nullptr));

  Serial.println();
  Serial.printf("Starting case %u/%u: %s data=%d clk=%d select=%d left=%d\r\n",
                (unsigned)(g_caseIndex + 1),
                (unsigned)(sizeof(g_cases) / sizeof(g_cases[0])),
                sweepCase.label,
                sweepCase.cfg.dataPin,
                sweepCase.cfg.clkPin,
                sweepCase.cfg.selectPin,
                sweepCase.cfg.selectLeft ? 1 : 0);
  printWhatToSay(sweepCase);

  if (!g_recorder.open(path)) {
    Serial.println("Failed to open output file; delaying and retrying.");
    g_nextActionMs = millis() + 1000;
    delay(100);
    return;
  }

  const bool ok = g_recorder.captureForMs(MIC_RECORD_SECONDS * 1000UL);
  g_recorder.close();

  Serial.printf("case %s status=%s next_case_in=%lus\r\n",
                sweepCase.label,
                ok ? "ok" : "failed",
                (unsigned long)(kBetweenRecordingsMs / 1000UL));

  g_caseIndex++;
  g_nextActionMs = millis() + kBetweenRecordingsMs;
  delay(100);
}
