#include "FileOperations.h"

/**
 * @brief Delete file
 *
 * @param fs
 * @param path
 */
void FileOperations::deleteFile(fs::FS &fs, const char *path)
{
  Serial.printf("Deleting file: %s\n", path);
  if (fs.remove(path))
  {
    Serial.println("File deleted");
  }
  else
  {
    Serial.println("Delete failed");
  }
}

/**
 * @brief Read file
 *
 * @param fs
 * @param path
 */
void FileOperations::printFile(fs::FS &fs, const char *path)
{
  Serial.printf("Reading file: %s\n", path);

  File file = fs.open(path);
  if (!file)
  {
    Serial.println("Failed to open file for reading");
    return;
  }
  while (file.available())
  {
    Serial.write(file.read());
  }
  Serial.println();
  file.close();
}

/**
 * @brief Write to file
 *
 * @param fs
 * @param path
 * @param message
 */
void FileOperations::writeFile(fs::FS &fs, const char *path, const char *message)
{
  Serial.printf("FileOperations 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();
}

/**
 * @brief Append in file
 *
 * @param fs
 * @param path
 * @param message
 */
void FileOperations::appendFile(fs::FS &fs, const char *path, const char *message)
{
  Serial.printf("Appending to file: %s\n", path);

  File file = fs.open(path, FILE_APPEND);
  if (!file)
  {
    Serial.println("Failed to open file for appending");
    return;
  }
  if (file.print(message))
  {
    Serial.println("Message appended");
  }
  else
  {
    Serial.println("Append failed");
  }
  file.close();
}

/**
 * @brief List all files in SPIFF
 *
 * @param fs
 * @param dirname
 * @param levels
 */
void FileOperations::listDir(fs::FS &fs, const char *dirname, uint8_t levels)
{
  Serial.printf("Listing directory: %s\n", dirname);

  File root = fs.open(dirname);
  if (!root)
  {
    Serial.println("Failed to open directory");
    return;
  }
  if (!root.isDirectory())
  {
    Serial.println("Not a directory");
    return;
  }

  File file = root.openNextFile();
  while (file)
  {
    if (file.isDirectory())
    {
      Serial.print("  DIR : ");
      Serial.print(file.name());
      time_t t = file.getLastWrite();
      struct tm *tmstruct = localtime(&t);
      Serial.printf("  LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour, tmstruct->tm_min, tmstruct->tm_sec);
      if (levels)
      {
        listDir(fs, file.name(), levels - 1);
      }
    }
    else
    {
      Serial.print("  FILE: ");
      Serial.print(file.name());
      Serial.print("  SIZE: ");
      Serial.print(file.size());
      time_t t = file.getLastWrite();
      struct tm *tmstruct = localtime(&t);
      Serial.printf("  LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour, tmstruct->tm_min, tmstruct->tm_sec);
    }
    file = root.openNextFile();
  }
}

/**
 * @brief Remove all files contains the user ID
 *
 * @param userID
 */
void FileOperations::removeUser(String userID)
{
  userID.trim();
  Serial.print("Deleting:");
  Serial.println(userID);
  File root = SPIFFS.open("/");
  if (!root)
  {
    Serial.println("Failed to open directory");
    return;
  }
  File file = root.openNextFile();
  while (file)
  {
    // check file name
    // if starts with number it is users card
    String fname = file.name();
    fname = fname.substring(1, fname.length()); // remove first symbol '/'
    if ((fname.charAt(0) >= '0') && (fname.charAt(0) <= '9'))
    {
      while (file.available())
      {
        if (userID.equals(file.readString()))
        {
          Serial.print(userID);
          Serial.print(" apears in:");
          Serial.println(fname);
          // TODO delete file
          deleteFile(SPIFFS, file.name());
        }
      }
      file.close();
    }
    file = root.openNextFile();
  }
}

/**
 * @brief Remove all users
 *
 */
void FileOperations::clearUsers()
{
  Serial.print("Deleting all");
  File root = SPIFFS.open("/");
  if (!root)
  {
    Serial.println("Failed to open directory");
    return;
  }
  File file = root.openNextFile();
  while (file)
  {
    // check file name
    // if starts with number it is users card
    String fname = file.name();
    fname = fname.substring(1, fname.length()); // remove first symbol '/'
    if ((fname.charAt(0) >= '0') && (fname.charAt(0) <= '9'))
    {
      if (file.available())
      {
        Serial.println(fname);
        // TODO delete file
        deleteFile(SPIFFS, file.name());
      }
      file.close();
    }
    file = root.openNextFile();
  }
}

/**
 * @brief Sends message with all users and slots
 *
 */
void FileOperations::listUsers(char *MAC_char, AsyncMqttClient mqttClient)
{
  // TODO returns all files but config.txt
  //    1242510d4d01/27:5,27:6,27:7,27:8,4:9,4:10,32:11,32:12,6:13,4:9,4:10,32:11,32:12,6:13,8:1,5:2,5:3
  File root = SPIFFS.open("/");
  if (!root)
  {
    Serial.println("Failed to open directory");
    return;
  }
  File file = root.openNextFile();

  String message(MAC_char);
  message += "/";
  while (file)
  {
    // check file name
    // if starts with number it is users card
    String fname = file.name();
    fname = fname.substring(1, fname.length()); // remove first symbol '/'
    // Serial.println(fname.charAt(0));
    if ((fname.charAt(0) >= '0') && (fname.charAt(0) <= '9'))
    {

      while (file.available())
      {
        message += file.readString();
        message += ":";
        break;
      }
      message += fname;
      message += ",";
      file.close();
    }
    file = root.openNextFile();
  }
  Serial.println(message); // print users
  int len = message.length();
  char msg[len];
  message.toCharArray(msg, len);
  mqttClient.publish("users", 0, false, msg, len);
}

FileOperations::FileOperations()
{
  // File System
  //  if(!SPIFFS.begin())
  //  {
  //    Serial.println("Card Mount Failed");
  //    return;
  //  }
}

void FileOperations::printInfo()
{
  Serial.print("SPIFFS Free: ");
  Serial.println(humanReadableSize((SPIFFS.totalBytes() - SPIFFS.usedBytes())));
  Serial.print("SPIFFS Used: ");
  Serial.println(humanReadableSize(SPIFFS.usedBytes()));
  Serial.print("SPIFFS Total: ");
  Serial.println(humanReadableSize(SPIFFS.totalBytes()));
}

// Make size of files human readable
// source: https://github.com/CelliesProjects/minimalUploadAuthESP32
String FileOperations::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";
}

/**
 * @brief Print file
 *
 * @param filename
 */
void FileOperations::readFile(char *buff, const char *filename)
{
  // Open file for reading
  File file = SPIFFS.open(filename);
  if (!file)
  {
    Serial.println(F("Failed to read file"));
    return;
  }

  // Extract each characters one by one
  uint16_t cnt = 0;
  while (file.available())
  {
    // Serial.print((char)file.read());
    buff[cnt] = (char)file.read();
    cnt++;
  }

  // Close the file
  file.close();
}


void FileOperations::findSlots(String filename, int value, int* keys, int& count) {
  File file = SPIFFS.open(filename); // open the file

  if (file) {
    count = 0; // initialize the count variable

    while (file.available()) {
      String line = file.readStringUntil('\n'); // read a line from the file
      int colonIndex = line.indexOf(':'); // find the index of the colon

      if (colonIndex != -1) { // if the colon is found
        int lineValue = line.substring(colonIndex + 1).toInt(); // get the value after the colon
        int lineKey = line.substring(0, colonIndex).toInt(); // get the key before the colon

        if (lineValue == value) { // if the value matches the given value
          keys[count] = lineKey; // add the key to the array
          count++; // increment the count variable
        }
      }
    }

    file.close(); // close the file
  } else {
    Serial.println("Failed to open file");
  }
}


void FileOperations::deleteKeys(String filename, uint64_t value) {
  File inputFile = SPIFFS.open(filename); // open the input file
  File outputFile = SPIFFS.open("/temp.txt", FILE_WRITE); // create a temporary output file for writing

  if (inputFile && outputFile) {
    while (inputFile.available()) {
      String line = inputFile.readStringUntil('\n'); // read a line from the input file
      int colonIndex = line.indexOf(':'); // find the index of the colon

      if (colonIndex != -1) { // if the colon is found
        int lineValue = line.substring(colonIndex + 1).toInt(); // get the value after the colon

        if (lineValue != value) { // if the value does not match the given value
          outputFile.println(line); // write the line to the output file
        }
      }
    }

    inputFile.close(); // close the input file
    outputFile.close(); // close the output file

    SPIFFS.remove(filename); // remove the input file
    SPIFFS.rename("/temp.txt", filename); // rename the output file to the input file
  } else {
    Serial.println("Failed to open file");
  }
}
