#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <SD.h>

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
#define OLED_RESET     -1 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

#define UP_BUTTON_PIN 32 // Pin for up button
#define DOWN_BUTTON_PIN 33 // Pin for down button
#define SELECT_BUTTON_PIN 25 // Pin for select button
#define RETURN_BUTTON_PIN 26 // Pin for return button

#define MAX_FILE_COUNT 50 // Maximum number of files/folders to display
#define MAX_NAME_LENGTH 20 // Maximum length of file/folder names

// Define a struct to hold file/folder information
struct FileInfo {
  char name[MAX_NAME_LENGTH];
  bool isFolder;
};

FileInfo fileList[MAX_FILE_COUNT]; // Array to hold file/folder information
int fileCount = 0; // Number of files/folders in the array
int currentIndex = 0; // Index of the currently highlighted file/folder
int firstIndex = 0; // Index of the first file/folder to display on the screen
int lastIndex = 0; // Index of the last file/folder to display on the screen

void setup() {
  // Initialize serial communication
  Serial.begin(9600);
  
  // Initialize the display
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.clearDisplay();
  display.setTextColor(WHITE);
  display.setTextSize(1);
  
  // Initialize the SD card
  if (!SD.begin(5)) {
    Serial.println("SD card initialization failed!");
    return;
  }
  
  // Load the file/folder information into the array
  loadFiles();
  
  // Display the file/folder list on the screen
  displayList();
}

void loop() {
  // Check if the up button is pressed
  if (digitalRead(UP_BUTTON_PIN) == LOW) {
    // Move the rectangle up one row
    currentIndex--;
    if (currentIndex < 0) {
      currentIndex = fileCount - 1;
    }
    
    // Check if the first index needs to be updated
    if (currentIndex < firstIndex) {
      firstIndex--;
      lastIndex--;
    }
    
    // Update the display
    displayList();
  }
  
  // Check if the down button is pressed
  if (digitalRead(DOWN_BUTTON_PIN) == LOW) {
    // Move the rectangle down one row
    currentIndex++;
    if (currentIndex >= fileCount) {
      currentIndex = 0;
    }
    
    // Check if the last index needs to be updated
    if (currentIndex > lastIndex) {
      lastIndex++;
      firstIndex++;
    }
    
    // Update the display
    displayList();
  }
  
  // Check if the select button is pressed
  if (digitalRead(SELECT_BUTTON_PIN) == LOW) {
    // Check if the current file/folder is a folder
    if (fileList[currentIndex].isFolder) {
      // Change to the selected folder
      chdir(fileList[currentIndex].name);
      
      // Load the file/folder information into the array
      loadFiles();
      
      // Reset the current, first, and last indices
      currentIndex = 0;    
      firstIndex = 0;
      lastIndex = min(fileCount - 1, SCREEN_HEIGHT / 8 - 1);
      
      // Update the display
      displayList();
    }
    else {
      // Open the selected file
      File file = SD.open(fileList[currentIndex].name);
      
      // Read the contents of the file
      String fileContents = "";
      while (file.available()) {
        fileContents += (char)file.read();
      }
      file.close();
      
      // Display the contents of the file on the screen
      display.clearDisplay();
      display.setCursor(0, 0);
      display.print(fileContents);
      display.display();
      
      // Wait for the user to press the return button
      while (digitalRead(RETURN_BUTTON_PIN) == HIGH) {
        delay(50);
      }
      
      // Display the file/folder list on the screen
      displayList();
    }
  }
}

void loadFiles() {
  // Clear the file list array and reset the file count
  memset(fileList, 0, sizeof(fileList));
  fileCount = 0;
  
  // Open the current directory
  File directory = SD.open(".");
  
  // Iterate over the files/folders in the directory
  while (File file = directory.openNextFile()) {
    // Check if the file/folder name is too long
    if (strlen(file.name()) > MAX_NAME_LENGTH) {
      continue;
    }
    
    // Copy the file/folder name into the array
    strcpy(fileList[fileCount].name, file.name());
    
    // Check if the file is a folder
    if (file.isDirectory()) {
      fileList[fileCount].isFolder = true;
    }
    
    // Increment the file count
    fileCount++;
    
    // Check if the maximum file count has been reached
    if (fileCount >= MAX_FILE_COUNT) {
      break;
    }
  }
  
  // Close the directory
  directory.close();
  
  // Sort the file list array
  qsort(fileList, fileCount, sizeof(FileInfo), compareFileInfo);
}

void displayList() {
  // Clear the display
  display.clearDisplay();
  
  // Draw the file/folder list
  int y = 0;
  for (int i = firstIndex; i <= lastIndex; i++) {
    // Check if the current index is out of range
    if (i >= fileCount) {
      break;
    }
    
    // Draw the rectangle for the current row
    if (i == currentIndex) {
      display.drawRect(0, y, SCREEN_WIDTH, 8, WHITE);
    }
    
    // Print the file/folder name
    display.setCursor(2, y + 1);
    display.print(fileList[i].name);
    
    // Move to the next row
    y += 8;
  }
  
  // Display the contents of the display buffer
  display.display();
}

int compareFileInfo(const void *a, const void *b) {
  // Convert the void pointers to FileInfo pointers
  FileInfo *infoA = (FileInfo *)a;
  FileInfo *infoB = (FileInfo *)b;
  
  // Compare the file/folder names
  return strcasecmp(infoA->name, infoB->name);
}