/*
Hello! This is a game console! If you want to add a game, follow these 4 steps:
1. Add your game to the GAME class.
2. Add your game to the gameList array.
3. Change the size of the gameList array.
4. Call an object of your game class and invoke the main method.

The program already includes the NewGame class, which you can remove. It serves as an example of how to correctly add a game.
*/

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

// Define screen width and height
#define OLED_WIDTH 128
#define OLED_HEIGHT 64

// Define pins
#define SIO 2
#define SJO 3
#define IO 7

// Initialize the OLED display object
Adafruit_SSD1306 display(OLED_WIDTH, OLED_HEIGHT, &Wire, -1);

// Control class to handle all input devices
class Control {
  // Define joystick range
    #define JOSTICK_MIN 123
      #define JOSTICK_MAX 900

        // Define joystick directions
          #define UP 1
            #define DOWN 2
              #define RIGHT 1
                #define LEFT 2

                  // Define pins for horizontal and vertical directions
                    #define HOR A0
                      #define VER A1

                      public:
                        // Function to read joystick input and determine direction
                          uint8_t Jostick(uint8_t pin) {
                              if (analogRead(pin) >= JOSTICK_MAX)
                                    return 2;
                                        if (analogRead(pin) <= JOSTICK_MIN)
                                              return 1;
                                                  else
                                                        return 0;
                                                          }

                                                            // Function to check the state of a switch
                                                              bool Switch(uint8_t pin) {
                                                                  if (digitalRead(pin) == HIGH)
                                                                        return false;
                                                                            if (digitalRead(pin) == LOW)
                                                                                  return true;
                                                                                    }

                                                                                      // Function to check the state of a switch and handle debouncing
                                                                                        bool SwitchIO(uint8_t pin, uint8_t time) {
                                                                                            if (Switch(pin)) {
                                                                                                  delay(time);
                                                                                                        if (Switch(pin))
                                                                                                                return true;
                                                                                                                      else
                                                                                                                              return false;
                                                                                                                                  }
                                                                                                                                    }
                                                                                                                                    };

                                                                                                                                    // 1. GAME Class: Add your games here!
                                                                                                                                    class Game {
                                                                                                                                    public:
                                                                                                                                      class NewGame {
                                                                                                                                          Control contr;

                                                                                                                                            public:
                                                                                                                                                void main() {
                                                                                                                                                      delay(200);

                                                                                                                                                            bool exit = true;

                                                                                                                                                                  while (exit) {
                                                                                                                                                                          display.display();
                                                                                                                                                                                  display.clearDisplay();
                                                                                                                                                                                          display.setTextColor(WHITE);
                                                                                                                                                                                                  display.setTextSize(2, 2);
                                                                                                                                                                                                          display.setCursor(15, 15);
                                                                                                                                                                                                                  display.print("NEW GAME");

                                                                                                                                                                                                                          display.setTextSize(1, 2);
                                                                                                                                                                                                                                  display.setCursor(37, 45);
                                                                                                                                                                                                                                          display.print("Exit");

                                                                                                                                                                                                                                                  display.drawRect(32, 42, 50, 20, WHITE);

                                                                                                                                                                                                                                                          if (contr.Switch(SJO)) {
                                                                                                                                                                                                                                                                    exit = false;
                                                                                                                                                                                                                                                                            }
                                                                                                                                                                                                                                                                                  }
                                                                                                                                                                                                                                                                                      }
                                                                                                                                                                                                                                                                                        };
                                                                                                                                                                                                                                                                                        };

                                                                                                                                                                                                                                                                                        // System class to manage the main menu and system operations
                                                                                                                                                                                                                                                                                        class System {
                                                                                                                                                                                                                                                                                          Control &contr;

                                                                                                                                                                                                                                                                                            #define CURSOR_HEIGHT 20
                                                                                                                                                                                                                                                                                              #define CURSOR_MARGIN 2
                                                                                                                                                                                                                                                                                                String gameList[1]; // 3. Don't forget to change the size of the array, and remember it is zero-based!
                                                                                                                                                                                                                                                                                                  uint8_t id;
                                                                                                                                                                                                                                                                                                    uint8_t gameListSize;
                                                                                                                                                                                                                                                                                                      uint8_t cursorLimit;
                                                                                                                                                                                                                                                                                                        int8_t cursorPos;
                                                                                                                                                                                                                                                                                                          int8_t bgPos;
                                                                                                                                                                                                                                                                                                            bool exitMenu;

                                                                                                                                                                                                                                                                                                            public:
                                                                                                                                                                                                                                                                                                              System() : id(1), cursorPos(2),
                                                                                                                                                                                                                                                                                                                cursorLimit(gameListSize * CURSOR_HEIGHT + CURSOR_MARGIN - CURSOR_HEIGHT),
                                                                                                                                                                                                                                                                                                                  gameListSize(sizeof(gameList) / 6), bgPos(5), exitMenu(true) {
                                                                                                                                                                                                                                                                                                                      // 2. Add your game to the menu. Remember to change the size of the array!
                                                                                                                                                                                                                                                                                                                          gameList[0] = "Create Your Game!";
                                                                                                                                                                                                                                                                                                                            }

                                                                                                                                                                                                                                                                                                                              // Main function for system operation
                                                                                                                                                                                                                                                                                                                                void main() {
                                                                                                                                                                                                                                                                                                                                    while (exitMenu) {
                                                                                                                                                                                                                                                                                                                                          delay(100);
                                                                                                                                                                                                                                                                                                                                                pointer();
                                                                                                                                                                                                                                                                                                                                                      displayDisplays(bgPos);
                                                                                                                                                                                                                                                                                                                                                            turnOFF();
                                                                                                                                                                                                                                                                                                                                                                }
                                                                                                                                                                                                                                                                                                                                                                  }

                                                                                                                                                                                                                                                                                                                                                                    // Function to turn on the system
                                                                                                                                                                                                                                                                                                                                                                      void turnON() {
                                                                                                                                                                                                                                                                                                                                                                          if (contr.SwitchIO(SIO, 20)) {
                                                                                                                                                                                                                                                                                                                                                                                digitalWrite(IO, HIGH);
                                                                                                                                                                                                                                                                                                                                                                                      delay(200);

                                                                                                                                                                                                                                                                                                                                                                                            display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
                                                                                                                                                                                                                                                                                                                                                                                                }
                                                                                                                                                                                                                                                                                                                                                                                                  }

                                                                                                                                                                                                                                                                                                                                                                                                    // Function to turn off the system
                                                                                                                                                                                                                                                                                                                                                                                                      void turnOFF() {
                                                                                                                                                                                                                                                                                                                                                                                                          if (contr.SwitchIO(SIO, 20)) {
                                                                                                                                                                                                                                                                                                                                                                                                                display.clearDisplay();
                                                                                                                                                                                                                                                                                                                                                                                                                      display.display();

                                                                                                                                                                                                                                                                                                                                                                                                                            digitalWrite(IO, LOW);

                                                                                                                                                                                                                                                                                                                                                                                                                                  delay(200);
                                                                                                                                                                                                                                                                                                                                                                                                                                        void (*resetFunc)(void) = 0;
                                                                                                                                                                                                                                                                                                                                                                                                                                              resetFunc();
                                                                                                                                                                                                                                                                                                                                                                                                                                                    delay(200);
                                                                                                                                                                                                                                                                                                                                                                                                                                                        }
                                                                                                                                                                                                                                                                                                                                                                                                                                                          }

                                                                                                                                                                                                                                                                                                                                                                                                                                                            // Function to display menu options on the OLED screen
                                                                                                                                                                                                                                                                                                                                                                                                                                                              void displayDisplays(uint8_t setElements) {
                                                                                                                                                                                                                                                                                                                                                                                                                                                                  display.clearDisplay();
                                                                                                                                                                                                                                                                                                                                                                                                                                                                      for (String name : gameList) {
                                                                                                                                                                                                                                                                                                                                                                                                                                                                            display.setTextColor(WHITE);
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  display.setTextSize(1, 2);
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        display.setCursor(5, setElements);
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              display.println(name);
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    setElements += CURSOR_HEIGHT;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        }
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            display.drawRect(2, cursorPos, 120, CURSOR_HEIGHT, WHITE);
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                display.display();
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  }

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    // Function to handle joystick input for menu navigation
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      void pointer() {
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          if (contr.Switch(SJO)) {
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                display.clearDisplay();
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      menu();
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          }

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              if (contr.Jostick(VER) == DOWN) {
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    cursorPos += CURSOR_HEIGHT;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          id++;

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                if (cursorPos > cursorLimit)
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        cursorPos = cursorLimit, id = gameListSize;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              if (cursorPos > 42)
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      bgPos -= 60, cursorPos = CURSOR_MARGIN, cursorLimit = ((gameListSize + 1) * CURSOR_HEIGHT + CURSOR_MARGIN) - 100;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          }

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              if (contr.Jostick(VER) == UP) {
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    cursorPos -= CURSOR_HEIGHT;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          id--;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                if (cursorPos < CURSOR_MARGIN && bgPos == 5)
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        cursorPos = CURSOR_MARGIN, id = 1;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              if (cursorPos < CURSOR_MARGIN)
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      bgPos += 60, cursorPos = 42, cursorLimit = gameListSize * CURSOR_HEIGHT + CURSOR_MARGIN - CURSOR_HEIGHT;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          }
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            }

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              // 4. Function to handle menu selection and navigation. Call your game object here!
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                void menu() {
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    Game Game;

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        switch (id) {
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            case 1: {
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  Game::NewGame newGame;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        newGame.main();
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              break;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  }

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      default:
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            Serial.println("Error");
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  break;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      }
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        }
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        };

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        Control contr;

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        // Setup function to initialize pins and serial communication
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        void setup() {
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          Serial.begin(9600);

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            pinMode(SIO, INPUT_PULLUP);
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              pinMode(SJO, INPUT_PULLUP);
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                pinMode(IO, OUTPUT);
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  pinMode(HOR, INPUT);
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    pinMode(VER, INPUT);
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    }

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    // Main loop function
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    void loop() {
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      if (contr.SwitchIO(SIO, 20)) {
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          // Create a System object, turn on the system, and run the main loop
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              System system;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  system.turnON();
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      system.main();
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        }
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        }