#pragma once

#include <Arduino.h>

// ------------------------- Hardware configuration -------------------------
// Enabled for the hand-wired prototype. Comment out for the production PCB.
// Both profiles map S3 signals by physical header position with 5V/GND aligned.
#define MAKETKA

// Select exactly one populated receiver circuit. Use
// BOARD_RX_INTERFACE_DIGITAL for MAKETKA and boards fitted with GPIO_RX.
#define BOARD_RX_INTERFACE_ADC 1
#define BOARD_RX_INTERFACE_DIGITAL 2
#ifndef BOARD_RX_INTERFACE
#define BOARD_RX_INTERFACE BOARD_RX_INTERFACE_DIGITAL
#endif

#if BOARD_RX_INTERFACE != BOARD_RX_INTERFACE_ADC && \
    BOARD_RX_INTERFACE != BOARD_RX_INTERFACE_DIGITAL
#error "BOARD_RX_INTERFACE must select ADC or DIGITAL"
#endif

#if defined(MAKETKA) && BOARD_RX_INTERFACE == BOARD_RX_INTERFACE_ADC
#error "MAKETKA requires BOARD_RX_INTERFACE_DIGITAL"
#endif

constexpr bool BOARD_RX_USES_ADC =
  BOARD_RX_INTERFACE == BOARD_RX_INTERFACE_ADC;

// Board-test parameters. These checks are selected at runtime from the UI;
// no special diagnostic firmware build is required.
constexpr uint32_t BOARD_PWM_TEST_FREQUENCY_HZ = 1;
constexpr uint32_t BOARD_PWM_TEST_HALF_PERIOD_MS = 500;

#if CONFIG_IDF_TARGET_ESP32C3
constexpr bool TARGET_IS_C3 = true;
constexpr uint8_t GPIO_PWM = 3;
constexpr uint8_t GPIO_RX = 4;
#ifdef MAKETKA
constexpr bool BOARD_ADC_AVAILABLE = false;
constexpr uint8_t GPIO_BUTTON_MODE = 0;
constexpr uint8_t GPIO_BUTTON_START = 1;
constexpr uint8_t GPIO_OPTICAL_CURRENT = 2;
constexpr uint8_t GPIO_OPTICAL_VCC = 5;
constexpr uint8_t GPIO_VBAT = UINT8_MAX;
constexpr uint8_t GPIO_ANALOG_RX = UINT8_MAX;
#else
constexpr bool BOARD_ADC_AVAILABLE = true;
constexpr uint8_t GPIO_BUTTON_MODE = 20;
constexpr uint8_t GPIO_BUTTON_START = 10;
constexpr uint8_t GPIO_OPTICAL_CURRENT = 1;
constexpr uint8_t GPIO_OPTICAL_VCC = 5;
constexpr uint8_t GPIO_VBAT = 2;
constexpr uint8_t GPIO_ANALOG_RX = 0;
#endif
constexpr uint8_t GPIO_SDA = 6;
constexpr uint8_t GPIO_SCL = 7;
#elif CONFIG_IDF_TARGET_ESP32S3
constexpr bool TARGET_IS_C3 = false;
#ifdef MAKETKA
constexpr bool BOARD_ADC_AVAILABLE = false;
// Same physical header contacts as the C3 MAKETKA profile when 5V/GND align.
constexpr uint8_t GPIO_PWM = 12;
constexpr uint8_t GPIO_RX = 13;
constexpr uint8_t GPIO_BUTTON_MODE = 9;
constexpr uint8_t GPIO_BUTTON_START = 10;
constexpr uint8_t GPIO_OPTICAL_CURRENT = 2;
constexpr uint8_t GPIO_OPTICAL_VCC = 6;
constexpr uint8_t GPIO_SDA = 44;
constexpr uint8_t GPIO_SCL = 1;
constexpr uint8_t GPIO_VBAT = UINT8_MAX;
constexpr uint8_t GPIO_ANALOG_RX = UINT8_MAX;
#else
constexpr bool BOARD_ADC_AVAILABLE = true;
// The S3 SuperMini is fitted so its 5V and GND pins occupy the same PCB
// contacts as on the C3 SuperMini. Signals therefore follow header position.
constexpr uint8_t GPIO_PWM = 12;
constexpr uint8_t GPIO_RX = 13;
constexpr uint8_t GPIO_BUTTON_MODE = 5;
constexpr uint8_t GPIO_BUTTON_START = 4;
constexpr uint8_t GPIO_OPTICAL_CURRENT = 10;
constexpr uint8_t GPIO_OPTICAL_VCC = 6;
constexpr uint8_t GPIO_SDA = 44;
constexpr uint8_t GPIO_SCL = 1;
constexpr uint8_t GPIO_VBAT = 11;
constexpr uint8_t GPIO_ANALOG_RX = 9;
#endif
#else
#error "Only ESP32-C3 and ESP32-S3 are supported"
#endif

static_assert(GPIO_OPTICAL_CURRENT != GPIO_PWM &&
              GPIO_OPTICAL_CURRENT != GPIO_RX &&
              GPIO_OPTICAL_CURRENT != GPIO_BUTTON_MODE &&
              GPIO_OPTICAL_CURRENT != GPIO_BUTTON_START &&
              GPIO_OPTICAL_CURRENT != GPIO_SDA &&
              GPIO_OPTICAL_CURRENT != GPIO_SCL,
  "Optical-current ADC GPIO conflicts with another board signal");
static_assert(GPIO_OPTICAL_VCC != GPIO_OPTICAL_CURRENT &&
              GPIO_OPTICAL_VCC != GPIO_PWM &&
              GPIO_OPTICAL_VCC != GPIO_RX &&
              GPIO_OPTICAL_VCC != GPIO_BUTTON_MODE &&
              GPIO_OPTICAL_VCC != GPIO_BUTTON_START &&
              GPIO_OPTICAL_VCC != GPIO_SDA &&
              GPIO_OPTICAL_VCC != GPIO_SCL,
  "Optical-VCC ADC GPIO conflicts with another board signal");

constexpr uint8_t OLED_ROTATION = 0;

constexpr uint8_t OLED_ADDRESS = 0x3C;
constexpr uint8_t ESPNOW_WIFI_CHANNEL = 6;
constexpr uint32_t SERIAL_BAUD = 115200;
// Native USB CDC may keep a stale "connected" state after light sleep. Keep
// logging non-blocking so a missing host can never delay button polling.
constexpr uint32_t SERIAL_TX_TIMEOUT_MS = 2;
constexpr uint32_t BOARD_ADC_PRINT_INTERVAL_MS = 100;
constexpr uint32_t BOARD_TEST_DISPLAY_INTERVAL_MS = 250;
constexpr float OPTICAL_VCC = 5.1f;
constexpr float OPTICAL_SENSE_R = 47.0f;
constexpr float OPTICAL_DIVIDER_RATIO = 2.0f;
constexpr uint32_t OPTICAL_CURRENT_SETTLE_MS = 20;
constexpr uint32_t OPTICAL_CURRENT_AVERAGING_MS = 200;
constexpr bool SERIAL_ACTION_LOG = true;
constexpr bool SERIAL_LOG_TIMESTAMPS = true;
constexpr bool SERIAL_MINIMAL_LOG = true;

#define BUTTON_ACTIVE_LEVEL LOW
// Fixed PCB conversion between electrical GPIO levels and actual optical
// light. User settings HH/HL/LH/LL operate only in the optical domain and
// never change these hardware facts.
#define TX_LIGHT_ON_GPIO_LEVEL LOW
#define RX_LIGHT_ON_GPIO_LEVEL LOW
#define TX_LIGHT_OFF_GPIO_LEVEL (TX_LIGHT_ON_GPIO_LEVEL == HIGH ? LOW : HIGH)
#define PWM_SETTLE_CYCLES 5U

constexpr uint32_t BUTTON_DEBOUNCE_MS = 30;
constexpr uint32_t BUTTON_LONG_PRESS_MS = 500;
constexpr uint32_t BUTTON_REPEAT_DELAY_MS = 600;
constexpr uint32_t BUTTON_REPEAT_MS = 180;
constexpr uint32_t FACTORY_RESET_HOLD_MS = 1500;

constexpr uint32_t LINK_REPLY_TIMEOUT_MS = 1500;
constexpr uint8_t LINK_PACKET_RETRIES = 10;
constexpr uint32_t LINK_RETRY_INTERVAL_MS = 1000;
constexpr uint32_t DISCOVERY_RETRY_INTERVAL_MS = 20;
// During discovery Master alternates actual optical light ON and OFF to wake
// a sleeping Slave through the optical channel.
constexpr uint32_t OPTICAL_WAKE_HALF_PERIOD_MS = 50;
constexpr uint32_t LINK_HEARTBEAT_INTERVAL_MS = 500;
constexpr uint32_t LINK_HEARTBEAT_TIMEOUT_MS = 2500;
constexpr uint32_t FINAL_ACK_RETRY_INTERVAL_MS = 50;
constexpr uint8_t FINAL_ACK_RETRIES = 2;
constexpr uint8_t NO_SIGNAL_TIMEOUT_PERIODS = 8;
constexpr uint16_t PERIOD_BATCH_SIZE = 128;
// Retained as the minimum statistical depth used by the hardware-resolution
// calculation and diagnostics. PASS/FAIL is evaluated for every complete
// pulse independently; accumulated values are used only for display.
constexpr uint16_t MEASUREMENT_AVERAGING_PERIODS = 100;
static_assert(MEASUREMENT_AVERAGING_PERIODS > 0,
  "Averaging window must contain at least one period");
constexpr uint8_t MEASUREMENT_PROGRESS_STEPS = 10;
constexpr uint32_t OLED_PROGRESS_UPDATE_MS = 15;
constexpr uint8_t DRIVER_SHORT_SAMPLE_PROGRESS_STEPS = 10;
constexpr uint32_t DRIVER_PROGRESS_INTERVAL_MS = 100;

constexpr uint32_t IDLE_POWER_SAVE_TIMEOUT_MS = 60000;
// usb_serial_jtag_is_connected() needs no open COM port or CDC traffic, but a
// short SOF detection gap must not send the board to sleep.
constexpr uint32_t USB_HOST_DISCONNECT_GRACE_MS = 5000;
constexpr uint16_t SLAVE_LISTEN_INTERVAL_MS = 100;
constexpr uint16_t SLAVE_LISTEN_WINDOW_MS = 20;
static_assert(SLAVE_LISTEN_WINDOW_MS < SLAVE_LISTEN_INTERVAL_MS,
  "Slave listen window must be shorter than its interval");
// Conservative sustained validation rate calibrated from real C3 logs.
constexpr uint32_t RX_PROCESSING_PERIODS_PER_SECOND = 300000;

constexpr uint32_t C3_STRICT_MAX_HZ = 1000000;
constexpr uint32_t S3_STRICT_MAX_HZ = 1000000;
// S3 MCPWM Capture uses one 32-bit 80 MHz timer for both edges. Unlike RMT,
// its width does not constrain long LOW/HIGH intervals, so capture precision
// stays at 12.5 ns for every selectable PWM frequency and pulse length.
constexpr uint32_t MCPWM_CAPTURE_RESOLUTION_HZ = 80000000;
// C3 uses the 40 MHz crystal as the LEDC clock.
// Keep this explicit so the resolution calculation never asks LEDC for an
// impossible frequency/resolution combination.
constexpr uint32_t LEDC_SOURCE_CLOCK_HZ = 40000000;
constexpr uint8_t LEDC_CHANNEL = 0;
constexpr uint8_t LEDC_MAX_BITS = 14;
// S3 uses the dedicated MCPWM peripheral. A 20 MHz timer clock keeps the
// selectable 500 Hz period within the S3's 16-bit counter while retaining
// 50 ns pulse resolution and exact periods for every menu frequency.
constexpr uint32_t MCPWM_RESOLUTION_HZ = 20000000;
constexpr uint32_t MCPWM_MAX_PERIOD_TICKS = 65535;

// Concept 1SP0635 status acknowledgement, expressed in the optical domain.
constexpr uint32_t DRIVER_MIN_INPUT_PULSE_NS = 2000;
constexpr uint32_t DRIVER_ACK_DELAY_NS = 250;
constexpr uint32_t DRIVER_ACK_WIDTH_NS = 700;
constexpr uint32_t DRIVER_ACK_START_MAX_NS = 2000;
constexpr uint32_t DRIVER_ACK_MERGE_MARGIN_NS = 250;
// The first MCPWM TX end may belong to a pulse that was already active when
// capture was enabled. The following period also drains capture events that
// were pending independently in the rising/falling channels. Validation
// therefore begins at the third TX period.
constexpr uint8_t DRIVER_CAPTURE_SYNC_CYCLES = 2;
// Any response this long is a fault, not a normal acknowledgement.
constexpr uint32_t DRIVER_FAULT_MIN_NS = 1500;
constexpr uint32_t DRIVER_RX_STUCK_MIN_NS = 20000;
// Retained by the generic receiver backend; the driver test itself uses the
// stricter ACK start deadline above.
constexpr uint32_t DRIVER_RESPONSE_TIMEOUT_NS = 10000;

// -------------------------- Menu value arrays -----------------------------
// The test uses one selected PWM frequency and walks the pulse-width list from
// the selected maximum down to the selected minimum. Widths are stored in
// nanoseconds so sub-microsecond pulses remain representable without floats.
constexpr uint32_t PWM_FREQUENCY_OPTIONS_HZ[] = {
  500, 1000, 2000, 5000, 10000,
};
constexpr uint32_t MAX_PULSE_OPTIONS_NS[] = {
  2000, 5000, 10000, 50000, 100000, 500000
};
constexpr uint32_t MIN_PULSE_OPTIONS_NS[] = {
  250, 500, 1000, 2000, 5000, 10000, 50000
};
constexpr uint32_t TEST_PULSE_WIDTHS_NS[] = {
  50, 100, 150, 200, 250, 500, 1000, 2000, 5000, 10000, 20000, 50000,
  100000, 200000, 500000, 1000000
};
constexpr float ACCURACY_OPTIONS_PCT[] = {1.0f, 2.0f, 5.0f, 10.0f};
constexpr uint32_t TEST_TIME_OPTIONS_MS[] = {100, 250, 500, 1000, 2000, 5000, 60000};

template <typename T, size_t N> constexpr size_t countOf(const T (&)[N]) { return N; }
