//rekam dan mainkan + TTS bisa, gabungkan Deepgram dan Chatbot
#include <driver/i2s.h>
#include <WiFi.h>
#include <HTTPClient.h>
#include <WiFiClientSecure.h>
#include <AudioFileSourceICYStream.h>
#include <AudioGeneratorMP3.h>
#include <AudioOutputI2S.h>
#include <AudioFileSourceHTTPStream.h>
#include <AudioFileSourceBuffer.h>
#include <ArduinoJson.h>

#define I2S_NUM         I2S_NUM_0
#define SAMPLE_RATE     16000
#define SAMPLE_BITS     16
#define CHANNELS        1
#define RECORD_SECONDS  2 // 2 detik
#define BUFFER_SIZE (SAMPLE_RATE * RECORD_SECONDS * CHANNELS * 2) // 2 byte
const int WAV_HEADER_SIZE = 44;
uint8_t audio_buffer[BUFFER_SIZE];

#define BUTTON_PIN      27
#define WIFI_SSID        "*"
#define WIFI_PASSWORD    "12345678"
String urlChatbot = "http://david.si2024.com/Program-O/chatbot/ajax.php?say=";
const char* DEEPGRAM_API_KEY = "6f5e99548aa38d876fa84750aad55d7a340d70e4";
#define STT_LANGUAGE      "id-ID"
#define TIMEOUT_DEEPGRAM  12 

String textDeepgram = "";
String textChatbot = "";

AudioGeneratorMP3 *mp3;
AudioFileSourceHTTPStream *file;
AudioOutputI2S *out;
AudioFileSourceBuffer *buff;

i2s_pin_config_t i2s_mic_pins = {
  .bck_io_num = 33,
  .ws_io_num = 32,
  .data_out_num = I2S_PIN_NO_CHANGE,
  .data_in_num = 21
};

i2s_pin_config_t i2s_spk_pins = {
  .bck_io_num = 26,
  .ws_io_num = 25,
  .data_out_num = 22,
  .data_in_num = I2S_PIN_NO_CHANGE
};

i2s_config_t i2s_mic_config = {
  .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX),
  .sample_rate = SAMPLE_RATE,
  .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
  .channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
  .communication_format = I2S_COMM_FORMAT_I2S,
  .intr_alloc_flags = 0,
  .dma_buf_count = 8,
  .dma_buf_len = 64,
  .use_apll = false,
  .tx_desc_auto_clear = false,
  .fixed_mclk = 0
};

i2s_config_t i2s_spk_config = {
  .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX),
  .sample_rate = SAMPLE_RATE,
  .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
  .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
  .communication_format = I2S_COMM_FORMAT_I2S,
  .intr_alloc_flags = 0,
  .dma_buf_count = 8,
  .dma_buf_len = 64,
  .use_apll = false,
  .tx_desc_auto_clear = true,
  .fixed_mclk = 0
};

// Wi-Fi connection setup
void connectToWiFi() {
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.print(".");
  }
  Serial.println("\nConnected to WiFi!");
}

void switch_to_mic() {
  i2s_driver_uninstall(I2S_NUM);
  i2s_driver_install(I2S_NUM, &i2s_mic_config, 0, NULL);
  i2s_set_pin(I2S_NUM, &i2s_mic_pins);
}

void switch_to_speaker() {
  i2s_driver_uninstall(I2S_NUM);
  i2s_driver_install(I2S_NUM, &i2s_spk_config, 0, NULL);
  i2s_set_pin(I2S_NUM, &i2s_spk_pins);
}

// URL encode function for the text query
String urlencode(String str) {
  String encoded = "";
  char c;
  for (int i = 0; i < str.length(); i++) {
    c = str.charAt(i);
    if (c == ' ') {
      encoded += "+";
    } else if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') {
      encoded += c;
    } else {
      encoded += "%" + String(c, HEX);
    }
  }
  return encoded;
}

void writeWavHeader(uint8_t* buffer, uint32_t dataLength) {
  const int sampleRate = SAMPLE_RATE;
  const int bitsPerSample = SAMPLE_BITS;
  const int channels = CHANNELS;
  uint32_t byteRate = sampleRate * channels * bitsPerSample / 8;
  uint32_t blockAlign = channels * bitsPerSample / 8;
  uint32_t totalDataLen = dataLength + 36; // 36 + Subchunk2Size

  // RIFF header
  memcpy(buffer, "RIFF", 4);
  buffer[4] = totalDataLen & 0xff;
  buffer[5] = (totalDataLen >> 8) & 0xff;
  buffer[6] = (totalDataLen >> 16) & 0xff;
  buffer[7] = (totalDataLen >> 24) & 0xff;
  memcpy(buffer + 8, "WAVE", 4);

  // fmt chunk
  memcpy(buffer + 12, "fmt ", 4);
  buffer[16] = 16; buffer[17] = 0; buffer[18] = 0; buffer[19] = 0;   // Subchunk1Size
  buffer[20] = 1; buffer[21] = 0;   // AudioFormat = 1 (PCM)
  buffer[22] = channels; buffer[23] = 0;
  buffer[24] = sampleRate & 0xff;
  buffer[25] = (sampleRate >> 8) & 0xff;
  buffer[26] = (sampleRate >> 16) & 0xff;
  buffer[27] = (sampleRate >> 24) & 0xff;
  buffer[28] = byteRate & 0xff;
  buffer[29] = (byteRate >> 8) & 0xff;
  buffer[30] = (byteRate >> 16) & 0xff;
  buffer[31] = (byteRate >> 24) & 0xff;
  buffer[32] = blockAlign;
  buffer[33] = 0;
  buffer[34] = bitsPerSample;
  buffer[35] = 0;

  // data chunk
  memcpy(buffer + 36, "data", 4);
  buffer[40] = dataLength & 0xff;
  buffer[41] = (dataLength >> 8) & 0xff;
  buffer[42] = (dataLength >> 16) & 0xff;
  buffer[43] = (dataLength >> 24) & 0xff;
}

String speechToTextWithDeepgram(uint8_t* wavData, int length) {
  Serial.println("🔁 Mengirim ke Deepgram...");

  String response = "";
  StaticJsonDocument<2048> doc;
  String transcript = "";  // Gunakan String
  
  WiFiClientSecure client;
  client.setInsecure(); // ⚠️ Gunakan dengan hati-hati di produksi

  HTTPClient https;
  https.begin(client, "https://api.deepgram.com/v1/listen?language=id&model=nova-2&punctuate=true");
  https.addHeader("Authorization", "Token " + String(DEEPGRAM_API_KEY));
  https.addHeader("Content-Type", "audio/wav");

  int httpResponseCode = https.POST(wavData, length);
  if (httpResponseCode > 0) {
    response = https.getString();
    Serial.print("🗣️ Response:");
    Serial.println(response);
  } else {
    Serial.printf("❌ Gagal: %s\n", https.errorToString(httpResponseCode).c_str());
  }

  https.end();
  
  DeserializationError error = deserializeJson(doc, response);  
  if (!error) {
    transcript = doc["results"]["channels"][0]["alternatives"][0]["transcript"].as<String>();
    Serial.println("📝 Hasil Transkripsi:");
    Serial.println(transcript);  
  } else {
    Serial.print("❌ JSON Error: ");
    Serial.println(error.c_str());
  }     
  
  return transcript; 
}

String textToChatbot(String transcript) {
  String payload = "";
  String encodedTranscript = "";
  String url = "";
 
  if (transcript!="") {
    encodedTranscript = urlencode(transcript);
    url = urlChatbot + encodedTranscript;
    HTTPClient http;
    http.begin(url);  // Memulai koneksi HTTP
    int httpCode = http.GET(); // Kirim GET request
  
    if (httpCode > 0) {
      payload = http.getString();
      Serial.println("🤖 Respon dari server:");
      Serial.println(payload);         
    } else {
      Serial.println("❌ Gagal melakukan HTTP request ke Chatbot");
    }
  
    http.end(); // Menutup koneksi        
  }  
  return payload;
}

// Function to fetch TTS audio from Google Translate
void playTTS(String text) {
  String url = "http://translate.google.com/translate_tts?ie=UTF-8&q=" + urlencode(text) + "&tl=id&client=tw-ob";

  // Hapus instansi sebelumnya jika ada
  if (mp3) {
    delete mp3;
    mp3 = nullptr;
  }
  if (file) {
    delete file;
    file = nullptr;
  }
  if (out) {
    delete out;
    out = nullptr;
  }
  if (buff) {
    delete buff;
    buff = nullptr;
  }  

  // Buat stream dan output
  file = new AudioFileSourceHTTPStream(url.c_str());
  buff = new AudioFileSourceBuffer(file, 2048);
  out = new AudioOutputI2S();
  out->begin();

  mp3 = new AudioGeneratorMP3();
  mp3->begin(buff, out);

  // Estimasi durasi: 60 ms per karakter (bisa disesuaikan)
  int estimatedMs = text.length() * 60;
  unsigned long start = millis();
  unsigned long timeout = start + estimatedMs + 800;
  
  while (millis() < timeout) {         
    if (!mp3->loop()) {
      Serial.println("Loop selesai. Playback selesai...");
      break;
    }
  }
  
  if (mp3->isRunning()) {
    Serial.println("Timeout. Memaksa stop...");
    mp3->stop();  // Paksa stop
  }

  Serial.println("Selesai playTTS...");
}

void setup() {
  Serial.begin(115200);
  pinMode(BUTTON_PIN, INPUT_PULLUP); // tombol aktif LOW
  connectToWiFi();
  switch_to_speaker();
  playTTS("Hai, Sovi Lau di sini. Apa yang bisa saya bantu?");
  
}

void loop() {
  static bool wasRecording = false;
  if (digitalRead(BUTTON_PIN) == LOW) {   
    if (!wasRecording) {
      Serial.println("Tombol ditekan. Mulai merekam...");
      switch_to_mic();
      delay(100);
      size_t bytes_read;
      i2s_read(I2S_NUM, audio_buffer, BUFFER_SIZE, &bytes_read, portMAX_DELAY);
      Serial.println("Rekaman selesai.");
      writeWavHeader(audio_buffer, BUFFER_SIZE);
      textDeepgram = speechToTextWithDeepgram(audio_buffer, WAV_HEADER_SIZE + BUFFER_SIZE);
      wasRecording = true;
    }
  } else { 
    if (wasRecording) {
      Serial.println("Tombol dilepas. Mainkan kembali...");
      switch_to_speaker();
      delay(100);
      size_t bytes_written;
      //memainkan audio dengan i2s internal
      //i2s_write(I2S_NUM, audio_buffer, BUFFER_SIZE, &bytes_written, portMAX_DELAY); 
      Serial.println("Menjalankan Google TTS...");
      i2s_driver_uninstall(I2S_NUM);
      if(textDeepgram) {
        textChatbot = textToChatbot(textDeepgram); 
      } else {
        textChatbot = "Maaf, apakah bisa diulang lagi?";
      }
      playTTS(textChatbot);     
      switch_to_mic();
      wasRecording = false;
    }
  }

  delay(50);
}