//
//----------------------
//  Include
//----------------------
#include <Arduino.h>
#include "FS.h"
#include "SD.h"
#include "SPI.h"
//
//----------------------
//  Define
//----------------------
#define SerialPC Serial
//
//----------------------
//  Helper
//----------------------
void ListDirectory(fs::FS &fs, const char * dirname, uint8_t levels)
{
  SerialPC.printf("Listing directory: %s\n", dirname);
  File R = fs.open(dirname);
  if (!R)
  {
    SerialPC.println("Failed to open directory");
    return;
  }
  if (!R.isDirectory())
  {
    SerialPC.println("Not a directory");
    return;
  }
  File F = R.openNextFile();
  while (F)
  {
    if (F.isDirectory())
    {
      SerialPC.print("  DIRECTORY : ");
      SerialPC.println(F.name());
      if(levels)
      {
        ListDirectory(fs, F.name(), levels -1);
      }
    } 
    else 
    {
      SerialPC.print("  FILE: ");
      SerialPC.print(F.name());
      SerialPC.print("  SIZE: ");
      SerialPC.println(F.size());
    }
    F = R.openNextFile();
  }
}
//
void CreateDirectory(fs::FS &fs, const char * path)
{
  SerialPC.printf("Creating Dir: %s\n", path);
  if (fs.mkdir(path))
  {
    SerialPC.println("Directory created");
  } 
  else 
  {
    SerialPC.println("mkdir failed");
  }
}
//
void RemoveDirectory(fs::FS &fs, const char * path)
{
  SerialPC.printf("Removing Dir: %s\n", path);
  if (fs.rmdir(path))
  {
    SerialPC.println("Dir removed");
  } 
  else 
  {
    SerialPC.println("rmdir failed");
  }
}
//
void ReadFile(fs::FS &fs, const char * path)
{
  SerialPC.printf("Reading file: %s\n", path);
  File file = fs.open(path);
  if (!file)
  {
    SerialPC.println("Failed to open file for reading");
    return;
  }
  SerialPC.print("Read from file: ");
  while (file.available())
  {
    SerialPC.write(file.read());
  }
  file.close();
}
//
void WriteFile(fs::FS &fs, const char * path, const char * message)
{
  SerialPC.printf("Writing file: %s\n", path);
  File file = fs.open(path, FILE_WRITE);
  if (!file)
  {
    SerialPC.println("Failed to open file for writing");
    return;
  }
  if (file.print(message)) 
  {
    SerialPC.println("File written");
  } 
  else 
  {
    SerialPC.println("Write failed");
  }
  file.close();
}
//
void AppendFile(fs::FS &fs, const char * path, const char * message)
{
  SerialPC.printf("Appending to file: %s\n", path);
  File file = fs.open(path, FILE_APPEND);
  if(!file)
  {
    SerialPC.println("Failed to open file for appending");
    return;
  }
  if(file.print(message))
  {
      SerialPC.println("Message appended");
  } 
  else 
  {
    SerialPC.println("Append failed");
  }
  file.close();
}
//
void RenameFile(fs::FS &fs, const char * path1, const char * path2)
{
  SerialPC.printf("Renaming file %s to %s\n", path1, path2);
  if (fs.rename(path1, path2)) 
  {
    SerialPC.println("File renamed");
  } 
  else 
  {
    SerialPC.println("Rename failed");
  }
}
//
void RemoveFile(fs::FS &fs, const char * path)
{
  SerialPC.printf("Deleting file: %s\n", path);
  if(fs.remove(path))
  {
    SerialPC.println("File deleted");
  } 
  else 
  {
    SerialPC.println("Delete failed");
  }
}
//
void TestFileIO(fs::FS &fs, const char * path)
{
  File F = fs.open(path);
  static uint8_t Buffer[512];
  size_t L = 0;
  uint32_t start = millis();
  uint32_t end = start;
  if (F)
  {
    L = F.size();
    size_t FL = L;
    start = millis();
    while (L)
    {
      size_t NTR = L;
      if(NTR > 512)
      {
        NTR = 512;
      }
      F.read(Buffer, NTR);
      L -= NTR;
    }
    end = millis() - start;
    SerialPC.printf("%u bytes read for %u ms\n", FL, end);
    F.close();
  } 
  else 
  {
    SerialPC.println("Failed to open file for reading");
  }
  F = fs.open(path, FILE_WRITE);
  if (!F)
  {
    SerialPC.println("Failed to open file for writing");
    return;
  }
  start = millis();
  for (size_t I = 0; I < 2048; I++)
  {
    F.write(Buffer, 512);
  }
  end = millis() - start;
  SerialPC.printf("%u bytes written for %u ms\n", 2048 * 512, end);
  F.close();
}
//
//---------------------- 
//  Main - Setup 
//----------------------
void setup()
{ // Initialize SerialPC
  SerialPC.begin(115200);
  delay(333);
  SerialPC.println("\r\n\r\n*** AIEsp32SDCardInfo ***");
  SerialPC.println("Version: 01V01");
  SerialPC.println("Date   : 220609");
  SerialPC.println("Time   : 1342");
  SerialPC.println("Author : OMdevelop");
  // Initialize SDCard
  if(!SD.begin(5))
  {
    SerialPC.println("Card Mount Failed");
    return;
  }
  uint8_t cardType = SD.cardType();
  if(CARD_NONE == cardType)
  {
    SerialPC.println("No SDCard attached");
    return;
  }
  SerialPC.print("SD Card Type: ");
  if(CARD_MMC == cardType)
  {
    SerialPC.println("MMC");
  } 
  else 
    if (CARD_SD == cardType)
    {
    SerialPC.println("SDSC");
    } 
    else 
      if (CARD_SDHC == cardType)
      {
        SerialPC.println("SDHC");
      } 
      else 
      {
        SerialPC.println("UNKNOWN");
      }
  uint64_t cardSize = SD.cardSize() / (1024 * 1024);
  SerialPC.printf("SD Card Size: %lluMB\n", cardSize);
  ListDirectory(SD, "/", 0);
  CreateDirectory(SD, "/mydir");
  ListDirectory(SD, "/", 0);
  RemoveDirectory(SD, "/mydir");
  ListDirectory(SD, "/", 2);
  WriteFile(SD, "/hello.txt", "Hello ");
  AppendFile(SD, "/hello.txt", "World!\n");
  ReadFile(SD, "/hello.txt");
  RemoveFile(SD, "/foo.txt");
  RenameFile(SD, "/hello.txt", "/foo.txt");
  ReadFile(SD, "/foo.txt");
  TestFileIO(SD, "/test.txt");
  SerialPC.printf("Total space: %lluMB\n", SD.totalBytes() / (1024 * 1024));
  SerialPC.printf("Used space: %lluMB\n", SD.usedBytes() / (1024 * 1024));
}
//
//----------------------
//  Main - Loop
//----------------------
void loop()
{
  // nothing to do
}
