
/**
 * @file file.ino
 * @brief File handling and storage for PacoMouseCYD throttle.
 * @author F. Cañada
 * @date 2025-2026
 * @copyright https://usuaris.tinet.cat/fmco/
 *
 * This file contains functions for managing locomotive data files, checking file names,
 * saving and loading data to and from the SD card or internal filesystem, and related utilities.
 */

////////////////////////////////////////////////////////////
// API Documentation
////////////////////////////////////////////////////////////
/**
 * @brief Checks if a file name has a .csv extension.
 * @param fileName The file name to check.
 * @return True if the file name ends with .csv, false otherwise.
 */
bool checkName(char *fileName);

/**
 * @brief Saves locomotive data to a .csv file.
 * @param fs The filesystem object.
 * @param pos The position in the locomotive data array.
 * @return True if the data was saved successfully, false otherwise.
 */
bool saveLocoData(fs::FS &fs, uint16_t pos);

// Add further function documentation here as needed for each public function.

////////////////////////////////////////////////////////////
// End API Documentation
////////////////////////////////////////////////////////////

/*    PacoMouseCYD throttle -- F. Cañada 2025-2026 --  https://usuaris.tinet.cat/fmco/
*/


bool checkName(char *fileName) {
  bool result;
  uint16_t lng;
  result = false;
  lng = strlen(fileName);
  if (lng > 4) {
    if ((fileName[lng - 4] == '.') && (fileName[lng - 3] == 'c') && (fileName[lng - 2] == 's') && (fileName[lng - 1] == 'v'))
      return true;
  }
  return result;
}

bool saveLocoData(fs::FS &fs, uint16_t pos) {                           // save loco data in .csv file
  char field[30];
  uint16_t cnt;
  bool dataOK, isDir;
  File myFile;
  dataOK = false;
  myFile = fs.open("/loco");
  if (myFile) {
    isDir = myFile.isDirectory();
    myFile.close();
    if (!isDir)
      return dataOK;
    DEBUG_MSG("/loco is a directory");
  }
  else {
    DEBUG_MSG("Directory /loco not found. Creating...")
    fs.mkdir("/loco");
  }
  sprintf (field, "/loco/%d.csv", locoData[pos].myAddr.address);
  myFile = fs.open(field, FILE_WRITE);
  if (myFile) {
    DEBUG_MSG("File %s opened for writting", field);
    getLabelTxt(LBL_NAME, field);                         // Header
    myFile.print(field);
    myFile.print(CSV_FILE_DELIMITER);
    getLabelTxt(LBL_IMAGE, field);
    myFile.print(field);
    myFile.print(CSV_FILE_DELIMITER);
    getLabelTxt(LBL_VMAX, field);
    myFile.print(field);
    for (cnt = 0; cnt < 29; cnt++) {
      myFile.print(CSV_FILE_DELIMITER);
      myFile.print('F');
      myFile.print(cnt);
    }
    myFile.print("\r\n");
    myFile.print(locoData[pos].myName);                   // Loco data
    myFile.print(CSV_FILE_DELIMITER);
    myFile.print(locoData[pos].myLocoID);
    myFile.print(CSV_FILE_DELIMITER);
    myFile.print(locoData[pos].myVmax);
    for (cnt = 0; cnt < 29; cnt++) {
      myFile.print(CSV_FILE_DELIMITER);
      myFile.print(locoData[pos].myFuncIcon[cnt] >> 1);
    }
    myFile.print("\r\n");
    myFile.close();
    dataOK = true;
  }
  return dataOK;
}


void deleteLocoData (fs::FS &fs, uint16_t num) 
{
  char line[200];
  sprintf (line, "/loco/%d.csv", num);
  if (fs.remove(line)) {
    DEBUG_MSG("File %s deleted", line);
  }
  else {
    DEBUG_MSG("File %s delete failed", line);
  }
}


bool readLocoData(fs::FS &fs, uint16_t num, uint16_t pos) {            // read loco data from .csv file
  char line[200];
  bool dataOK;
  uint16_t n;
  File myFile;
  dataOK = false;
  sprintf (line, "/loco/%d.csv", num);
  myFile = fs.open(line);
  if (myFile) {
    if (readCSV(myFile, line, sizeof(line), false)) {                                     // skip header line
      if (readCSV(myFile, locoData[pos].myName, sizeof(locoData[pos].myName), true)) {    // read data field: Name
        if (readCSV(myFile, line, sizeof(line), true)) {                                  // read data field: Picture
          locoData[pos].myLocoID = atoi(line);
          if (readCSV(myFile, line, sizeof(line), true)) {
            locoData[pos].myVmax = atoi(line);
            DEBUG_MSG("%d %s %d %d", num, locoData[pos].myName, locoData[pos].myLocoID, locoData[pos].myVmax)
            for (n = 0; n < 29; n++) {
              if (!readCSV(myFile, line, sizeof(line), true)) {                             // read data field: Functions
                myFile.close();
                return false;
              }
              locoData[pos].myFuncIcon[n] = (uint8_t)(atoi(line) << 1);
              //DEBUG_MSG("Func: %d - %d", n, locoData[pos].myFuncIcon[n])
            }
            locoData[pos].myAddr.address = num;
            dataOK = true;
          }
        }
      }
    }
    myFile.close();
  }
  return dataOK;
}


bool readCSV(File & f, char* line, uint16_t maxLen, bool getField) {
  uint16_t n;                                               // read field or line from CSV file
  char chr;
  n = 0;
  yield();
  while (n < maxLen) {
    chr = f.read();
    switch (chr) {
      case 0:
        line[n] = '\0';
        return false;                                       // EOF
        break;
      case '\r':
        line[n] = '\0';
        break;
      case '\n':
        line[n] = '\0';
        return true;
        break;
      case ',':
      case ';':
        if (getField) {
          line[n] = '\0';
          return true;
        }
        break;
      default:
        line[n++] = chr;
        break;
    }
  }
  return false;                                             // too long
}


void loadLocoFiles(fs::FS &fs, const char * dirname) {
  uint16_t pos, adr;
  char nameFile[50];
  File myFile;
  if (wifiSetting.protocol == CLIENT_ECOS)
    return;
  File root = fs.open(dirname);
  if (!root) {
    //DEBUG_MSG("Failed to open directory %s", dirname);
    return;
  }
  if (!root.isDirectory()) {
    //DEBUG_MSG("%s Not a directory", dirname);
    return;
  }
  pos = 0;
  File file = root.openNextFile();
  while (file) {
    if (! file.isDirectory()) {
      if (pos < LOCOS_IN_STACK) {
        sprintf(nameFile, "%s", file.name());
        adr = atoi(nameFile);
        //DEBUG_MSG("%d %s", adr, nameFile);
        if (readLocoData(fs, adr, pos++))
          pushLoco(adr);
      }
    }
    file = root.openNextFile();
  }
}

void initImageList() {
  uint16_t n, maxImg;
  uint16_t pos, id;
  char nameFile[50];
  File myFile;

  for (n = 0; n < MAX_LOCO_IMAGE; n++)                          // add to list system images
    locoImages[n] = (n < (MAX_SYS_LPIC - 1)) ? n + 1 : 0;
  if (wifiSetting.protocol != CLIENT_ECOS) {
    if (sdDetected) {                                           // add  to list user images from SD
      File root = SD.open("/image");
      if (root) {
        if (root.isDirectory()) {
          pos = MAX_SYS_LPIC - 1;
          File file = root.openNextFile();
          while (file) {
            if (! file.isDirectory()) {
              if (pos < MAX_LOCO_IMAGE) {
                sprintf(nameFile, "%s", file.name());
                id = atoi(nameFile);
                if (id >= 1000) {
                  locoImages[pos++] = id;
                }
              }
            }
            file = root.openNextFile();
          }
        }
      }
    }
  }
  locoImageIndex = 0;
}


void populateImageList() {
  uint16_t n;
  for (n = 0; n < 6; n++)
    lpicData[LPIC_SEL_IMG1 + n].id = locoImages[locoImageIndex + n];
}


bool saveCurrAccPanel(fs::FS &fs) {
  char field[30];
  uint16_t cnt;
  bool dataOK, isDir;
  File myFile;
  dataOK = false;
  myFile = fs.open("/acc");
  if (myFile) {
    isDir = myFile.isDirectory();
    myFile.close();
    if (!isDir)
      return dataOK;
    DEBUG_MSG("/acc is a directory");
  }
  else {
    DEBUG_MSG("Directory /acc not found. Creating...")
    fs.mkdir("/acc");
  }
  sprintf (field, "/acc/%d.csv", currPanel);
  myFile = fs.open(field, FILE_WRITE);
  if (myFile) {
    DEBUG_MSG("File %s opened for writting", field);
    getLabelTxt(LBL_ACC_TYPE, field);                         // Header
    myFile.print(field);
    myFile.print(CSV_FILE_DELIMITER);
    getLabelTxt(LBL_ACC_ADDR, field);
    myFile.print(field);
    myFile.print(CSV_FILE_DELIMITER);
    getLabelTxt(LBL_ACC_ADDR, field);
    myFile.print(field);
    myFile.print(CSV_FILE_DELIMITER);
    getLabelTxt(LBL_ACC_NAME, field);
    myFile.print(field);
    myFile.print(CSV_FILE_DELIMITER);
    getLabelTxt(LBL_BITS, field);
    myFile.print(field);
    myFile.print(CSV_FILE_DELIMITER);
    myFile.print("\r\n");
    for (cnt = 0; cnt < 16; cnt++) {
      myFile.print(accPanel[cnt].type);                  // Acc data
      myFile.print(CSV_FILE_DELIMITER);
      myFile.print(accPanel[cnt].addr);
      myFile.print(CSV_FILE_DELIMITER);
      myFile.print(accPanel[cnt].addr2);
      myFile.print(CSV_FILE_DELIMITER);
      myFile.print(accPanel[cnt].accName);
      myFile.print(CSV_FILE_DELIMITER);
      myFile.print(accPanel[cnt].activeOutput);
      myFile.print(CSV_FILE_DELIMITER);
      myFile.print("\r\n");
    }
    myFile.close();
    dataOK = true;
  }
  return dataOK;
}


bool loadAccPanel(fs::FS & fs) {
  char line[200];
  bool dataOK;
  uint16_t cnt = 0;
  File myFile;
  dataOK = false;
  sprintf (line, "/acc/%d.csv", currPanel);
  myFile = fs.open(line);
  if (myFile) {
    if (readCSV(myFile, line, sizeof(line), false)) {                                     // skip header line
      for (cnt = 0; cnt < 16; cnt++) {
        readCSV(myFile, line, sizeof(line), true);
        accPanel[cnt].type = (accType)atoi(line);
        readCSV(myFile, line, sizeof(line), true);
        accPanel[cnt].addr = atoi(line);
        readCSV(myFile, line, sizeof(line), true);
        accPanel[cnt].addr2 = atoi(line);
        readCSV(myFile, line, sizeof(line), true);
        snprintf(accPanel[cnt].accName, ACC_LNG + 1, line);
        readCSV(myFile, line, sizeof(line), false);
        accPanel[cnt].activeOutput = atoi(line);
        DEBUG_MSG("Line %d: %d, %d, %d, \"%s\", %d", cnt, accPanel[cnt].type, accPanel[cnt].addr, accPanel[cnt].addr2, accPanel[cnt].accName, accPanel[cnt].activeOutput)
      }
    }
    myFile.close();
    dataOK = true;
  }
  return dataOK;
}


bool saveAccPanelNames(fs::FS & fs) {
  char field[30];
  uint16_t cnt;
  bool dataOK, isDir;
  File myFile;
  dataOK = false;
  myFile = fs.open("/acc");
  if (myFile) {
    isDir = myFile.isDirectory();
    myFile.close();
    if (!isDir)
      return dataOK;
    DEBUG_MSG("/acc is a directory");
  }
  else {
    DEBUG_MSG("Directory /acc not found. Creating...")
    fs.mkdir("/acc");
  }
  sprintf (field, "/acc/panel.csv");
  myFile = fs.open(field, FILE_WRITE);
  if (myFile) {
    DEBUG_MSG("File %s opened for writting", field);
    getLabelTxt(LBL_NAME, field);                         // Header
    myFile.print(field);
    myFile.print("\r\n");
    for (cnt = 0; cnt < 16; cnt++) {
      myFile.print(panelNamesBuf[cnt]);                   // Panel names
      myFile.print("\r\n");
    }
    myFile.close();
    dataOK = true;
  }
  return dataOK;
}


bool loadAccPanelNames(fs::FS & fs) {
  char line[200];
  bool dataOK;
  uint16_t n;
  File myFile;
  dataOK = false;
  sprintf (line, "/acc/panel.csv");
  myFile = fs.open(line);
  if (myFile) {
    if (readCSV(myFile, line, sizeof(line), false)) {                                     // skip header line
      for (n = 0; n < 16; n++) {
        if (readCSV(myFile, line, sizeof(line), true)) {                                  // read data field
          snprintf(panelNamesBuf[n], PANEL_LNG + 1, line);
          DEBUG_MSG("%s", panelNamesBuf[n])
        }
      }
    }
    myFile.close();
  }
  else {
    for (n = 0; n < 16; n++)                                                            // default names
      snprintf(panelNamesBuf[n], PANEL_LNG + 1, "Panel %d", n);
  }
  return dataOK;
}









/*
  void 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.println(file.name());
      if (levels) {
        listDir(fs, file.path(), levels - 1);
      }
    } else {
      Serial.print("  FILE: ");
      Serial.print(file.name());
      Serial.print("  SIZE: ");
      Serial.println(file.size());
    }
    file = root.openNextFile();
  }
  }
*/
