// First-pixels bringup test for the Waveshare ESP32-S3-ePaper-13.3E6.
//
// Renders the panel's six color bars (BLACK/BLUE/GREEN/RED/YELLOW/WHITE)
// — same pattern as Waveshare's stock demo, ported to Arduino-on-S3.
// Self-contained on purpose: no LittleFS, no shared epd.h, no PSRAM
// framebuffer, no panels/ driver. Once pixels render, refactor into the
// existing panels/{vendor}/v{N}/ structure.
//
// Pin map verified against the ESP32-S3-ePaper-13.3E6 (all-in-one) board,
// not the separate ESP32 driver board (which uses different GPIOs).
//
// Source of init sequence + display protocol:
//   github.com/teatall/13.3inch_e-Paper_E-Frame (community project for
//   the same Waveshare board) and Waveshare's official 13in3e demo.

#ifdef ENV_TEST_DISPLAY_13E6

#include <Arduino.h>

static constexpr int PIN_SCK   = 9;
static constexpr int PIN_MOSI  = 46;
static constexpr int PIN_CS_M  = 10;
static constexpr int PIN_CS_S  = 3;
static constexpr int PIN_RST   = 2;
static constexpr int PIN_DC    = 11;
static constexpr int PIN_BUSY  = 12;
static constexpr int PIN_PWR   = 1;

static constexpr uint16_t EPD_W = 1200;
static constexpr uint16_t EPD_H = 1600;

static constexpr uint8_t C_BLACK  = 0x0;
static constexpr uint8_t C_WHITE  = 0x1;
static constexpr uint8_t C_YELLOW = 0x2;
static constexpr uint8_t C_RED    = 0x3;
static constexpr uint8_t C_BLUE   = 0x5;
static constexpr uint8_t C_GREEN  = 0x6;

static inline void cs_both(int v) {
    digitalWrite(PIN_CS_M, v);
    digitalWrite(PIN_CS_S, v);
}

// Bit-banged MSB-first SPI matching Waveshare's reference timing exactly.
// DC and CS are handled by the caller. Cmd byte goes out while DC is low;
// data while DC is high.
static void spi_write_byte(uint8_t b) {
    for (int i = 0; i < 8; i++) {
        digitalWrite(PIN_MOSI, (b & 0x80) ? HIGH : LOW);
        b <<= 1;
        digitalWrite(PIN_SCK, HIGH);
        digitalWrite(PIN_SCK, LOW);
    }
}

static void send_cmd(uint8_t cmd) {
    digitalWrite(PIN_DC, LOW);
    spi_write_byte(cmd);
    digitalWrite(PIN_DC, HIGH);
}

static void send_data(uint8_t d) {
    spi_write_byte(d);
}

static void send_cmd_with_data(uint8_t cmd, const uint8_t* buf, size_t len) {
    send_cmd(cmd);
    for (size_t i = 0; i < len; i++) spi_write_byte(buf[i]);
}

// BUSY is active-LOW: panel pulls it low while working, releases high when idle.
static void wait_busy_idle() {
    Serial.println("busy: wait");
    while (digitalRead(PIN_BUSY) == LOW) {
        delay(10);
    }
    delay(20);
    Serial.println("busy: idle");
}

static void panel_reset() {
    digitalWrite(PIN_RST, HIGH); delay(30);
    digitalWrite(PIN_RST, LOW);  delay(30);
    digitalWrite(PIN_RST, HIGH); delay(30);
    digitalWrite(PIN_RST, LOW);  delay(30);
    digitalWrite(PIN_RST, HIGH); delay(30);
}

static void panel_init() {
    panel_reset();

    static const uint8_t AN_TM_V[]          = {0xC0,0x1C,0x1C,0xCC,0xCC,0xCC,0x15,0x15,0x55};
    static const uint8_t CMD66_V[]          = {0x49,0x55,0x13,0x5D,0x05,0x10};
    static const uint8_t PSR_V[]            = {0xDF,0x69};
    static const uint8_t CDI_V[]            = {0xF7};
    static const uint8_t TCON_V[]           = {0x03,0x03};
    static const uint8_t AGID_V[]           = {0x10};
    static const uint8_t PWS_V[]            = {0x22};
    static const uint8_t CCSET_V[]          = {0x01};
    static const uint8_t TRES_V[]           = {0x04,0xB0,0x03,0x20};
    static const uint8_t PWR_V[]            = {0x0F,0x00,0x28,0x2C,0x28,0x38};
    static const uint8_t EN_BUF_V[]         = {0x07};
    static const uint8_t BTST_P_V[]         = {0xE8,0x28};
    static const uint8_t BOOST_VDDP_EN_V[]  = {0x01};
    static const uint8_t BTST_N_V[]         = {0xE8,0x28};
    static const uint8_t BUCK_VDDN_V[]      = {0x01};
    static const uint8_t TFT_VCOM_V[]       = {0x02};

    // The first few commands go to BOTH halves of the panel (CS_M then CS_S
    // released together). After that, several "tuning" commands are master-
    // only — exactly as in the reference, even though it's not obvious why.
    digitalWrite(PIN_CS_M, LOW);
    send_cmd_with_data(0x74, AN_TM_V, sizeof(AN_TM_V));
    cs_both(HIGH);

    cs_both(LOW);  send_cmd_with_data(0xF0, CMD66_V, sizeof(CMD66_V));         cs_both(HIGH);
    cs_both(LOW);  send_cmd_with_data(0x00, PSR_V,   sizeof(PSR_V));           cs_both(HIGH);
    cs_both(LOW);  send_cmd_with_data(0x50, CDI_V,   sizeof(CDI_V));           cs_both(HIGH);
    cs_both(LOW);  send_cmd_with_data(0x60, TCON_V,  sizeof(TCON_V));          cs_both(HIGH);
    cs_both(LOW);  send_cmd_with_data(0x86, AGID_V,  sizeof(AGID_V));          cs_both(HIGH);
    cs_both(LOW);  send_cmd_with_data(0xE3, PWS_V,   sizeof(PWS_V));           cs_both(HIGH);
    cs_both(LOW);  send_cmd_with_data(0xE0, CCSET_V, sizeof(CCSET_V));         cs_both(HIGH);
    cs_both(LOW);  send_cmd_with_data(0x61, TRES_V,  sizeof(TRES_V));          cs_both(HIGH);

    digitalWrite(PIN_CS_M, LOW); send_cmd_with_data(0x01, PWR_V,           sizeof(PWR_V));           cs_both(HIGH);
    digitalWrite(PIN_CS_M, LOW); send_cmd_with_data(0xB6, EN_BUF_V,        sizeof(EN_BUF_V));        cs_both(HIGH);
    digitalWrite(PIN_CS_M, LOW); send_cmd_with_data(0x06, BTST_P_V,        sizeof(BTST_P_V));        cs_both(HIGH);
    digitalWrite(PIN_CS_M, LOW); send_cmd_with_data(0xB7, BOOST_VDDP_EN_V, sizeof(BOOST_VDDP_EN_V)); cs_both(HIGH);
    digitalWrite(PIN_CS_M, LOW); send_cmd_with_data(0x05, BTST_N_V,        sizeof(BTST_N_V));        cs_both(HIGH);
    digitalWrite(PIN_CS_M, LOW); send_cmd_with_data(0xB0, BUCK_VDDN_V,     sizeof(BUCK_VDDN_V));     cs_both(HIGH);
    digitalWrite(PIN_CS_M, LOW); send_cmd_with_data(0xB1, TFT_VCOM_V,      sizeof(TFT_VCOM_V));      cs_both(HIGH);
}

static void panel_turn_on() {
    cs_both(LOW);  send_cmd(0x04);              cs_both(HIGH);  // POWER_ON
    wait_busy_idle();

    delay(50);
    cs_both(LOW);  send_cmd(0x12); send_data(0x00); cs_both(HIGH);  // DRF (refresh)
    wait_busy_idle();

    cs_both(LOW);  send_cmd(0x02); send_data(0x00); cs_both(HIGH);  // POWER_OFF
}

static void panel_sleep() {
    cs_both(LOW);
    send_cmd(0x07);
    send_data(0xA5);
    cs_both(HIGH);
}

// Six horizontal bars: BLACK, BLUE, GREEN, RED, YELLOW, WHITE — top to bottom.
// 4 bits/pixel packed 2-per-byte; each half-panel is 600 wide → 300 bytes/row.
static void draw_color_bars() {
    static const uint8_t bars[6] = { C_BLACK, C_BLUE, C_GREEN, C_RED, C_YELLOW, C_WHITE };
    constexpr uint16_t bytes_per_row = EPD_W / 4;  // 300 bytes per half
    constexpr uint16_t rows_per_bar  = EPD_H / 6;  // ~266 rows per bar

    for (int half = 0; half < 2; half++) {
        digitalWrite(half == 0 ? PIN_CS_M : PIN_CS_S, LOW);
        send_cmd(0x10);
        for (int k = 0; k < 6; k++) {
            uint8_t byte = (bars[k] << 4) | bars[k];
            for (uint16_t row = 0; row < rows_per_bar; row++) {
                for (uint16_t col = 0; col < bytes_per_row; col++) {
                    send_data(byte);
                }
            }
            delay(1);
        }
        cs_both(HIGH);
        Serial.printf("half %d: pushed\n", half);
    }
}

void setup() {
    Serial.begin(115200);
    delay(2000);
    Serial.println("\n13.3E6 first-pixels test");
    Serial.printf("Free heap: %u, PSRAM: %u\n",
                  ESP.getFreeHeap(),
                  (unsigned)ESP.getPsramSize());

    pinMode(PIN_PWR,   OUTPUT);
    pinMode(PIN_RST,   OUTPUT);
    pinMode(PIN_DC,    OUTPUT);
    pinMode(PIN_BUSY,  INPUT);
    pinMode(PIN_SCK,   OUTPUT);
    pinMode(PIN_MOSI,  OUTPUT);
    pinMode(PIN_CS_M,  OUTPUT);
    pinMode(PIN_CS_S,  OUTPUT);

    digitalWrite(PIN_PWR,  HIGH);
    digitalWrite(PIN_CS_M, HIGH);
    digitalWrite(PIN_CS_S, HIGH);
    digitalWrite(PIN_SCK,  LOW);
    delay(100);

    Serial.println("init");
    panel_init();
    Serial.println("draw color bars");
    draw_color_bars();
    Serial.println("refresh");
    panel_turn_on();
    Serial.println("sleep");
    panel_sleep();

    Serial.println("done");
}

void loop() {
    delay(60000);
}

#endif
