#if defined(ESP8266)
#include <ESP8266WiFi.h> //https://github.com/esp8266/Arduino
#else
#include <WiFi.h>
#endif

#include <ESPAsyncWebServer.h>   //Local WebServer used to serve the configuration portal
#include <ESPAsyncWiFiManager.h> //https://github.com/tzapu/WiFiManager WiFi Configuration Magic
#include <ArduinoJson.h>

#include "Audio.h"

#include <SPI.h>
#include <SD.h>
#include <MFRC522.h> //RFID Reader
#include <map>

// define pins for RFID
#define CS_RFID 32 // SIC, tried 4 and 32 but only this worked!
#define RST_RFID 33
#define IRQ_RFID 34

// Audio DAC
#define I2S_DOUT 26 // connect to DAC pin DIN
#define I2S_BCLK 27 // connect to DAC pin BCK
#define I2S_LRC 25  // connect to DAC pin LCK

#define BTN_START_STOP 4 // Button on XX and GND
#define BTN_NEXT 17
#define BTN_PREV 16

#define CS_SDCARD 22

#define BAT_VOLTAGE_PIN 35

#define RFID_LOOP_INTERVAL 25

#define VOLTAGE_LOOP_INTERVAL 5000

#define VOLTAGE_THRESHOLD 0

#define SHORT_PRESS_TIME 250

#define LONG_PRESS_TIME 1000

#define MAX_VOL 15

#include "globals.h"
#include "WebContent.h"
#include "css.h"
#include "DirectoryNode.h"

#define SOUND_STARTUP "start.mp3"
#define SOUND_SLEEP "sleep.mp3"




File root;
File mp3File;

Audio audio;

uint volume = 7;

AsyncWebServer server(80);
DNSServer dns;

//static variable has to be instantiated outside of class definition:
uint16_t DirectoryNode::idCounter = 0;

DirectoryNode rootNode("/");
DirectoryNode *currentNode = nullptr;



volatile bool newRfidInt = false;

MFRC522 rfid(CS_RFID, RST_RFID); // instatiate a MFRC522 reader object.

TaskHandle_t RfidTask;

bool asyncStop = false;

bool asyncStart = false;

bool asyncTogglePlayPause = false;

bool asyncNext = false;

bool asyncPrev = false;

bool SDActive = false;

bool RFIDActive = false;

uint16_t voltage_threshold_counter = 0;

size_t free_heap = 0;

/*
std::map<String, String> rfid_map{{"67 152 204 14", "01-The_Box_Tops-The_Letter.mp3"},
                                  {"67 175 148 160", "068-Der_Schatz_im_Bergsee"}};

*/                                  

void activateSD()
{
  if (SDActive)
    return;

  if (!SD.begin(CS_SDCARD))
  {
    Serial.println("SD initialization failed!");
  }
  SDActive = true;
}

void deactivateSD()
{
  if (SDActive) {
    digitalWrite(CS_SDCARD, HIGH);
    SDActive = false;
  }
}

void activateRFID()
{
  SPI.begin(-1, -1, -1, CS_RFID);
  rfid.PCD_Init(CS_RFID, RST_RFID);
  RFIDActive = true;
}

void deactivateRFID()
{
  if (RFIDActive) {
    digitalWrite(CS_RFID, HIGH);
    RFIDActive = false;
  }
}

// Make size of files human readable
// source: https://github.com/CelliesProjects/minimalUploadAuthESP32
String humanReadableSize(const size_t bytes) {
  if (bytes < 1024) return String(bytes) + " B";
  else if (bytes < (1024 * 1024)) return String(bytes / 1024.0) + " KB";
  else if (bytes < (1024 * 1024 * 1024)) return String(bytes / 1024.0 / 1024.0) + " MB";  
  else return String(bytes / 1024.0 / 1024.0 / 1024.0) + " GB";
}

void handleUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
  String logmessage;
  
  if (!index) {
    // Validate filename and file extension
    if (filename.length() == 0) {
      request->send(400, "text/plain", "Invalid filename");
      return;
    }
    
    // Check if filename has valid extension (mp3, wav, flac, etc.)
    String lowerFilename = filename;
    lowerFilename.toLowerCase();
    if (!lowerFilename.endsWith(".mp3") && !lowerFilename.endsWith(".wav") && 
        !lowerFilename.endsWith(".flac") && !lowerFilename.endsWith(".m4a") &&
        !lowerFilename.endsWith(".ogg")) {
      request->send(400, "text/plain", "Invalid file type. Only audio files are allowed.");
      return;
    }
    
    // Check available SD card space
    uint64_t cardSize = SD.cardSize() / (1024 * 1024);
    uint64_t usedBytes = SD.usedBytes() / (1024 * 1024);
    uint64_t freeSpace = cardSize - usedBytes;
    
    if (freeSpace < 10) { // Less than 10MB free
      request->send(507, "text/plain", "Insufficient storage space");
      return;
    }
    
    logmessage = "Upload Start: " + String(filename) + " (Free space: " + String(freeSpace) + "MB)";
    Serial.println(logmessage);
    
    // Ensure SD is active
    activateSD();
    
    // Check if file already exists and create backup name if needed
    String filepath = "/" + filename;
    if (SD.exists(filepath)) {
      String baseName = filename.substring(0, filename.lastIndexOf('.'));
      String extension = filename.substring(filename.lastIndexOf('.'));
      int counter = 1;
      do {
        filepath = "/" + baseName + "_" + String(counter) + extension;
        counter++;
      } while (SD.exists(filepath) && counter < 100);
      
      if (counter >= 100) {
        request->send(409, "text/plain", "Too many files with similar names");
        return;
      }
      Serial.println("File exists, using: " + filepath);
    }
    
    // Open the file for writing
    request->_tempFile = SD.open(filepath, FILE_WRITE);
    if (!request->_tempFile) {
      request->send(500, "text/plain", "Failed to create file on SD card");
      return;
    }

  }

  if (len) {
    // Check if file handle is valid
    if (!request->_tempFile) {
      request->send(500, "text/plain", "File handle invalid");
      return;
    }
    
    // Write data and verify bytes written
    size_t bytesWritten = request->_tempFile.write(data, len);
    if (bytesWritten != len) {
      request->_tempFile.close();
      request->send(500, "text/plain", "Write error - SD card may be full");
      return;
    }
    
    // Flush data periodically to ensure it's written
    if (index % 8192 == 0) { // Flush every 8KB
      request->_tempFile.flush();
    }
    
    // Log progress every 100KB
    if (index % 102400 == 0) {
      logmessage = "Upload progress: " + String(filename) + " - " + humanReadableSize(index + len);
      Serial.println(logmessage);
    }
  }

  if (final) {
    if (request->_tempFile) {
      request->_tempFile.flush(); // Ensure all data is written
      request->_tempFile.close();
      
      logmessage = "Upload Complete: " + String(filename) + ", size: " + humanReadableSize(index + len);
      Serial.println(logmessage);
      
      // Rebuild directory tree to include new file
      rootNode.buildDirectoryTree("/");
      
      request->send(200, "text/plain", "Upload successful");
    } else {
      request->send(500, "text/plain", "Upload failed - file handle was invalid");
    }
  }
}

void handleMoveFile(AsyncWebServerRequest *request) {
  String from = request->arg("from");
  String to = request->arg("to");

  if (SD.exists(from)) {
    SD.rename(from.c_str(), to.c_str());
    Serial.println("Moved file: " + from + " to " + to);
    request->send(200, "text/plain", "File moved successfully.");
  } else {
    Serial.println("File not found: " + from);
    request->send(404, "text/plain", "File not found.");
  }
}

void handleDeleteFile(AsyncWebServerRequest *request) {
  String filename = request->arg("filename");

  if (SD.exists(filename)) {
    SD.remove(filename.c_str());
    Serial.println("Deleted file: " + filename);
    request->send(200, "text/plain", "File deleted successfully.");
  } else {
    Serial.println("File not found: " + filename);
    request->send(404, "text/plain", "File not found.");
  }
}

uint32_t getBatteryVoltageMv() {
  uint32_t voltage = analogReadMilliVolts(BAT_VOLTAGE_PIN);
  voltage *= 2;//*2 because of the voltage divider.
  Serial.print("Battery Voltage: ");
  Serial.println(voltage);
  Serial.println(" mV");
  return voltage;
}


void playSongById(uint16_t id, uint32_t continueSeconds = 0)
{
  currentNode = rootNode.advanceToMP3(id);

  if (currentNode==nullptr) {    
    return;
  }

  String mp3File = currentNode->getCurrentPlayingFilePath();
  Serial.print("playing by id: ");
  Serial.print(id);Serial.print(" ");Serial.println(continueSeconds);
  
  Serial.println(mp3File.c_str());
  deactivateRFID();
  activateSD();

  if (currentNode != nullptr && currentNode->getCurrentPlaying()==nullptr) {
    currentNode = nullptr;
    Serial.println("no node by id found, exiting playSongById");
    return;
  }

  playFile(mp3File.c_str());
  if (continueSeconds!=0) {
    audio.setAudioPlayPosition(continueSeconds);
  }
  activateRFID();
  deactivateSD();
}

void playSongByName(String song)
{
  currentNode = rootNode.advanceToMP3(&song);
  String mp3File = currentNode->getCurrentPlayingFilePath();
  Serial.println(mp3File.c_str());
  deactivateRFID();
  activateSD();
  playFile(mp3File.c_str());
}

void playSongByPath(String path)
{
  playFile(path.c_str());
}

void playSongByRFID(String id)
{
  auto songit = rfid_map.find(id);
  if (songit==rfid_map.end()) {
    Serial.println("song for uid not found.");
    return;
  }
  Serial.println("searching for ");
  Serial.println(songit->second);
  playSongByName(songit->second);
}

/**
 * @brief Wrapper, so that we can intercept each call for other stuff.
 *
 * @param filename
 * @param resumeFilePos
 * @return true
 * @return false
 */
bool playFile(const char *filename, uint32_t resumeFilePos)
{
  if (filename == nullptr || strlen(filename)==0) {
    Serial.println("filename is empty.");
    return false;
  }
  //return audio.connecttoFS(filename, resumeFilePos);
  return audio.connecttoFS(SD,filename,resumeFilePos);
}

void playNextMp3()
{
  stop();
  continuousMode = true;
  if (currentNode == NULL)
  {
    currentNode = rootNode.findFirstDirectoryWithMP3s();
    if (currentNode)
    {
      currentNode->advanceToFirstMP3InThisNode();
    }
  }
  else
  {
    currentNode = rootNode.advanceToNextMP3(currentNode->getCurrentPlaying());
  }

  if (currentNode != NULL)
  {
    currentNode->setSecondsPlayed(0);
  }

  Serial.print("Now advancing to ");
  String mp3File = currentNode->getCurrentPlayingFilePath();
  Serial.println(mp3File.c_str());
  deactivateRFID();
  activateSD();
  playFile(mp3File.c_str());
  activateRFID();
  deactivateSD();
}

void audio_info(const char *info)
{
  // Serial.print("info        "); Serial.println(info);
}

void mute()
{
  if (audio.getVolume() != 0)
  {
    volume = audio.getVolume();
  }

  audio.setVolume(0);
}

void unmute()
{
  audio.setVolume(volume);
}

void writeSongProgress(const char *filename, uint16_t id, uint32_t seconds) {
  File file = SD.open(filename, FILE_WRITE);
  if (file) {
    file.print(id);
    file.print(" ");
    file.println(seconds);
    file.close();
    Serial.println("Data written to file: ID-" + String(id) + ", Number-" + String(seconds));
  } else {
    Serial.println("Error opening file for writing.");
  }
}

boolean readSongProgress(const char *filename) {
  String data = "";
  File file = SD.open(filename);

  if (file) {
    while (file.available()) {
      char character = file.read();
      data += character;
    }
    file.close();
    // Parse the string into ID and number
    sscanf(data.c_str(), "%d %d", &currentSongId, &currentSongSeconds);
    Serial.println("Data read from file: " + data);
    return true;
  } else {
    Serial.println("Error opening file for reading.");
    return false;
  }
}

String getState()
{
  DynamicJsonDocument jsonState(1024);
  jsonState["playing"] = audio.isRunning();

  if (currentNode != nullptr)
    jsonState["title"] = *currentNode->getCurrentPlaying();
  else
    jsonState["title"] = "Angehalten";

  jsonState["time"] = audio.getAudioCurrentTime();
  jsonState["volume"] = audio.getVolume();
  jsonState["length"] = audio.getAudioFileDuration();
  jsonState["voltage"] = lastVoltage;
  jsonState["uid"] = lastUid;
  jsonState["heap"] = free_heap;
  String output;
  serializeJson(jsonState, output);
  jsonState.clear();
  jsonState.garbageCollect();
  return output;
}

// Function to save the rfid_map to the mapping file
void saveMappingToFile(const char *filename) {
  File file = SD.open(filename, FILE_WRITE);
  if (file) {
    for (const auto &pair : rfid_map) {
      file.println(pair.first + "=" + pair.second);
    }
    file.close();
    Serial.println("Mapping saved to file.");
  } else {
    Serial.println("Error opening file for writing.");
  }
}

// Function to handle edit requests
void editMapping(AsyncWebServerRequest *request) {
  if (request->hasParam("rfid", true) && request->hasParam("song", true)) {
    String rfid = request->getParam("rfid", true)->value();
    String song = request->getParam("song", true)->value();
    rfid_map[rfid] = song;
    saveMappingToFile(mapping_file.c_str());
    request->send(200, "text/plain", "Mapping updated");
  } else {
    request->send(400, "text/plain", "Invalid parameters");
  }
}

std::map<String, String> readDataFromFile(const char *filename) {
  
  File file = SD.open(filename);

  if (file) {
    while (file.available()) {
      // Read key and value from the file
      String line = file.readStringUntil('\n');
      int separatorIndex = line.indexOf('=');
      if (separatorIndex != -1) {
        // Extract key and value
        String key = line.substring(0, separatorIndex).c_str();
        String value = line.substring(separatorIndex + 1).c_str();
        // Add key-value pair to the map
        rfid_map[key] = value;
      }
    }
    file.close();
  } else {
    Serial.print("Error opening file ");
    Serial.println(filename);
  }

  return rfid_map;
}

String processor(const String &var)
{
  if (var == "DIRECTORY")
  {
    return rootNode.getDirectoryStructureHTML();
  }
  return String();
}

void stop()
{
  if (audio.isRunning())
  {
    Serial.println("stopping audio.");
    audio.stopSong();
    if (currentNode != NULL)
    {
      currentNode->setSecondsPlayed(0);
    }
  }
}

void start()
{
  if (currentNode != NULL)
  {
    currentNode->setCurrentPlaying(NULL);
    currentNode = NULL;
  }

  playNextMp3();
}

void togglePlayPause()
{
  if (currentNode != NULL)
  {
    audio.pauseResume();
  }
  else
  {
    playNextMp3();
  }
  lastInteraction = millis();
}

void next()
{
  playNextMp3();
  lastInteraction = millis();
}

void previous()
{
    if (currentNode != NULL) {
        
        const String* curr = currentNode->getCurrentPlaying();
        DirectoryNode* newNode = currentNode->goToPreviousMP3();
        
        if (newNode != NULL) {
            if (curr == newNode->getCurrentPlaying()) {
              // reset to 0 seconds playtime:
              audio.setAudioPlayPosition(0);
            } else {
              // Update the current node and start playing the previous song
              Serial.println("");
              Serial.print("Playing previous song: ");
              Serial.println(currentNode->getCurrentPlayingFilePath());
              currentNode = newNode;
              stop();
              deactivateRFID();
              activateSD();
              playSongByPath(currentNode->getCurrentPlayingFilePath());
              deactivateSD();
              activateRFID();
            }


        }
    }
  lastInteraction = millis();
}

void audio_eof_mp3(const char *info)
{
  Serial.println("audio file ended.");
  if (continuousMode && !prepareSleepMode)
    playNextMp3();
}

/* not working, FIXME remove me! */
void IRAM_ATTR rfid_interrupt()
{
  newRfidInt = true;
}

void readRFID()
{
  rfid.PICC_ReadCardSerial();
  
  String newUid = getRFIDString(rfid.uid.uidByte);
  if (newUid==lastUid) {
    return;
  }
  stop();
  lastUid = newUid;

  Serial.print("Card UID: ");
  Serial.println(lastUid);

  //rfid.PICC_DumpDetailsToSerial(&(rfid.uid));


  playSongByRFID(lastUid);
  lastInteraction = millis();
}

void setup()
{
  // put your setup code here, to run once:
  Serial.begin(115200);
  

  pinMode(BTN_START_STOP, INPUT_PULLUP);
  pinMode(BTN_NEXT, INPUT_PULLUP);
  pinMode(BTN_PREV, INPUT_PULLUP);

  /* setup the IRQ pin*/
  pinMode(IRQ_RFID, INPUT_PULLUP);

  pinMode(CS_RFID, OUTPUT);
  pinMode(CS_SDCARD, OUTPUT);

  digitalWrite(CS_RFID,HIGH);
  digitalWrite(CS_SDCARD,HIGH);
  RFIDActive = false;
  SDActive = false;


  Serial.print("Initializing SD card...");
  activateSD();
  Serial.println("SD initialization done.");
  

  //deep sleep wakeup
  esp_sleep_enable_ext0_wakeup((gpio_num_t)BTN_START_STOP, LOW);



  rootNode.buildDirectoryTree("/");
  rootNode.printDirectoryTree();

  readDataFromFile(mapping_file.c_str());

  
  
  String progressPath = "/"+sys_dir+"/"+progress_file;

  continuePlaying = readSongProgress(progressPath.c_str());

  if (continuePlaying) {
    Serial.print("deleting ");
    Serial.println(progressPath.c_str());
    SD.remove(progressPath.c_str());
  }


  deactivateSD();
  activateRFID();
  Serial.println("RFID");

  //Init MFRC522
  //Init SPI bus
  //SPI.begin(-1, -1, -1, CS_RFID);
  rfid.PCD_Init(CS_RFID, RST_RFID);

 //somehow this test stops rfid from working!
 /* 
 if (rfid.PCD_PerformSelfTest())
  {
    Serial.println("RFID OK");
  }
  else
  {
    Serial.println("RFID Self Test failed!");
  }
*/
  audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT);
  audio.setVolume(volume); // 0...21

  Serial.println("Audio initialized.");

  lastVoltage = getBatteryVoltageMv();

  free_heap = xPortGetFreeHeapSize();

    // first parameter is name of access point, second is the password
  AsyncWiFiManager wifiManager(&server, &dns);

  wifiManager.setTimeout(180);

  Serial.println("Deactivating Brownout detector...");
  WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector

  //wifiManager.resetSettings();

  if (wifiManager.autoConnect("HannaBox"))
  {
    server.on("/", HTTP_GET, [](AsyncWebServerRequest *request)
              { request->send_P(200, "text/html charset=UTF-8", index_html, processor); });

    server.on("/style.css", HTTP_GET, [](AsyncWebServerRequest *request)
              { request->send_P(200, "text/css", css); });

    server.on("/state", HTTP_GET, [](AsyncWebServerRequest *request)
              { 
              String state = getState();
              request->send(200, "application/json charset=UTF-8", state.c_str()); });

    server.on("/start", HTTP_GET, [](AsyncWebServerRequest *request)
              {
        
        request->send(200, "text/plain charset=UTF-8", "start"); 
        start(); });

    server.on("/toggleplaypause", HTTP_GET, [](AsyncWebServerRequest *request)
              {
        
        request->send(200, "text/plain charset=UTF-8", "toggleplaypause"); 
        togglePlayPause(); });

    server.on("/stop", HTTP_GET, [](AsyncWebServerRequest *request)
              {
        
        request->send(200, "text/plain", "stop"); 
        stop(); });

    server.on("/next", HTTP_GET, [](AsyncWebServerRequest *request)
              {
                  
                  request->send(200, "text/plain", "next");
                  next(); });

    server.on("/previous", HTTP_GET, [](AsyncWebServerRequest *request)
              {
                  
                  request->send(200, "text/plain", "previous");
                  previous(); });                  

    server.on("/playbyid", HTTP_GET, id_song_action);

    server.on("/progress", HTTP_POST, progress_action);

    server.on("/volume", HTTP_POST, volume_action);

    server.on("/edit_mapping", HTTP_POST, editMapping);

      // run handleUpload function when any file is uploaded
    server.on("/upload", HTTP_POST, [](AsyncWebServerRequest *request) {
        request->send(200);
      }, handleUpload);

    server.on("/move_file", HTTP_GET, handleMoveFile);
    server.on("/delete_file", HTTP_GET, handleDeleteFile);

    server.begin();
    Serial.println("Wifi initialized.");
  } else {
    Serial.println("Wifi timed out. Fallback no Wifi.");
  }

  Serial.println("Activating Brownout detector...");
  WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 1); //enable brownout detector

  xTaskCreatePinnedToCore(
      loop2,      /* Function to implement the task */
      "RFIDTask", /* Name of the task */
      10000,      /* Stack size in words */
      NULL,       /* Task input parameter */
      0,          /* Priority of the task */
      &RfidTask,  /* Task handle. */
      0);         /* Core where the task should run */

  lastInteraction = millis();
  Serial.println("initialization done.");
}

void id_song_action(AsyncWebServerRequest *request) 
{
  Serial.println("song by id!");

  int params = request->params();
  for (int i = 0; i < params; i++)
  {
    const AsyncWebParameter *p = request->getParam(i);
    if (p->name() == "id")
    {
      playSongById(atoi(p->value().c_str()));
    }
  }
  lastInteraction = millis();
  request->send_P(200, "text/plain", "ok");
}


void progress_action(AsyncWebServerRequest *request)
{

  int params = request->params();
  for (int i = 0; i < params; i++)
  {
    const AsyncWebParameter *p = request->getParam(i);
    if (p->name() == "value")
    {
      audio.setAudioPlayPosition(atoi(p->value().c_str()));
    }
  }
  lastInteraction = millis();
  request->send_P(200, "text/plain", "ok");
}

void volume_action(AsyncWebServerRequest *request)
{

  int params = request->params();
  for (int i = 0; i < params; i++)
  {
    const AsyncWebParameter *p = request->getParam(i);
    if (p->name() == "value")
    {
      audio.setVolume(atoi(p->value().c_str()));
    }
  }
  lastInteraction = millis();
  request->send_P(200, "text/plain", "ok");
}

String getStartupSoundDir() {
  static String tempPath = "/"+sys_dir+"/"+startup_sound;
  return tempPath;
}

void loop()
{
  if (audio.isRunning())
  {
    if (asyncStop)
    {
      asyncStop = false;
      stop();
    }
    audio.loop();
    if (currentNode != nullptr && !prepareSleepMode)
    {
      currentNode->setSecondsPlayed(audio.getAudioCurrentTime());
    }
  }
  else if (asyncStart)
  {
    asyncStart = false;
    start();
  }

  if (continuePlaying) {
    continuePlaying = false;
    startupSoundPlayed = true;
    playSongById(currentSongId,currentSongSeconds);    
  } else if (!startupSoundPlayed) {
    startupSoundPlayed = true;
    playSongByPath(getStartupSoundDir().c_str());
  }



  // send device to sleep:
  long now = millis();

  if (!sleepSoundPlayed && now - lastInteraction > sleepMessageDelay) {       
    sleepSoundPlayed = true;
    prepareSleepMode = true;
    if (currentNode != NULL) {
      String progressPath = "/"+sys_dir+"/"+progress_file;
      
      writeSongProgress(progressPath.c_str(),currentNode->getCurrentPlayingId(),currentNode->getSecondsPlayed());
    }

    String tempPath = "/"+sys_dir+"/"+sleep_sound;
    playSongByPath(tempPath.c_str());
  } 
  
  
  if (now - lastInteraction > sleepDelay) {
    Serial.println("entering deep sleep...");
    deactivateRFID();   
    deactivateSD();
    esp_deep_sleep_start();
  }

  if (asyncTogglePlayPause)
  {
    asyncTogglePlayPause = false;
    togglePlayPause();
  }
  else if (asyncNext)
  {
    asyncNext = false;
    if (audio.isRunning()) {
      next();
    } else {
      uint8_t vol = audio.getVolume();
      if (vol!=MAX_VOL) {
        vol++;
      }
      audio.setVolume(vol);
      playSongByPath(getStartupSoundDir().c_str());

    }
    
  }
  else if (asyncPrev)
  {
    asyncPrev = false;
    if (audio.isRunning()) {
      previous();
    } else {
      uint8_t vol = audio.getVolume();
      if (vol!=0) {
        vol--;
      }
      audio.setVolume(vol);
      playSongByPath(getStartupSoundDir().c_str());
    }
    
  }



  if (loopCounter % RFID_LOOP_INTERVAL == 0)
  {
    deactivateSD();
    activateRFID();
    if (rfid.PICC_IsNewCardPresent())
    {
      readRFID();
    }
    deactivateRFID();
    activateSD();
  }

  if (loopCounter % VOLTAGE_LOOP_INTERVAL == 0)
  {
    lastVoltage = getBatteryVoltageMv();
    free_heap = xPortGetFreeHeapSize();
    if (lastVoltage<VOLTAGE_THRESHOLD) {
      if (voltage_threshold_counter>3) {
        Serial.println("entering deep sleep due to low voltage...");
        lastInteraction = millis() - sleepMessageDelay;
        voltage_threshold_counter = 0;
      } else {
        voltage_threshold_counter++;
      }
    } else {
      voltage_threshold_counter = 0;
    }

  }

  loopCounter++;
}

void loop2(void *parameter)
{
  bool loggingDone = false;
  
  for (;;)
  {
    
    if (buttonPressed(BTN_NEXT))
    {
      asyncNext = true;
    }
    if (buttonPressed(BTN_PREV))
    {
      asyncPrev = true;
    }
    if (buttonPressed(BTN_START_STOP))
    {
      asyncTogglePlayPause = true;
    }
    if (!loggingDone) {
      Serial.println("loop2 started");
      loggingDone = true;
    } 
    vTaskDelay(1);
  }
  
}

boolean buttonPressed(const uint8_t pin)
{

  if (digitalRead(pin) == LOW && buttontoignore != pin)
  {

    unsigned long now = millis();
    if (now - lastStart > SHORT_PRESS_TIME)
    {
      lastStart = now;
      Serial.println("button pressed.");
      buttontoignore = pin;
      lastInteraction = now;
      return true;
    }
  } else if (digitalRead(pin) == HIGH && buttontoignore == pin) {
     buttontoignore = 0;
  }
  
  return false;
}
