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

#define OLED_RESET -1
Adafruit_SSD1306 display(128, 64, &Wire, OLED_RESET);

#define SWITCH1 2
#define SWITCH2 3
#define SWITCH3 4
#define SWITCH4 5
#define SWITCH5 6
#define BUTTON 7
#define MOTOR_IN1 8
#define MOTOR_IN2 9

// Predefined correct sequence: {HIGH, LOW, HIGH, LOW, HIGH}
int correctSequence[] = {HIGH, LOW, HIGH, LOW, HIGH};
int switches[] = {SWITCH1, SWITCH2, SWITCH3, SWITCH4, SWITCH5};
int attempts = 5;
bool actuatorExtended = false;
const unsigned char lock [] PROGMEM =
{
  0x00, 0x0f, 0xc0, 0x00, 0x00, 0x7f, 0xf8, 0x00, 0x01, 0xff, 0xfc, 0x00, 0x03, 0xf8, 0x7e, 0x00,
  0x03, 0xe0, 0x1f, 0x00, 0x07, 0xc0, 0x0f, 0x80, 0x07, 0x80, 0x07, 0x80, 0x07, 0x80, 0x07, 0x80,
  0x0f, 0x80, 0x07, 0x80, 0x0f, 0x80, 0x07, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x7f, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xf8, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0xfc,
  0xff, 0xff, 0xff, 0xfc, 0xff, 0xf8, 0xff, 0xfc, 0xff, 0xf0, 0x3f, 0xfc, 0xff, 0xf0, 0x3f, 0xfc,
  0xff, 0xf0, 0x3f, 0xfc, 0xff, 0xf8, 0x7f, 0xfc, 0xff, 0xf8, 0x7f, 0xfc, 0xff, 0xf8, 0x7f, 0xfc,
  0xff, 0xf8, 0xff, 0xfc, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0xfc,
  0x7f, 0xff, 0xff, 0xf8, 0x3f, 0xff, 0xff, 0xe0
};
const unsigned char smile [] PROGMEM =
{
  0x00, 0x3f, 0xe0, 0x00, 0x00, 0xe0, 0x38, 0x00, 0x03, 0x00, 0x06, 0x00, 0x06, 0x00, 0x03, 0x00,
  0x08, 0x00, 0x01, 0x80, 0x10, 0x00, 0x00, 0xc0, 0x30, 0x00, 0x00, 0x60, 0x20, 0x00, 0x00, 0x20,
  0x40, 0x00, 0x00, 0x10, 0x40, 0x00, 0x00, 0x10, 0xc0, 0x00, 0x00, 0x18, 0x80, 0xc0, 0x18, 0x08,
  0x80, 0xe0, 0x38, 0x08, 0x80, 0xc0, 0x18, 0x08, 0x80, 0x00, 0x00, 0x08, 0x80, 0x00, 0x00, 0x08,
  0x80, 0x00, 0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0x80, 0x00, 0x00, 0x08, 0xc0, 0x00, 0x00, 0x18,
  0x40, 0x40, 0x10, 0x10, 0x60, 0x20, 0x20, 0x30, 0x20, 0x10, 0x40, 0x20, 0x30, 0x0f, 0x80, 0x60,
  0x18, 0x00, 0x00, 0xc0, 0x0c, 0x00, 0x01, 0x80, 0x06, 0x00, 0x03, 0x00, 0x03, 0x80, 0x0e, 0x00,
  0x00, 0xf0, 0x78, 0x00, 0x00, 0x1f, 0xc0, 0x00
};
const unsigned char sad [] PROGMEM =
{
  0x00, 0x1f, 0xe0, 0x00, 0x00, 0xff, 0xfc, 0x00, 0x01, 0xf8, 0x7e, 0x00, 0x07, 0xc0, 0x0f, 0x80,
  0x0f, 0x00, 0x03, 0xc0, 0x1e, 0x00, 0x00, 0xe0, 0x1c, 0x00, 0x00, 0x70, 0x38, 0x60, 0x18, 0x70,
  0x70, 0xf0, 0x3c, 0x38, 0x70, 0xf0, 0x3c, 0x38, 0x60, 0xf0, 0x3c, 0x18, 0xe0, 0x70, 0x1c, 0x1c,
  0xe0, 0x00, 0x00, 0x1c, 0xe0, 0x00, 0x00, 0x0c, 0xc0, 0x00, 0x00, 0x0c, 0xe0, 0x00, 0x00, 0x0c,
  0xe0, 0x0f, 0xc0, 0x1c, 0xe0, 0x3f, 0xf0, 0x1c, 0x60, 0x78, 0x78, 0x18, 0x70, 0x70, 0x1c, 0x38,
  0x70, 0xe0, 0x1c, 0x38, 0x38, 0xc0, 0x0c, 0x70, 0x1c, 0x00, 0x00, 0xe0, 0x1e, 0x00, 0x01, 0xe0,
  0x0f, 0x00, 0x03, 0xc0, 0x07, 0xc0, 0x0f, 0x80, 0x01, 0xf8, 0x7e, 0x00, 0x00, 0x7f, 0xfc, 0x00,
  0x00, 0x1f, 0xe0, 0x00
};

void setup() {
  // Initialize switches and button
  for (int i = 0; i < 5; i++) pinMode(switches[i], INPUT_PULLUP);
  pinMode(BUTTON, INPUT_PULLUP);
  pinMode(MOTOR_IN1, OUTPUT);
  pinMode(MOTOR_IN2, OUTPUT);

  // Initialize OLED
  // display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // here the 0x3c is the I2C address, check your i2c address if u have multiple devices.
  display.clearDisplay();
  delay(2000);

  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(WHITE);
  showLockScreen();
}

void loop() {
  static bool buttonPressed = false;

  if (digitalRead(BUTTON) == LOW) {
    if (!buttonPressed) {
      buttonPressed = true;
      if (actuatorExtended) {
        retractActuator();
        showMessage("Reset switches to Lock again!");
        attempts = 0;
      } else if (attempts > 0) {
        if (checkSequence()) {
          display.clearDisplay();
          display.setCursor(45, 0);
          display.println("Correct!");
          display.drawBitmap(49, 17, smile, 30, 30, WHITE);
          display.display();
          extendActuator();
          // showMessage("Correct! Smile :)");
        } else {
          attempts--;
          if (attempts == 0) {
            showMessage("No attempts left! Reset.");
          } else {
            showMessage("Attempts left: " + String(attempts));
            display.drawBitmap(49, 17, sad, 30, 30, WHITE);
            display.display();

          }
        }
      } else {
        if (resetSwitches()) {
          attempts = 5;
          showLockScreen();
        } else {
          showMessage("Reset switches to retry.");
        }
      }
    }
  } else {
    buttonPressed = false;
  }
  delay(250);
}

bool checkSequence() {
  for (int i = 0; i < 5; i++) {
    int state = digitalRead(switches[i]);
    if ((correctSequence[i] == HIGH && state != HIGH) ||
        (correctSequence[i] == LOW && state != LOW) ||
        (correctSequence[i] == HIGH && state != HIGH && state != LOW)) {
      return false;
    }
  }
  return true;
}

bool resetSwitches() {
  for (int i = 0; i < 5; i++) {
    int state = digitalRead(switches[i]);
    if (state == LOW ) return false;
  }
  return true;
}

void extendActuator() {
  digitalWrite(MOTOR_IN1, HIGH);
  digitalWrite(MOTOR_IN2, LOW);
  delay(5000); // Extend actuator for 5 seconds
  digitalWrite(MOTOR_IN1, LOW);
  digitalWrite(MOTOR_IN2, LOW);
  actuatorExtended = true;
}

void retractActuator() {
  digitalWrite(MOTOR_IN1, LOW);
  digitalWrite(MOTOR_IN2, HIGH);
  delay(5000); // Retract actuator for 5 seconds
  digitalWrite(MOTOR_IN1, LOW);
  digitalWrite(MOTOR_IN2, LOW);
  actuatorExtended = false;
}

void showLockScreen() {
  display.clearDisplay();
  display.setCursor(10, 0);
  display.println("Puzzle Box Locked");
  display.drawBitmap(49, 17, lock, 30, 30, WHITE);
  display.display();
}

void showMessage(String message) {
  display.clearDisplay();
  display.setCursor(0, 0);
  display.println(message);
  display.display();
}