#include <Arduino.h>
#include "ESP_I2S.h"
#include "esp_check.h"
#include "esp_task_wdt.h"
#include "Wire.h"
#include "es8311.h"
#include <lvgl.h>
#include <Arduino_GFX_Library.h>
#include <Adafruit_MCP23X17.h>
#include <stdarg.h> 

static const char *TAG = "ES8311_EXAMPLE";

// I2C pin definitions
#define I2C_SDA 8
#define I2C_SCL 7

// I2S pin definitions (according to schematic W10-MB-V1_1)
#define I2S_MCK_PIN 1      // MCLK pin (GPIO1)
#define I2S_BCK_PIN 2      // BCLK/SCLK pin (GPIO2)
#define I2S_LRCK_PIN 4     // LRCLK pin (GPIO4)
#define I2S_DOUT_PIN 5     // Data output (playback, ESP32 -> ES8311, GPIO5)
#define I2S_DIN_PIN 3      // Data input (recording, ES8311 -> ESP32, GPIO3)

// Audio parameters
#define EXAMPLE_SAMPLE_RATE (44100)
#define EXAMPLE_MCLK_MULTIPLE (256)
#define EXAMPLE_MCLK_FREQ_HZ (EXAMPLE_SAMPLE_RATE * EXAMPLE_MCLK_MULTIPLE)
#define EXAMPLE_VOICE_VOLUME (70)  // Volume 0-100

// MCP23017 I2C address (for PA amplifier and LCD reset control)
// MCP23017 address: A2=0, A1=0, A0 depends on configuration (default 0x20, can be 0x21)
#define MCP23017_ADDR      0x20  // Default address, can be adjusted according to hardware configuration
#define PA_CTRL_PIN        7     // PA_CTRL corresponds to EXIO7 (MCP23017 GPA7, pin 7)
#define LCD_RST_PIN        1     // LCD reset corresponds to EXIO1 (MCP23017 GPA1, pin 1)

// Button pin
#define BUTTON_PIN 0  // GPIO0, using internal pull-up
// Display pin definitions
#define GFX_BL 6
#define SPI_MISO 11
#define SPI_MOSI 13
#define SPI_SCLK 12
#define LCD_CS 10
#define LCD_DC 16
#define LCD_RST 10
#define LCD_HOR_RES 240
#define LCD_VER_RES 240

// Global variables
I2SClass i2s;
es8311_handle_t g_es_handle = NULL;
uint8_t* g_wav_buffer = NULL;
size_t g_wav_size = 0;

// Display related global variables
Adafruit_MCP23X17 mcp;
uint8_t mcp23017_address = 0x20;  // MCP23017 address, default 0x20, can be adjusted according to hardware configuration
Arduino_DataBus *bus = new Arduino_ESP32SPI(LCD_DC, LCD_CS, SPI_SCLK, SPI_MOSI, SPI_MISO);
Arduino_GFX *gfx = new Arduino_ST7789(bus, LCD_RST, 0, true, LCD_HOR_RES, LCD_VER_RES);

uint32_t screenWidth;
uint32_t screenHeight;
uint32_t bufSize;
lv_disp_draw_buf_t draw_buf;
lv_color_t *disp_draw_buf1;
lv_color_t *disp_draw_buf2;
lv_disp_drv_t disp_drv;

// Serial output display related
lv_obj_t *textarea_serial = NULL;
String serial_output_buffer = "";
const size_t MAX_SERIAL_BUFFER_SIZE = 2048;  // Maximum buffer size (number of characters)
const size_t MAX_DISPLAY_LINES = 20;         // Maximum number of display lines

// LCD reset function
void lcd_reset(void) {
  if (mcp23017_address != 0x00) {
    // Use MCP23017 library functions
    mcp.digitalWrite(LCD_RST_PIN, HIGH);
    delay(10);
    mcp.digitalWrite(LCD_RST_PIN, LOW);
    delay(10);
    mcp.digitalWrite(LCD_RST_PIN, HIGH);
    delay(50);  // Reduced delay for faster screen clearing
  } else {
    // Fallback: Direct I2C operation
    // Configure GPA1 as output and execute reset sequence
    Wire.beginTransmission(MCP23017_ADDR);
    Wire.write(0x00);  // IODIRA register
    Wire.endTransmission();
    Wire.requestFrom(MCP23017_ADDR, (uint8_t)1);
    uint8_t iodira = Wire.read();
    iodira &= ~0x02;  // Clear bit 1 to make GPA1 an output
    Wire.beginTransmission(MCP23017_ADDR);
    Wire.write(0x00);
    Wire.write(iodira);
    Wire.endTransmission();
    delay(5);
    
    // Execute reset sequence
    Wire.beginTransmission(MCP23017_ADDR);
    Wire.write(0x14);  // OLATA register
    Wire.endTransmission();
    Wire.requestFrom(MCP23017_ADDR, (uint8_t)1);
    uint8_t olata = Wire.read();
    olata |= 0x02;  // Set bit 1 to HIGH
    Wire.beginTransmission(MCP23017_ADDR);
    Wire.write(0x14);
    Wire.write(olata);
    Wire.endTransmission();
    delay(10);
    
    olata &= ~0x02;  // Set bit 1 to LOW
    Wire.beginTransmission(MCP23017_ADDR);
    Wire.write(0x14);
    Wire.write(olata);
    Wire.endTransmission();
    delay(10);
    
    olata |= 0x02;  // Set bit 1 to HIGH
    Wire.beginTransmission(MCP23017_ADDR);
    Wire.write(0x14);
    Wire.write(olata);
    Wire.endTransmission();
    delay(50);
  }
}

// MCP23017 operation function: Enable PA amplifier (reference 15_comprehensiv_example_new.ino)
void initPAByMCP23017() {
  // MCP23017: Set GPA7 (pin 7) to HIGH to enable PA
  // If MCP23017 is initialized, use library functions; otherwise use direct I2C operations
  if (mcp23017_address != 0x00) {
    // Use MCP23017 library functions (more concise)
    mcp.pinMode(PA_CTRL_PIN, OUTPUT);
    mcp.digitalWrite(PA_CTRL_PIN, HIGH);
    delay(10);
  } else {
    // Fallback: Direct I2C operation (if library initialization fails)
    // According to ESP-IDF driver: OLATA=0x14 (output latch), IODIRA=0x00 (direction)
    // BANK=0 mode register map:
    //   IODIRA = 0x00 (direction: 0=output, 1=input)
    //   OLATA = 0x14 (output latch A, write output value)
    
    // Step 1: Configure GPA7 as output (set IODIRA bit 7 to 0)
    Wire.beginTransmission(MCP23017_ADDR);
    Wire.write(0x00);  // IODIRA register
    Wire.endTransmission();
    Wire.requestFrom(MCP23017_ADDR, (uint8_t)1);
    uint8_t iodira = Wire.read();
    
    // Clear bit 7 to make GPA7 an output (0=output, 1=input)
    iodira &= ~0x80;
    
    Wire.beginTransmission(MCP23017_ADDR);
    Wire.write(0x00);  // IODIRA register
    Wire.write(iodira);
    Wire.endTransmission();
    delay(5);
    
    // Step 2: Set GPA7 output to HIGH via OLATA register
    Wire.beginTransmission(MCP23017_ADDR);
    Wire.write(0x14);  // OLATA register
    Wire.endTransmission();
    Wire.requestFrom(MCP23017_ADDR, (uint8_t)1);
    uint8_t olata = Wire.read();
    
    // Set bit 7 (GPA7) to HIGH
    olata |= 0x80;
    
    // Write to OLATA register (output latch)
    Wire.beginTransmission(MCP23017_ADDR);
    Wire.write(0x14);  // OLATA register
    Wire.write(olata);
    Wire.endTransmission();
    delay(10);
  }
}

// Display refresh callback function
void my_disp_flush(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p) {
  uint32_t w = (area->x2 - area->x1 + 1);
  uint32_t h = (area->y2 - area->y1 + 1);

#if (LV_COLOR_16_SWAP != 0)
  gfx->draw16bitBeRGBBitmap(area->x1, area->y1, (uint16_t *)&color_p->full, w, h);
#else
  gfx->draw16bitRGBBitmap(area->x1, area->y1, (uint16_t *)&color_p->full, w, h);
#endif

  lv_disp_flush_ready(disp_drv);
}

// Add serial output to buffer
void addSerialOutput(const String& text) {
  serial_output_buffer += text;
  
  // Limit buffer size
  if (serial_output_buffer.length() > MAX_SERIAL_BUFFER_SIZE) {
    // Remove oldest lines, keep latest content
    int first_newline = serial_output_buffer.indexOf('\n');
    if (first_newline >= 0) {
      serial_output_buffer = serial_output_buffer.substring(first_newline + 1);
    } else {
      // If no newline, truncate directly
      serial_output_buffer = serial_output_buffer.substring(serial_output_buffer.length() - MAX_SERIAL_BUFFER_SIZE / 2);
    }
  }
  
  // Limit number of lines
  int line_count = 0;
  int pos = 0;
  while ((pos = serial_output_buffer.indexOf('\n', pos)) >= 0) {
    line_count++;
    pos++;
  }
  
  if (line_count > MAX_DISPLAY_LINES) {
    // Remove oldest lines
    int lines_to_remove = line_count - MAX_DISPLAY_LINES;
    int pos = 0;
    for (int i = 0; i < lines_to_remove; i++) {
      pos = serial_output_buffer.indexOf('\n', pos);
      if (pos >= 0) {
        pos++;
      } else {
        break;
      }
    }
    if (pos > 0) {
      serial_output_buffer = serial_output_buffer.substring(pos);
    }
  }
  
  // Update display
  if (textarea_serial != NULL) {
    lv_textarea_set_text(textarea_serial, serial_output_buffer.c_str());
    // Scroll to bottom
    lv_textarea_set_cursor_pos(textarea_serial, LV_TEXTAREA_CURSOR_LAST);
  }
}

// Custom Serial.print function wrapper
void SerialPrint(const String& text) {
  Serial.print(text);
  addSerialOutput(text);
}

void SerialPrintln(const String& text = "") {
  Serial.println(text);
  addSerialOutput(text + "\n");
}

void SerialPrintf(const char* format, ...) {
  char buffer[256];
  va_list args;
  va_start(args, format);
  vsnprintf(buffer, sizeof(buffer), format, args);
  va_end(args);
  Serial.print(buffer);
  addSerialOutput(String(buffer));
}

// Initialize LVGL display UI
void initSerialDisplayUI() {
  // Create title label
  lv_obj_t *label_title = lv_label_create(lv_scr_act());
  lv_label_set_text(label_title, "ES8311 Serial Monitor");
  lv_obj_set_style_text_font(label_title, &lv_font_montserrat_16, 0);
  lv_obj_set_style_text_color(label_title, lv_color_white(), 0);
  lv_obj_align(label_title, LV_ALIGN_TOP_MID, 0, 5);
  
  // Create text area for displaying serial output
  textarea_serial = lv_textarea_create(lv_scr_act());
  lv_obj_set_size(textarea_serial, LCD_HOR_RES - 10, LCD_VER_RES - 35);
  lv_obj_align(textarea_serial, LV_ALIGN_TOP_LEFT, 5, 25);
  
  // Set styles
  lv_obj_set_style_bg_color(textarea_serial, lv_color_hex(0x000000), 0);  // Black background
  lv_obj_set_style_text_color(textarea_serial, lv_color_hex(0x00FF00), 0); // Green text
  lv_obj_set_style_text_font(textarea_serial, &lv_font_montserrat_12, 0);
  lv_obj_set_style_border_color(textarea_serial, lv_color_hex(0x333333), 0);
  lv_obj_set_style_border_width(textarea_serial, 1, 0);
  
  // Set to read-only (LVGL 8.4.0 compatible method: remove clickable flag)
  lv_obj_clear_flag(textarea_serial, LV_OBJ_FLAG_CLICKABLE);
  
  // Initialize display
  lv_textarea_set_text(textarea_serial, "Serial Monitor Ready...\n");
  serial_output_buffer = "Serial Monitor Ready...\n";
}

// ES8311 initialization function (reference 15_lvgl_example_pro.ino)
static esp_err_t es8311_codec_init(void) {
  Serial.println("Initializing ES8311...");
  
  // Initialize PA first (reference 15_comprehensiv_example_new.ino)
  initPAByMCP23017();
  
  // Create ES8311 handle
  g_es_handle = es8311_create(I2C_NUM_0, ES8311_ADDRESS_0);
  if (g_es_handle == NULL) {
    Serial.println("ERROR: es8311_create failed");
    return ESP_FAIL;
  }

  // Configure clock parameters
  const es8311_clock_config_t es_clk = {
    .mclk_inverted = false,
    .sclk_inverted = false,
    .mclk_from_mclk_pin = true,  // MCLK input from MCLK pin
    .mclk_frequency = EXAMPLE_MCLK_FREQ_HZ,
    .sample_frequency = EXAMPLE_SAMPLE_RATE
  };

  // Initialize ES8311
  esp_err_t ret = es8311_init(g_es_handle, &es_clk, ES8311_RESOLUTION_16, ES8311_RESOLUTION_16);
  if (ret != ESP_OK) {
    Serial.printf("ERROR: es8311_init failed: %s\n", esp_err_to_name(ret));
    return ret;
  }

  // Configure microphone (analog microphone, not digital microphone)
  ret = es8311_microphone_config(g_es_handle, false);
  if (ret != ESP_OK) {
    Serial.printf("WARNING: es8311_microphone_config failed: %s\n", esp_err_to_name(ret));
  }

  // Set volume (reference 15_lvgl_example_pro.ino, use 70)
  ret = es8311_voice_volume_set(g_es_handle, EXAMPLE_VOICE_VOLUME, NULL);
  if (ret != ESP_OK) {
    Serial.printf("WARNING: es8311_voice_volume_set failed: %s\n", esp_err_to_name(ret));
  }

  // Ensure DAC is not muted
  ret = es8311_voice_mute(g_es_handle, false);
  if (ret != ESP_OK) {
    Serial.printf("WARNING: es8311_voice_mute failed: %s\n", esp_err_to_name(ret));
  }

  // Configure output routing to LINE OUT (for PA amplifier)
  // Note: es8311_init() defaults REG13 to 0x10 (HP output), we need to change it to 0x00 (LINE OUT)
  // Direct register write to configure LINE OUT (reference 15_lvgl_example_pro.ino)
  Wire.beginTransmission(ES8311_ADDRESS_0);
  Wire.write(0x13);  // SYSTEM_REG13
  Wire.write(0x00);  // LINE OUT (HP disabled, LINE OUT enabled)
  uint8_t wire_result = Wire.endTransmission();
  if (wire_result != 0) {
    Serial.printf("WARNING: LINE OUT config failed, Wire.endTransmission() returned %d\n", wire_result);
  }
  delay(10);

  Serial.println("ES8311 initialized successfully");

  return ESP_OK;
}

// I2S initialization function
bool setupI2S() {
  // Set I2S pins
  i2s.setPins(I2S_BCK_PIN, I2S_LRCK_PIN, I2S_DOUT_PIN, I2S_DIN_PIN, I2S_MCK_PIN);
  
  // Initialize I2S bus (standard mode, stereo, 16-bit)
  if (!i2s.begin(I2S_MODE_STD, EXAMPLE_SAMPLE_RATE, I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO, I2S_STD_SLOT_BOTH)) {
    Serial.println("ERROR: Failed to initialize I2S bus!");
    return false;
  }

  Serial.println("I2S initialized successfully");

  return true;
}

// Perform recording and playback
void performRecordingAndPlayback() {
  SerialPrintln("\n[Recording] Starting recording (5 seconds)...");

  // Feed watchdog
  esp_task_wdt_reset();

  // Clear previous buffer
  if (g_wav_buffer != NULL) {
    free(g_wav_buffer);
    g_wav_buffer = NULL;
  }

  // Execute recording
  unsigned long record_start = millis();
  g_wav_buffer = i2s.recordWAV(5, &g_wav_size);
  unsigned long record_end = millis();

  // Check recording result
  if (g_wav_buffer == NULL || g_wav_size == 0) {
    SerialPrintln("[Error] Recording failed!");
    return;
  }

  SerialPrintf("[Recording] Complete: %d bytes (%lu ms)\n", g_wav_size, record_end - record_start);

  // Feed watchdog
  esp_task_wdt_reset();
  delay(500);

  // Configuration before playback (reference 15_lvgl_example_pro.ino, ensure all configurations are correct)
  if (g_es_handle != NULL) {
    SerialPrintln("[Playback] Configuring ES8311 for playback...");
    
    // Ensure DAC is not muted
    esp_err_t ret = es8311_voice_mute(g_es_handle, false);
    if (ret != ESP_OK) {
      SerialPrintf("[Warning] es8311_voice_mute failed: %s\n", esp_err_to_name(ret));
    } else {
      SerialPrintln("[Playback] DAC unmuted");
    }
    
    // Ensure volume is correct (reference 15_lvgl_example_pro.ino, use 70)
    int current_volume = 0;
    es8311_voice_volume_get(g_es_handle, &current_volume);
    SerialPrintf("[Playback] Current volume: %d (target: %d)\n", current_volume, EXAMPLE_VOICE_VOLUME);
    if (current_volume != EXAMPLE_VOICE_VOLUME) {
      ret = es8311_voice_volume_set(g_es_handle, EXAMPLE_VOICE_VOLUME, NULL);
      if (ret != ESP_OK) {
        SerialPrintf("[Warning] es8311_voice_volume_set failed: %s\n", esp_err_to_name(ret));
      } else {
        SerialPrintf("[Playback] Volume set to %d\n", EXAMPLE_VOICE_VOLUME);
      }
    }

    // Ensure output routing is LINE OUT (direct register write)
    Wire.beginTransmission(ES8311_ADDRESS_0);
    Wire.write(0x13);  // SYSTEM_REG13
    Wire.write(0x00);  // LINE OUT
    uint8_t wire_result = Wire.endTransmission();
    if (wire_result != 0) {
      SerialPrintf("[Warning] LINE OUT config failed, Wire.endTransmission() returned %d\n", wire_result);
    } else {
      SerialPrintln("[Playback] LINE OUT configured");
    }
    delay(10);
    
    // Confirm PA is enabled again (important: ensure PA amplifier is enabled)
    initPAByMCP23017();
    SerialPrintln("[Playback] PA enabled");
    
    delay(50);  // Give configuration some time to take effect
  } else {
    SerialPrintln("[Error] ES8311 handle is NULL, cannot configure for playback!");
    return;
  }

  // Play audio
  esp_task_wdt_reset();  // Feed watchdog before playback
  
  SerialPrintln("[Playback] Starting playback...");
  unsigned long playback_start = millis();
  i2s.playWAV(g_wav_buffer, g_wav_size);
  unsigned long playback_end = millis();
  
  // Feed watchdog immediately after playback
  esp_task_wdt_reset();

  SerialPrintf("[Playback] Complete (%lu ms)\n", playback_end - playback_start);
}

void setup() {
  Serial.begin(115200);
  delay(1000);
  
  // Output directly to serial first (display system not initialized yet)
  Serial.println("\n=== ES8311 Recording and Playback Example ===\n");

  // Configure watchdog (to prevent reboot during long operations)
  esp_err_t wdt_ret = esp_task_wdt_add(NULL);
  if (wdt_ret == ESP_OK) {
    esp_task_wdt_config_t wdt_config = {
      .timeout_ms = 60000,  // 60 second timeout
      .idle_core_mask = 0,
      .trigger_panic = false
    };
    esp_task_wdt_init(&wdt_config);
  }

  // Initialize I2C
  Wire.begin(I2C_SDA, I2C_SCL);
  Wire.setClock(100000);  // Use 100kHz for scanning (more reliable than 400kHz)
  Wire.setTimeout(1000000);
  delay(100);

  // Initialize MCP23017 (for LCD reset and PA control)
  // Auto-detect MCP23017 address (could be 0x20 or 0x21)
  Serial.println("Detecting MCP23017 I2C address...");
  bool mcp_found = false;
  
  // Try to detect MCP23017 address (0x20 or 0x21)
  for (uint8_t addr = 0x20; addr <= 0x21; addr++) {
    Wire.beginTransmission(addr);
    uint8_t error = Wire.endTransmission();
    if (error == 0) {
      // Try to read register to verify it's MCP23017
      Wire.beginTransmission(addr);
      Wire.write(0x12);  // GPIOA register
      if (Wire.endTransmission() == 0) {
        uint8_t bytes = Wire.requestFrom(addr, (uint8_t)1);
        if (bytes == 1) {
          uint8_t value = Wire.read();
          Serial.printf("Found MCP23017 at address 0x%02X\n", addr);
          mcp23017_address = addr;
          mcp_found = true;
          break;
        }
      }
    }
  }
  
  if (!mcp_found) {
    Serial.println("WARNING: MCP23017 not found! Will use direct I2C access");
    mcp23017_address = 0x00;  // Set to 0x00 to indicate not found, will use direct I2C operations
  }
  
  // Initialize MCP23017
  if (mcp_found) {
    if (!mcp.begin_I2C(mcp23017_address)) {
      Serial.println("ERROR: MCP23017 initialization failed! Will use direct I2C access");
      mcp23017_address = 0x00;  // Initialization failed, use direct I2C operations
    } else {
      Serial.printf("MCP23017 initialized at address 0x%02X\n", mcp23017_address);
      
      // Configure LCD reset pin as output
      mcp.pinMode(LCD_RST_PIN, OUTPUT);
      
      // Configure PA control pin as output
      mcp.pinMode(PA_CTRL_PIN, OUTPUT);
    }
  }
  
  // Initialize PA (via MCP23017) - will also be called during ES8311 initialization, initialize once here to ensure PA is enabled
  initPAByMCP23017();
  
  // Initialize LCD
  lcd_reset();
  
  // Initialize display
  if (!gfx->begin()) {
    SerialPrintln("[Error] Display initialization failed!");
  } else {
    gfx->fillScreen(RGB565_BLACK);
    
#ifdef GFX_BL
    pinMode(GFX_BL, OUTPUT);
    digitalWrite(GFX_BL, HIGH);
#endif
    
    // Initialize LVGL
    lv_init();
    screenWidth = gfx->width();
    screenHeight = gfx->height();
    bufSize = screenWidth * 40;
    
    // Allocate display buffers
    disp_draw_buf1 = (lv_color_t *)heap_caps_malloc(bufSize * 2, MALLOC_CAP_DEFAULT | MALLOC_CAP_8BIT);
    disp_draw_buf2 = (lv_color_t *)heap_caps_malloc(bufSize * 2, MALLOC_CAP_DEFAULT | MALLOC_CAP_8BIT);
    
    if (disp_draw_buf1 && disp_draw_buf2) {
      lv_disp_draw_buf_init(&draw_buf, disp_draw_buf1, disp_draw_buf2, bufSize);
      
      // Initialize display driver
      lv_disp_drv_init(&disp_drv);
      disp_drv.hor_res = screenWidth;
      disp_drv.ver_res = screenHeight;
      disp_drv.flush_cb = my_disp_flush;
      disp_drv.draw_buf = &draw_buf;
      lv_disp_drv_register(&disp_drv);
      
      // Initialize serial display UI
      initSerialDisplayUI();
      
      // Add previously output content to display buffer
      addSerialOutput("\n=== ES8311 Recording and Playback Example ===\n");
      addSerialOutput("Display initialized successfully\n");
    } else {
      Serial.println("[Warning] Failed to allocate LVGL display buffers!");
    }
  }
  
  // Initialize ES8311 (Note: These functions still use Serial internally, as they may be called before the display system is ready)
  esp_err_t ret = es8311_codec_init();
  if (ret != ESP_OK) {
    char err_msg[128];
    snprintf(err_msg, sizeof(err_msg), "[Error] ES8311 initialization failed: %s\n", esp_err_to_name(ret));
    Serial.print(err_msg);
    addSerialOutput(String(err_msg));
    while(1) {
      delay(1000);
      esp_task_wdt_reset();
      if (textarea_serial != NULL) {
        lv_timer_handler();
      }
    }
  }

  // Initialize I2S
  if (!setupI2S()) {
    Serial.println("[Error] I2S initialization failed!");
    addSerialOutput("[Error] I2S initialization failed!\n");
    while(1) {
      delay(1000);
      esp_task_wdt_reset();
      if (textarea_serial != NULL) {
        lv_timer_handler();
      }
    }
  }

  // Initialize button
  pinMode(BUTTON_PIN, INPUT_PULLUP);

  SerialPrintln("System ready - Press button to start recording (5 seconds)\n");

  esp_task_wdt_reset();
}

void loop() {
  // Handle LVGL tasks
  if (textarea_serial != NULL) {
    lv_timer_handler();
  }
  
  static bool button_pressed = false;
  static unsigned long last_debounce = 0;
  
  // Button debounce handling
  int button_state = digitalRead(BUTTON_PIN);
  unsigned long now = millis();

  // Detect button press (LOW level)
  if (button_state == LOW && !button_pressed) {
    if (now - last_debounce > 50) {  // 50ms debounce
      button_pressed = true;
      last_debounce = now;
      
      SerialPrintln("[Button] Recording will start in 3 seconds...");
      
      // Countdown
      for (int i = 3; i > 0; i--) {
        SerialPrintf("Countdown: %d...\n", i);
        delay(1000);
        esp_task_wdt_reset();
        if (textarea_serial != NULL) {
          lv_timer_handler();
        }
      }
      
      // Execute recording and playback
      performRecordingAndPlayback();
      
      SerialPrintln("Ready - Press button again to record again\n");
    }
  }
  
  // Detect button release
  if (button_state == HIGH && button_pressed) {
    button_pressed = false;
  }

  // Feed watchdog periodically
  esp_task_wdt_reset();
  delay(5);  // Reduce delay for smoother LVGL operation
}
