#include "my_buzzer.h"
#include <FS.h>
#include <LittleFS.h>
#include <melody_player.h>
#include <melody_factory.h>
#include "my_board.h"
#include "global.h"
#include "JsonConstrain.h"
#include "global.h"

//#define buzzPin Buzzer_Pin

static const char* tag = "buzzer";

MelodyPlayer* player;

BUZZ_TUNE buzzTune[12];
uint8_t buzzPin;

void Init_Buzzer(uint8_t pin)
{
  File file = LittleFS.open("/cfg/buzzer.json");
  if(!file){
    file.close();
    ESP_LOGE(tag, "Error opening buzzer.json...");
  }else{
    JsonDocument doc;
    DeserializationError error = deserializeJson(doc, file);
    file.close();
    if(error){ ESP_LOGE(tag, "buzzer.json deserialize error!..");  return; }

    if(jsonConstrainBool(tag, doc.as<JsonObject>(), "en", false)){
      //player = new MelodyPlayer(buzzPin, buzzerCh, false);
      int freeChannel = findUnusedLedcChannel();
      freeChannel = 1;
      player = new MelodyPlayer(buzzPin, freeChannel, false);
      
      if(player){
        Buzzer_Load_Tunes(doc.as<JsonObject>()); // Load Tunes
        ESP_LOGD(tag, "Buzzer initialized.. using Ch:%d", freeChannel);
      }
      else{
        ESP_LOGE(tag, "Buzzer initialization failed..");
      }

    }
  }
}

void Buzzer_Play_Tune(TUNE_TYPE tune, bool async, bool hasPriority)
{
  static int prev_tune = -1;
  static Melody melody;

  //melody = MelodyFactory.loadRtttlString( buzzTune[tune].melody.c_str() );
  //player->playAsync(melody);
  

  if(!player) { 
    ESP_LOGV(tag, "no buzzer"); 
    return; 
  }

  //if(!player->isPlaying() || (player->isPlaying() && hasPriority)){
  //  ESP_LOGD(tag, "Playing tune: %d, melody: %s", tune, buzzTune[tune].melody.c_str());

  //  if(prev_tune == tune){
  //    ESP_LOGD(tag, "Same tune: %d, melody: %s", tune, buzzTune[tune].melody.c_str());
  //    for(int c = 0; c < buzzTune[tune].cycles; c++){
  //      ( async ) ? player->playAsync() : player->play();
  //    } 
  //  }
  //  else{
      melody = MelodyFactory.loadRtttlString( buzzTune[tune].melody.c_str() );
      prev_tune = tune;
      ESP_LOGD(tag, "New tune: %d, melody: %s", tune, buzzTune[tune].melody.c_str());

      for(int c = 0; c < buzzTune[tune].cycles; c++){
        ( async ) ? player->playAsync(melody) : player->play(melody);
        
      }
  //  }
  //}
  //else{
  //  ESP_LOGD(tag, "buzzer busy");
  //}
  
}

void Buzzer_Beep(int mSecs, int freq)
{
  ledcAttachPin(buzzPin, buzzerCh);
  ledcSetup(buzzerCh, 2000, 8);
  ledcWrite(buzzerCh, 125);
  vTaskDelay(mSecs);
  ledcWrite(buzzerCh, 0);
}

void Buzzer_Load_Tunes(const JsonObject &doc)
{  
  const char* tuneName[] PROGMEM = {"boot", "error", "success", "click", "beep",
                                  "wifi-conn", "wifi-disc", "ble-conn", "ble-disc",
                                  "download", "waiting", "restart", "test", "ack", nullptr};

  int listCount = 0;
  while (true){
    if(tuneName[listCount] == nullptr){ break; }
    listCount++;
  }
  
  JsonObject js[listCount]; // JsonObject leaks in a loop so create separate objects

  listCount = 3;

  for(int i = 0; i < listCount; i++){
    js[i] = doc[tuneName[i]];
    buzzTune[i].cycles =  jsonConstrain<int>(tag, js[i], "cycles", 1, 100, 1);
    buzzTune[i].pause =   jsonConstrain<int>(tag, js[i], "pause", 0, 100, 0);
    buzzTune[i].melody =  jsonConstrainString(tag, js[i], "tune", "Ack:d=16,o=5,b=112:b,b#").c_str();
    ESP_LOGD(tag, "tune %d : %s", i, buzzTune[i].melody.c_str());
  }
  ESP_LOGV(tag, "loaded %d tunes", listCount);
}