// Header files -- start

#include <Arduino.h>
#include <U8g2lib.h> //U8G2 Library
#include <Wire.h> //For I2C connection
#include <vector>

// Header files -- end

// Store images as bitmaps here

/*
  In Arduino programming, a "bitmap" typically refers to a graphical representation
  of an image or pattern in a 2D grid of pixels. Each pixel in the grid is
  represented by a binary value (0 or 1), indicating whether the pixel
  is turned on (1) or off (0). Bitmaps are commonly used in applications
  where you need to display graphics on devices like LCD screens or LED matrices.
*/

static const unsigned char SDCardIcon[] U8X8_PROGMEM = {0xfc, 0x0f, 0x06, 0x10, 0xaa, 0x22, 0xaa, 0x42, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0xfa, 0x5f, 0x0a, 0x50, 0x0a, 0x50, 0x0a, 0x50, 0x0a, 0x50, 0xfa, 0x5f, 0x06, 0x60, 0xfc, 0x3f};
static const unsigned char WiFiIcon[] U8X8_PROGMEM = {0xf0, 0x07, 0xfe, 0x3f, 0x0f, 0x78, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x03, 0xf8, 0x0f, 0x10, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01};
static const unsigned char BluetoothIcon[] U8X8_PROGMEM = {0x60, 0x00, 0xe0, 0x00, 0xe0, 0x03, 0x60, 0x07, 0x7c, 0x0e, 0x7c, 0x0e, 0x78, 0x07, 0xf0, 0x01, 0xf0, 0x01, 0x78, 0x07, 0x7c, 0x0e, 0x7c, 0x0e, 0x60, 0x07, 0xe0, 0x01, 0xe0, 0x00, 0x60, 0x00};
static const unsigned char BatteryEmptyIcon[] U8X8_PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x1f, 0xfe, 0x3f, 0xff, 0x7f, 0x07, 0x60, 0x07, 0xe0, 0x07, 0xe0, 0x07, 0x60, 0xff, 0x7f, 0xfe, 0x3f, 0xfc, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
static const unsigned char BatteryLowIcon[] U8X8_PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x1f, 0xfe, 0x3f, 0xff, 0x7f, 0x37, 0x60, 0x37, 0xe0, 0x37, 0xe0, 0x37, 0x60, 0xff, 0x7f, 0xfe, 0x3f, 0xfc, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
static const unsigned char BatteryMidIcon[] U8X8_PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x1f, 0xfe, 0x3f, 0xff, 0x7f, 0xb7, 0x61, 0xb7, 0xe1, 0xb7, 0xe1, 0xb7, 0x61, 0xff, 0x7f, 0xfe, 0x3f, 0xfc, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
static const unsigned char BatteryFullIcon[] U8X8_PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x1f, 0xfe, 0x3f, 0xff, 0x7f, 0xb7, 0x6d, 0xb7, 0xed, 0xb7, 0xed, 0xb7, 0x6d, 0xff, 0x7f, 0xfe, 0x3f, 0xfc, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

// Bitmaps -- end

// Store the names of bitmaps in an array for easy accessibility

const unsigned int bitmapImagesCount = 7;
const unsigned char* bitmapImages[bitmapImagesCount] = {
  SDCardIcon,
  WiFiIcon,
  BluetoothIcon,
  BatteryEmptyIcon,
  BatteryLowIcon,
  BatteryMidIcon,
  BatteryFullIcon
};

// Bitmap names in array -- end

// StatusBar - Class to manage StatusBar icons

/*
  StatusBar occupies the top-most row of a display to show necessary information
  like time, bluetooth, WiFi etc.
*/

class StatusBar {
  private:
    U8G2 &u8g2_StatusBar;  // Reference to the U8G2 object for the status bar
    bool wifiEnabled;      // Flag indicating whether WiFi is enabled
    bool bluetoothEnabled; // Flag indicating whether Bluetooth is enabled
    bool sdCardEnabled;     // Flag indicating whether the SD card is enabled
    int batteryLevel;      // Current battery level

    // Method to display the time on the status bar
    void displayTime() {
      /*
        Draw string on the display at specified coordinates
        Usage: u8g2.drawStr(x, y, "TEXT");
      */
      u8g2_StatusBar.drawStr(6, 15, "17:00");
    }

    // Method to display the SD card icon on the status bar
    void displaySdCardIcon() {
      if (sdCardEnabled) {
        /*
          Draw XBM (X BitMap) image on the display at specified coordinates
          Usage: u8g2.drawXBMP(x, y, width, height, data);
        */
        u8g2_StatusBar.drawXBMP(42, 3, 16, 16, bitmapImages[0]);
      }
    }

    // Method to display the WiFi icon on the status bar
    void displayWifiIcon() {
      if (wifiEnabled) {
        u8g2_StatusBar.drawXBMP(65, 3, 16, 16, bitmapImages[1]);
      }
    }

    // Method to display the Bluetooth icon on the status bar
    void displayBluetoothIcon() {
      if (bluetoothEnabled) {
        u8g2_StatusBar.drawXBMP(86, 3, 16, 16, bitmapImages[2]);
      }
    }

    // Enumeration for battery level states
    enum BatteryLevel {
      LEVEL_EMPTY,
      LEVEL_LOW,
      LEVEL_MID,
      LEVEL_FULL
    };

    // Method to display the battery icon based on the battery level
    void displayBatteryIcon() {

      // Map the battery level to the enum
      BatteryLevel batteryEnum = static_cast<BatteryLevel>(batteryLevel);

      switch (batteryEnum) {
        case LEVEL_EMPTY:
          u8g2_StatusBar.drawXBMP(106, 3, 16, 16, bitmapImages[3]);
          break;
        case LEVEL_LOW:
          u8g2_StatusBar.drawXBMP(106, 3, 16, 16, bitmapImages[4]);
          break;
        case LEVEL_MID:
          u8g2_StatusBar.drawXBMP(106, 3, 16, 16, bitmapImages[5]);
          break;
        case LEVEL_FULL:
          u8g2_StatusBar.drawXBMP(106, 3, 16, 16, bitmapImages[6]);
          break;
        default:
          // Default to the empty battery icon
          u8g2_StatusBar.drawXBMP(106, 3, 16, 16, bitmapImages[3]);
          break;
      }
    }

  public:
    // Constructor for the StatusBar class
    StatusBar(U8G2 &u8g2_StatusBar) : u8g2_StatusBar(u8g2_StatusBar), batteryLevel(0), wifiEnabled(false), bluetoothEnabled(false), sdCardEnabled(false) {}

    // Setup method to initialize the OLED display settings
    void setup() {
      u8g2_StatusBar.begin();
      u8g2_StatusBar.setBitmapMode(1);
      u8g2_StatusBar.setFontMode(0);
      u8g2_StatusBar.setDrawColor(1);
      u8g2_StatusBar.setFont(u8g2_font_courB08_tn);
    }

    // Turn icons on/off
    void setSdCardIcon(bool on) {
      sdCardEnabled = on;
    }

    void setWifiIcon(bool on) {
      wifiEnabled = on;
    }

    void setBluetoothIcon(bool on) {
      bluetoothEnabled = on;
    }

    // Set battery level (0-3)
    void setBatteryLevel(int level) {
      if (level >= 0 && level <= 3) {
        batteryLevel = level;
      }
    }

    // Method to display icons on the status bar
    void displayIcons() {
      // Display icons from left to right
      displayTime();
      displaySdCardIcon();
      displayWifiIcon();
      displayBluetoothIcon();
      displayBatteryIcon();
      u8g2_StatusBar.drawFrame(1, 1, 126, 62);
      u8g2_StatusBar.drawLine(1, 20, 126, 20);
    }
};

// StatusBar class -- end

// Menu class for managing menu items
class Menu {
  private:
    int numberOfMenuItems;          // Total number of menu items
    std::vector<const char*> &menuItems; // Vector to store menu items
    int selectedItem;               // Index of the currently selected item
    int nextItem;                   // Index of the next item in the menu

  public:
    // Constructor for the Menu class
    Menu(int numberOfMenuItems, std::vector<const char*> &menuItems) : numberOfMenuItems(numberOfMenuItems), menuItems(menuItems), selectedItem(0), nextItem(1) {}

    // Move to next item
    void moveToNextItem() {
      selectedItem = (selectedItem + 1) % numberOfMenuItems;
      nextItem = (selectedItem + 1) % numberOfMenuItems;
    }

    // Move to previous item
    void moveToPrevItem() {
      selectedItem = selectedItem - 1;
      if (selectedItem < 0)
        selectedItem = numberOfMenuItems - 1;
      nextItem = (selectedItem + 1) % numberOfMenuItems;
    }

    // Return currently selected item
    const char* getCurrentItem() {
      return menuItems[selectedItem];
    }

    // Return next item
    const char* getNextItem() {
      return menuItems[nextItem];
    }
};

// OLED class for controlling the OLED screen
class OLED {
    U8G2 u8g2_OLED;     // U8G2 object for the OLED display
    StatusBar statusBar; // StatusBar object
    Menu mainMenu;       // Menu object

    const int BUTTON_DOWN_PIN = 2; // Pin for the down button
    const int BUTTON_UP_PIN = 4;   // Pin for the up button

  public:
    // Constructor for the OLED class
    OLED(U8G2 &u8g2_OLED, StatusBar &sb, Menu &m) : u8g2_OLED(u8g2_OLED), statusBar(sb), mainMenu(m) {}

    // Setup method to initialize the OLED display and button pins
    void setup() {
      statusBar.setup();
      u8g2_OLED.begin();
      u8g2_OLED.setBitmapMode(1);
      u8g2_OLED.setFontMode(1);
      pinMode(BUTTON_DOWN_PIN, INPUT_PULLUP);
      pinMode(BUTTON_UP_PIN, INPUT_PULLUP);
    }

    // Loop method to handle OLED display updates
    void loop() {
      // Check if the up button is pressed and move to the previous menu item
      if (digitalRead(BUTTON_UP_PIN) == LOW) {
        mainMenu.moveToPrevItem();
      }

      // Check if the down button is pressed and move to the next menu item
      if (digitalRead(BUTTON_DOWN_PIN) == LOW) {
        mainMenu.moveToNextItem();
      }

      // Start rendering on the OLED display
      u8g2_OLED.firstPage();

      // Set various status bar properties
      statusBar.setSdCardIcon(true);
      statusBar.setWifiIcon(true);
      statusBar.setBluetoothIcon(true);
      statusBar.setBatteryLevel(2);

      // Loop through the pages and render the status bar and menu
      do {
        statusBar.displayIcons();
        displayMenu();
      } while (u8g2_OLED.nextPage());

      // Delay to control the update rate
      delay(500);
    }

    // Method to display the menu on the OLED display
    void displayMenu() {
      u8g2_OLED.setFont(u8g2_font_courB10_tf);
      u8g2_OLED.setDrawColor(2);
      u8g2_OLED.drawBox(6, 24, 115, 16);

      // Get the current item and next item
      const char* currentItem = mainMenu.getCurrentItem();
      const char* nextItem = mainMenu.getNextItem();

      // Get the length of items to dynamically center the text wrto the screen
      int x_offset_current = u8g2_OLED.getStrWidth(currentItem);
      int x_offset_next = u8g2_OLED.getStrWidth(nextItem);

      // Calculation of x offsets
      x_offset_current = (118 - x_offset_current) / 2;
      x_offset_next = (118 - x_offset_next) / 2;

      // Display the current menu item
      u8g2_OLED.drawStr(x_offset_current, 36, currentItem);

      // Display the next menu item
      u8g2_OLED.setFont(u8g2_font_courB10_tf);
      u8g2_OLED.setDrawColor(1);
      u8g2_OLED.drawStr(x_offset_next, 55, nextItem);
    }
};

// Definition of constructors

// Two constructors to manage StatusBar and Menu separately

//Use for Wowki online simulator
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2_OLED(U8G2_R0, /* reset=*/U8X8_PIN_NONE);
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2_StatusBar(U8G2_R0, /* reset=*/U8X8_PIN_NONE);

//Use for hardware
// U8G2_SH1106_128X64_NONAME_F_SW_I2C u8g2_OLED(U8G2_R0, SCL, SDA,U8X8_PIN_NONE);
// U8G2_SH1106_128X64_NONAME_F_SW_I2C u8g2_StatusBar(U8G2_R0, SCL, SDA,U8X8_PIN_NONE);

// Constructors -- end

// Populate the menu items here
std::vector<const char*> menuItems = {"Read RFID", "Settings", "About"};

//Initialize the StatusBar class
StatusBar sb(u8g2_StatusBar);

// Initialize the Menu class
Menu menu(menuItems.size(), menuItems);

// Initialize OLED class
OLED oled(u8g2_OLED, sb, menu);

void setup(void) {
  oled.setup();
}

void loop(void) {
  oled.loop();
}