#ifndef MAIN_H_
#define MAIN_H_

// define pins for RFID
#define CS_RFID 32 // SIC, tried 4 and 32 but only this worked!
#define RST_RFID 33
// this does not work as the irq pin is input only:
#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_WEBREQUEST_BLOCKINGS 10000

#define MAX_VOL 15

File root;
File mp3File;

Audio audio;

uint volume = 7;

// Folder-play tracking: flattened list of files inside a mapped folder and current index
// Used when a mapping targets a folder (play folder once or loop folder)
static std::vector<std::pair<DirectoryNode *, int>> folderFlatList;
static int folderFlatIndex = -1;
static String folderRootPath = "";
// Pointer to the root DirectoryNode for active folder-mode playback
DirectoryNode *folderRootNode = nullptr;

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;
volatile bool playButtonDown = false;
// Track if play button hold is active and if volume was adjusted during this hold
volatile bool playHoldActive = false;
volatile bool volumeAdjustedDuringHold = false;
volatile uint8_t sd_lock_flag = 0;

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 asyncReset = false;

bool SDActive = false;

bool RFIDActive = false;


// Web request concurrency counter and helpers (atomic via GCC builtins)
volatile uint32_t webreq_cnt = 0;
static inline void webreq_enter() { __sync_add_and_fetch(&webreq_cnt, 1); }
static inline void webreq_exit()  { __sync_sub_and_fetch(&webreq_cnt, 1); }
volatile bool server_reset_pending = false;

uint16_t voltage_threshold_counter = 0;

size_t free_heap = 0;

void stop();

void start();

bool playFile(const char* filename, uint32_t resumeFilePos = 0);

void loop2(void* parameter);

void id_song_action(AsyncWebServerRequest *request);

void progress_action(AsyncWebServerRequest *request);

void volume_action(AsyncWebServerRequest *request);

void init_webserver();

boolean buttonPressed(const uint8_t pin);


/**
 * Helper routine to dump a byte array as hex values to Serial.
 */
void dump_byte_array(byte *buffer, byte bufferSize)
{
    for (byte i = 0; i < bufferSize; i++)
    {
        Serial.print(buffer[i] < 0x10 ? " 0" : " ");
        Serial.print(buffer[i], HEX);
    }
}

/* Simple spinlock using older GCC sync builtins (no libatomic required).
   sd_lock_acquire() will block (with a small delay) until the lock is free.
   sd_lock_release() releases the lock. This is sufficient for short SD ops. */
static inline void sd_lock_acquire()
{
  while (__sync_lock_test_and_set(&sd_lock_flag, 1))
  {
    delay(1);
  }
}

static inline void sd_lock_release()
{ 
  __sync_lock_release(&sd_lock_flag);
}


String getRFIDString(byte uidByte[10])
{
    String uidString = String(uidByte[0]) + " " + String(uidByte[1]) + " " +
                       String(uidByte[2]) + " " + String(uidByte[3]);
    return uidString;
}

void writeFile(fs::FS &fs, const char * path, const char * message){
  Serial.printf("Writing file: %s\n", path);

  File file = fs.open(path, FILE_WRITE);
  if(!file){
    Serial.println("Failed to open file for writing");
    return;
  }
  if(file.print(message)){
    Serial.println("File written");
  } else {
    Serial.println("Write failed");
  }
  file.close();
}

unsigned long lastStart = 0;

unsigned long lastInteraction =  0;

boolean sleepSoundPlayed = false;

boolean startupSoundPlayed = false;

boolean continuousMode = false;

uint8_t buttontoignore = 0;

uint32_t lastVoltage = 0;

uint loopCounter = 0;

String lastUid = "";

uint16_t currentSongId = 0;

uint32_t currentSongSeconds = 0;

boolean continuePlaying = false;

boolean prepareSleepMode = false;

class DirectoryNode;

/* Mapping entry that stores target (file or folder) and playback mode:
 * 's' = single (default) - play only the selected song (or single file in folder)
 * 'f' = folder  - play files inside the selected folder, then stop
 * 'r' = random-folder - play files inside the selected folder in random order, then stop
 * 'c' = continuous - continuously play (like previous continuousMode)
 */
struct MappingEntry {
  String target;
  char mode;
  MappingEntry() : target(""), mode('s') {}
  MappingEntry(const String& t, char m) : target(t), mode(m) {}
};

std::map<String, MappingEntry> rfid_map;

// Folder-play helper: when a mapping requests "folder only" playback we keep
// track of the folder root node so EOF handling can advance only inside that folder.
bool folderModeActive = true;

bool pendingSeek = false;
uint32_t pendingSeekSeconds = 0;

static const size_t MAX_DEPTH = 32;

#endif
