#include "my_buzzer.h"
#include <FS.h>
#include <LittleFS.h>

#include <anyrtttl.h>
#include <binrtttl.h>
#include <pitches.h>

#include "JsonConstrain.h"

const char* DEFAULT_MELODY = "Ack:d=16,o=5,b=200:c,e,g";

// serial debugging enabled
//#define ANY_RTTTL_INFO

static const char* tag = "buzzer";

BUZZ_TUNE buzzTune[TUNE_MAX_COUNT];
int8_t buzzPin;


void Init_Buzzer(int8_t pin, const char* configFile)
{
  buzzPin = pin;
  if(buzzPin >= 0){
    pinMode(buzzPin, OUTPUT);
  }

  Buzzer_Load_Tunes(configFile); // Load Tunes
  ESP_LOGI(tag, "Buzzer initialized..");
}

void Buzzer_Play_Tune(TUNE_TYPE tune, bool async, bool hasPriority)
{
  static int prev_tune = -1;
  if (buzzPin < 0) return;

  // Range / data validation
  if (tune < 0 || tune >= TUNE_MAX_COUNT) {
    ESP_LOGW(tag, "Invalid tune index: %d", (int)tune);
    return;
  }
  const String &melody = buzzTune[tune].melody;
  if (melody.isEmpty()) {
    ESP_LOGW(tag, "Empty melody for tune %d", (int)tune);
    return;
  }

  // Async mode: begin once, then caller should periodically call again to advance playback
  if (async) {
    bool playing = anyrtttl::nonblocking::isPlaying();
    if (hasPriority && playing) {
      anyrtttl::nonblocking::stop();
      playing = false;
    }
    if (!playing || prev_tune != tune) {
      // (Re)start tune
      anyrtttl::nonblocking::begin(buzzPin, melody.c_str());
      prev_tune = tune;
      ESP_LOGD(tag, "Started async tune %d (%s)", (int)tune, melody.c_str());
    }
    // Advance playback one tick
    anyrtttl::nonblocking::play();
    return;
  }

  // Blocking mode: play full tune cycles with optional pause
  ESP_LOGD(tag, "Playing blocking tune %d cycles=%d pause=%d", (int)tune, buzzTune[tune].cycles, buzzTune[tune].pause);
  for (int c = 0; c < buzzTune[tune].cycles; ++c) {
    anyrtttl::blocking::play(buzzPin, melody.c_str());
    if (buzzTune[tune].pause > 0 && c + 1 < buzzTune[tune].cycles) {
      delay(buzzTune[tune].pause); // simple pause between cycles
    }
    yield(); // allow other tasks to run
  }
  prev_tune = tune;
}

// TODO Buzzer Beep finish
void Buzzer_Beep(int mSecs, int freq)
{
  /*
  ledcAttachPin(buzzPin, buzzerCh);
  ledcSetup(buzzerCh, 2000, 8);
  ledcWrite(buzzerCh, 125);
  vTaskDelay(mSecs);
  ledcWrite(buzzerCh, 0);
  */
}

// TODO Reduce tunes to load ()
void Buzzer_Load_Tunes(const char* tunesPath){  
  File file = LittleFS.open(tunesPath);

  if (!file) {
      ESP_LOGE(tag, "Error opening %s...", tunesPath);
      return;
  }

  JsonDocument doc;
  DeserializationError error = deserializeJson(doc, file);
  file.close();

  if(error){ 
    ESP_LOGE(tag, "%s deserialize error!..", tunesPath); 
    return;
  }

  JsonArray tuneJsonArray = doc["tunes"];
  if(!tuneJsonArray.isNull()){
    int tuneIndex = 0;
    for(JsonObject obj : tuneJsonArray){
      if(tuneIndex >= TUNE_MAX_COUNT) break;
      buzzTune[tuneIndex].cycles = jsonConstrain<int>(tag, obj, "cycles", 1, 100, 1);
      buzzTune[tuneIndex].pause  = jsonConstrain<int>(tag, obj, "pause", 0, 100, 0);
      buzzTune[tuneIndex].melody =  jsonConstrainString(tag, obj, "tune", DEFAULT_MELODY);
      ESP_LOGD(tag, "tune %d : %s", tuneIndex, buzzTune[tuneIndex].melody.c_str());
      tuneIndex++;
    }
    ESP_LOGI(tag, "Loaded tunes...");
  }else{
    ESP_LOGE(tag, "Error!, %s key: tunes not found..", tunesPath); 
  }
}