// Include the jpeg decoder library
#include <TJpg_Decoder.h>

// Include the SD card library
#include <SD.h>

// Include Adafruit ILI9341 TFT display library
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>

// Pin definitions
#define SD_CS    4  // SD card chip select
#define TFT_CS   10 // TFT chip select
#define TFT_DC   9  // TFT data/command
#define TFT_RST  -1 // Reset pin, -1 for no reset pin

#define BUFFPIXEL 20  // Buffer size for reading BMP

// Function to read a 16-bit value from the file with byte swap
uint16_t read16(File &f) {
  uint16_t val;
  f.read((uint8_t*)&val, 2);  // Read 2 bytes from file into val
  return (val >> 8) | (val << 8);  // Swap bytes
}

// Function to read a 32-bit value from the file with byte swap
uint32_t read32(File &f) {
  uint32_t val;
  f.read((uint8_t*)&val, 4);  // Read 4 bytes from file into val
  return (val >> 24) | ((val >> 8) & 0x0000FF00) | ((val << 8) & 0x00FF0000) | (val << 24);  // Swap bytes
}


// Initialize TFT display
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST);

void listFiles(File dir, int numTabs) {
  while (true) {
    File entry = dir.openNextFile();
    if (!entry) {
      // No more files, exit the loop
      break;
    }
    for (uint8_t i = 0; i < numTabs; i++) {
      Serial.print("\t");  // Print indentation for directories
    }
    Serial.print(entry.name());  // Print file name
    if (entry.isDirectory()) {
      Serial.println("/");
      listFiles(entry, numTabs + 1);  // Recursive call to list files in subdirectories
    } else {
      Serial.print("\t\t");
      Serial.println(entry.size(), DEC);  // Print file size
    }
    entry.close();
  }
}

// This function opens a BMP file and displays it at the given coordinates
void drawBMP(char *filename, int x, int y) {
  File bmpFile = SD.open(filename);
  if (!bmpFile) {
    Serial.println("File not found!");
    return;
  }

  // BMP header check
  if (read16(bmpFile) == 0x4D42) {  // "BM" header
    uint32_t fileSize = read32(bmpFile);
    uint32_t imageOffset = read32(bmpFile);
    uint32_t headerSize = read32(bmpFile);
    int32_t bmpWidth = read32(bmpFile);
    int32_t bmpHeight = read32(bmpFile);
    uint16_t planes = read16(bmpFile);
    uint16_t depth = read16(bmpFile);

    if (planes != 1 || depth != 24) {
      Serial.println("Unsupported BMP format!");
      bmpFile.close();
      return;
    }

    // Skip to the image data
    bmpFile.seek(imageOffset);

    // Calculate row size (including padding)
    uint32_t rowSize = (bmpWidth * 3 + 3) & ~3;

    // Allocate buffer for one row
    uint8_t rowBuffer[3 * BUFFPIXEL];

    // Set TFT window for the image area
    tft.setAddrWindow(x, y, x + bmpWidth - 1, y + bmpHeight - 1);

    // Read and display pixel data row by row
    for (int row = 0; row < bmpHeight; row++) {
      uint32_t pos = imageOffset + (bmpHeight - 1 - row) * rowSize;  // BMP stores rows bottom to top

      bmpFile.seek(pos);
      int pixelCount = 0;
      while (pixelCount < bmpWidth) {
        int toRead = min(BUFFPIXEL, bmpWidth - pixelCount);
        bmpFile.read(rowBuffer, 3 * toRead);

        for (int i = 0; i < toRead; i++) {
          uint8_t b = rowBuffer[3 * i];  // Blue
          uint8_t g = rowBuffer[3 * i + 1];  // Green
          uint8_t r = rowBuffer[3 * i + 2];  // Red

          uint16_t color = tft.color565(r, g, b);
          tft.pushColor(color);  // Push the pixel to the screen
        }
        pixelCount += toRead;
      }
    }
    bmpFile.close();
    Serial.println("BMP Loaded!");
  } else {
    Serial.println("Not a valid BMP file.");
    bmpFile.close();
  }
}

void setup()
{
  // Start serial communication for debugging
  Serial.begin(115200);
  Serial.println("\n\nTesting TJpg_Decoder library");

  // Initialize SD card
  if (!SD.begin(SD_CS)) {
    Serial.println(F("SD.begin failed!"));
    while (1) delay(0);  // Infinite loop if SD fails
  }
  Serial.println("\r\nSD initialization successful.");

  // Initialize TFT display
  tft.begin();
  tft.setRotation(3);  // Set orientation if needed
  tft.fillScreen(ILI9341_BLACK);  // Clear screen with black color

  File root = SD.open("/");
  listFiles(root, 0);  // List files starting from the root directory
  root.close();

  drawBMP("/test.bmp", 0,0);
}

void loop()
{
  // Wait for 2 seconds before loading the next image
  delay(2000);
}