#include <Wire.h>
#include <LiquidCrystal_I2C.h>

/*

Things needed are:
1. Arduino Mega
2. 9 TTP223 capacitive touch sensors (https://hobbycomponents.com/sensors/901-ttp223-capacitive-touch-sensor)
3. 20x4 I2C LCD
4. Some wires/cabling as needed
5. An LED
*/

/*
The board is Arduino Mega
LCD 16x2 located at I2C addr: 0x27
*/

LiquidCrystal_I2C screen(0x27, 20, 4);

// LED connected to 29
int ledPin = 29;

enum keypad_pin {
  KEYPAD_NUM0,
  KEYPAD_NUM1,
  KEYPAD_NUM2,
  KEYPAD_NUM3,
  KEYPAD_NUM4,
  KEYPAD_NUM5,
  KEYPAD_NUM6,
  KEYPAD_NUM7,
  KEYPAD_NUM8,
  KEYPAD_NUM9,
  KEYPAD_CLEAR,
  KEYPAD_CHANGE_PIN,
  KEYPAD_COUNT
};

// The button should be TTP223 but it
// doesnt exist on simulator

// Pins where the buttons connected to
static int buttonToDigitalPin[KEYPAD_COUNT] = {
  [KEYPAD_NUM0] = 33,
  [KEYPAD_NUM1] = 37,
  [KEYPAD_NUM2] = 39,
  [KEYPAD_NUM3] = 41,
  [KEYPAD_NUM4] = 43,
  [KEYPAD_NUM5] = 45,
  [KEYPAD_NUM6] = 47,
  [KEYPAD_NUM7] = 49,
  [KEYPAD_NUM8] = 51,
  [KEYPAD_NUM9] = 53,
  [KEYPAD_CLEAR] = 31,
  [KEYPAD_CHANGE_PIN] = 35
};

enum global_state {
  GLOBAL_STATE_NOP,
  GLOBAL_STATE_ASKING_PIN,
  GLOBAL_STATE_CHECK_PIN,
  GLOBAL_STATE_WAIT_ANY_KEY
};

enum button_state {
  BUTTON_PRESSED,
  BUTTON_RELEASED
};

bool buttonStatePrev[KEYPAD_COUNT] = {false};
bool buttonStateCurrent[KEYPAD_COUNT] = {false};
enum button_state buttonState[KEYPAD_COUNT] = {};
enum global_state globalState = GLOBAL_STATE_NOP;

// 6 PIN code
#define PIN_SIZE 6

int currentPIN[PIN_SIZE] = {5,5,7, 7,5,5};

int currentPos = 0;
int pinBuffer[PIN_SIZE] = {};

void lockTheThing() {
  // digitalWrite(ledPin, LOW);
}

void unlockTheThing() {
  digitalWrite(ledPin, HIGH);
}

void setup() {
  for (int i = 0; i < KEYPAD_COUNT; i++)
    pinMode(buttonToDigitalPin[i], INPUT);

  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, HIGH);

  screen.init();
  screen.backlight();
  screen.setCursor(0, 0);
  screen.print("Hello There UwU");
  screen.setCursor(0, 1);
  screen.blink();

  lockTheThing();
}

void clearLine(int y) {
  screen.setCursor(0, y);
  screen.print("                    ");
  screen.setCursor(0, y);
}

void clearScreen() {
  screen.clear();
  screen.setCursor(0, 3);
  screen.print("           By Fox <3");
}

#define EINVAL 1
#define ENODATA 2

// -ENODATA: No button pressed
// -EINVAL: non numeric buttons
int getNumberAndPrint() {
  int ret = -ENODATA;
  for (int i = 0; i < KEYPAD_COUNT; i++) {
    if (buttonState[i] == BUTTON_PRESSED) {
      ret = i - KEYPAD_NUM0;
      break;
    }
  }

  if (ret > 9)
    ret = -EINVAL;
  if (ret >= 0)
    screen.print(ret);
  return ret;
}

void loop() {
  for (int i = 0; i < KEYPAD_COUNT; i++)
    buttonStateCurrent[i] = digitalRead(buttonToDigitalPin[i]) == HIGH;
  
  for (int i = 0; i < KEYPAD_COUNT; i++) {
    if (buttonStatePrev[i] == false && buttonStateCurrent[i] == true)
      buttonState[i] = BUTTON_PRESSED;
    else
      buttonState[i] = BUTTON_RELEASED;
  }

  // button_manager_poll();

  int buttonActive = 0;
  for (int i = 0; i < KEYPAD_COUNT; i++)
    buttonActive += buttonStateCurrent[i] ? 1 : 0;
  
  // We only care if there one button active
  if (buttonActive != 1)
    goto dont_care;

  screen.noBlink();

  switch (globalState) {
    case GLOBAL_STATE_WAIT_ANY_KEY:
      // No button was recently pressed
      if (getNumberAndPrint() == -ENODATA)
        break;

      globalState = GLOBAL_STATE_NOP;
      lockTheThing();
      clearScreen();
      break;
    case GLOBAL_STATE_NOP:
      clearScreen();
      
      screen.setCursor(0, 0);
      screen.print("Enter PIN UwU:");
      screen.setCursor(3, 1);
      screen.print("-");
      screen.setCursor(0, 1);
      currentPos = 0;
      globalState = GLOBAL_STATE_ASKING_PIN;
    case GLOBAL_STATE_ASKING_PIN:
      // Always place the seperator
      screen.setCursor(3, 1);
      screen.print("-");
      screen.setCursor(currentPos + (currentPos / 3), 1);

      int num = getNumberAndPrint();
      if (num < 0 && buttonState[KEYPAD_CLEAR] == BUTTON_PRESSED) {
        clearLine(1);
        currentPos = 0;
        goto processing_done;
      }
      
      if (num >= 0 && num <= 9) {
        pinBuffer[currentPos] = num;
        currentPos++;
      }
      
      if (currentPos == PIN_SIZE)
        globalState = GLOBAL_STATE_CHECK_PIN;
      else
        break;
    case GLOBAL_STATE_CHECK_PIN:
      clearScreen();
      screen.setCursor(0, 0);
      currentPos = 0;
      
      // Check pin
      if (memcmp(pinBuffer, currentPIN, sizeof(pinBuffer)) != 0) {
        screen.print("Incorrect PIN! :(");
        goto wrong_pin_wait_for_any_key;
      }

      screen.print("Correct PIN! :)");

      // At here maybe open something
      // protected
      unlockTheThing();
wrong_pin_wait_for_any_key:
      // Wait for any key
      screen.setCursor(0, 1);
      screen.print("Press any key :3");
      globalState = GLOBAL_STATE_WAIT_ANY_KEY;
      break;
  }
  
processing_done:
  screen.blink();
dont_care:
  memcpy(buttonStatePrev, buttonStateCurrent, sizeof(buttonStateCurrent));
  delay(10);
}