#include <Keypad.h>
#include <LiquidCrystal_I2C.h>
#include "SevSeg.h"
#include <arduino-timer.h>


// Keypad defines.
const uint8_t ROWS = 4;
const uint8_t COLS = 4;
char keys[ROWS][COLS] = {
  { '1', '2', '3'},
  { '4', '5', '6' },
  { '7', '8', '9' },
  { '*', '0', '#' }
};

uint8_t colPins[COLS] = { 31, 33, 35 };
uint8_t rowPins[ROWS] = { 23, 25, 27, 29 };

// Hardware defines.
LiquidCrystal_I2C LCD(0x27,20,4);
Keypad KEYPAD = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);
SevSeg SEVSEG;

// Variables
auto timer = timer_create_default();

int LED_GREEN_PIN = 2;
int LED_RED_PIN = 3;
long DEFUSE_TIME = 0;

int LcdTextLength = 16;
String CODE = "";
String DEFUSE_CODE = "";
int MAX_CODE_LENGTH = 4;

int readDimPinsArray[] = {8,9,10,11};

byte digitPins[] = {32, 30, 28, 26};
byte segmentPins[] = {36, 37, 38, 39, 40, 41, 42};
bool resistorsOnSegments = false; // 'false' means resistors are on digit pins
byte hardwareConfig = COMMON_ANODE; // See README.md for options


// STATE
boolean SETUP_CODE_COMPLETE = false;
boolean SETUP_ARMED_COMPLETE = false;
boolean GAME_END = false;
boolean GAME_FINISHED = false;
boolean BOMB_IS_DEFUSED = false;
boolean BOMB_EXPLODED_OR_DEFUSED = false;

// Text
String TEXT_INPUT_SET_CODE = "SET CODE: ";
String TEXT_RESET_INPUT_CODE = "RESET INPUT...";
String TEXT_INPUT_CODE = "CODE: ";
String TEXT_BOMB_ARMING = "BOMB ARMING";
String TEXT_BOMB_ARMED = "BOMB ARMED";
String TEXT_BOMB_DEFUSED = "BOMB DEFUSED";
String TEXT_EXPLODE = "<<< BOOOOOOM >>>  TERRORISTS WIN";

void setText(const String text="set text", boolean clear=true, int position=0, int line=2){
  if(clear == true) {
    LCD.clear();
    LCD.setCursor(0, 0);
  };

  if(text.length() > LcdTextLength) {
    int i = 0;
    while (i <= text.length())
    {
      if(i == LcdTextLength) {
        LCD.setCursor(0, 1);
      }

      LCD.print(text.charAt(i));
      i++;
    }

  } else{
    LCD.print(text);
  }
}

void setup() {    
  Serial.begin(9600);
  LCD.init(); 
  LCD.backlight();
  for (int pin : readDimPinsArray) {
      pinMode(pin, INPUT); 

      if(digitalRead(pin) == HIGH) {
        increasePlaytime();
      }
  }

  SEVSEG.begin(hardwareConfig,4, digitPins, segmentPins, resistorsOnSegments, false, false, true);
  SEVSEG.setBrightness(90);

  pinMode(LED_GREEN_PIN, OUTPUT);
  pinMode(LED_RED_PIN, OUTPUT);
  pinMode(49, INPUT); 

  LCD.begin(16, 2);
  setText(TEXT_INPUT_SET_CODE, false);

  KEYPAD.addEventListener(keypadEvent); //add an event listener for this keypad

  // Remove next line
  setText(String(DEFUSE_TIME), false);
  SEVSEG.setNumber(DEFUSE_TIME,0);
}

void setLedStatus(int pin, uint8_t status=LOW) {
  digitalWrite(pin, status);
}

void timerDecrease() {
  DEFUSE_TIME = DEFUSE_TIME - 1;
  SEVSEG.setNumber(DEFUSE_TIME,0);

  if(DEFUSE_TIME == 0){
    BOMB_EXPLODED_OR_DEFUSED = true;
    return false;
  } else {
    return true;
  }
}

void increasePlaytime() {
  DEFUSE_TIME = DEFUSE_TIME + 300;
}

void loop() { 
  char key = KEYPAD.getKey();
  timer.tick();
  SEVSEG.refreshDisplay();

  // Bomb loop for game end.
  if(SETUP_ARMED_COMPLETE == true && BOMB_EXPLODED_OR_DEFUSED == true && GAME_FINISHED == false) {
    GAME_FINISHED = true;
    timer.cancel();
    KEYPAD.addEventListener(nullptr);

    if(BOMB_IS_DEFUSED) {
      setLedStatus(LED_RED_PIN, LOW);
      setLedStatus(LED_GREEN_PIN);
      setText(TEXT_BOMB_DEFUSED);
      delay(10000);
      setLedStatus(LED_GREEN_PIN, LOW);
    } else {
      setText(TEXT_EXPLODE);
    }
  }
}


void ledBlinkOnce(int pin, int blinkDelay=500) {
  digitalWrite(pin, HIGH);
  delay(blinkDelay);
  digitalWrite(pin, LOW);
}

void setAndUpdateCode(String codeInput){
  CODE = CODE + codeInput;
  ledBlinkOnce(LED_GREEN_PIN);

  if(CODE.length() == MAX_CODE_LENGTH ){
      SETUP_CODE_COMPLETE = true;
      setupDefuseStage();
  }
}

void setAndUpdateDefuseCode(String codeInput){
  DEFUSE_CODE = DEFUSE_CODE + codeInput;
  ledBlinkOnce(LED_GREEN_PIN); // todo

  if(DEFUSE_CODE == CODE ){
      BOMB_IS_DEFUSED = true;
      BOMB_EXPLODED_OR_DEFUSED = true;
  }
}

void setupDefuseStage(){
      int i = 0;
      String dots = "";
  
      while(i < 4) {
        ledBlinkOnce(LED_RED_PIN);
        setText(TEXT_BOMB_ARMING + dots);
        i++;
        dots.concat(".");
        delay(2000);
      }

      setText(TEXT_BOMB_ARMED);
      setLedStatus(LED_RED_PIN, HIGH);
      KEYPAD.addEventListener(nullptr);
      setupDefuseStageComplete();
}

void setupDefuseStageComplete () {
      SETUP_ARMED_COMPLETE = true;
      setText(TEXT_INPUT_CODE);
      KEYPAD.addEventListener(keypadEventDefuse);
      timer.every(1000, timerDecrease);
}

// Keypad for setup.
void keypadEvent(KeypadEvent key){
  String keyInput = String(key);
  Serial.println("test");
   switch (KEYPAD.getState()){
    case PRESSED:
          setText(keyInput, false);
          setAndUpdateCode(keyInput);
    break;
    case RELEASED:
      switch (key){
      }
    break;
    case HOLD:
      switch (key){
      }
    break;
  }
}

// Keypad for defuse.
void keypadEventDefuse(KeypadEvent key){
  String keyInput = String(key);
   switch (KEYPAD.getState()){
    case PRESSED:
          setText(keyInput, false);
          setAndUpdateDefuseCode(keyInput);
    break;
    case RELEASED:
      switch (key){
      }
    break;
      case HOLD:
      switch (key){
        case '*':
        setText(TEXT_RESET_INPUT_CODE);
        DEFUSE_CODE = "";
        delay(1000);
        setText(TEXT_INPUT_CODE);
      }
    break;
  }
}