// LCD1602 to Arduino Uno connection example
#define WOKWI_SIMULATOR
#define red 9
#define green 8
#define ALARM_LED 6

#define ENCODER_CLK 2
#define ENCODER_DT  3
#define ENCODER_SW 4

#define LOCKED 180
#define UNLOCKED 0

const int BUZZER = 11;
const int IRSENSOR = 0;
const int ALARMOFF = 7;

#include <LiquidCrystal_I2C.h>
#include <Servo.h>

//LiquidCrystal lcd(12, 11, 10, 9, 8, 7);
#ifdef WOKWI_SIMULATOR
LiquidCrystal_I2C lcd(0x27, 16, 2);
#else
LiquidCrystal_I2C lcd(0x3f, 16, 2);
#endif

Servo servo;

enum AlarmState
{
  alOff,
  alOn,
  alOnAlt,
  alReset
};

void lock()
{
  digitalWrite(green, LOW);
  digitalWrite(red, HIGH);
  servo.write(LOCKED);
}
int Alarm=alOff;

void unlock()
{
  digitalWrite(red, LOW);
  digitalWrite(green, HIGH);
  servo.write(UNLOCKED);
}

void setup() {
  //lcd.begin(16, 2);
  lcd.init();
  lcd.backlight();

  pinMode(red, OUTPUT);
  pinMode(green, OUTPUT);
  pinMode(ALARMOFF, INPUT_PULLUP);
  pinMode(ALARM_LED, OUTPUT);
  pinMode(ENCODER_CLK, INPUT);
  pinMode(ENCODER_DT, INPUT);
  pinMode(ENCODER_SW, INPUT_PULLUP);
  servo.attach(5);
  lock();
  updateDisplay();
  //lcd.write("Locked 0");
  Serial.begin(9600);
 
}

int entry[4]={0,0,0,0};
int pw[4]={0,8,1,1};

int lastClk = HIGH;
int num=0;
int state=0;

int sw=1;
int lastSw=1;

void updateDisplay()
{
  lcd.clear();
  if(state<0)
  {
    lcd.print("New Code ");
    lcd.print(num/2);
    lcd.setCursor(0,1);
    int i=0;
    switch(state)
    {
      case -7:lcd.print(pw[0]);lcd.print(pw[1]);lcd.print(pw[2]);lcd.print(pw[3]); break;
      case -6:lcd.print(pw[0]);lcd.print(pw[1]);lcd.print(pw[2]); break;
      case -5:lcd.print(pw[0]);lcd.print(pw[1]); break;
      case -4:lcd.print(pw[0]); break;
    }
  }
  else
  {
    lcd.print("Locked ");
    lcd.print(num/2);
    lcd.setCursor(0,1);
    switch(state)
    {
      case 4:lcd.print(".");
      case 3:lcd.print(".");
      case 2:lcd.print(".");
      case 1:lcd.print(".");
    }
  } 
}

int ticks=0;

int alarmTicks=0;

void loop() {
  int ir=analogRead(IRSENSOR);
  int off=digitalRead(ALARMOFF);
  //Serial.println(off);
  
  switch(Alarm)
  {
    case alOff:
      if(ir==0)
        Alarm=alOn;
    break;
    case alOn:
      if(millis()/100&1==1)
        tone(BUZZER,700);
      else 
        tone(BUZZER,300);
      
      if(millis()/200&1==1)
        digitalWrite(ALARM_LED, LOW);
      else 
        digitalWrite(ALARM_LED, HIGH);
      
      if(digitalRead(ALARMOFF)==LOW)
        Alarm=alReset;
    break;
    case alOnAlt:
      tone(BUZZER,300);
      if(digitalRead(ALARMOFF)==LOW)
        Alarm=alReset;
    break;
    case alReset:
      noTone(BUZZER);
      digitalWrite(ALARM_LED, LOW);
      if(ir>0)
        Alarm=alOff;
    break;
  }
  int newClk = digitalRead(ENCODER_CLK);
  if (newClk != lastClk) {
    // There was a change on the CLK pin
    lastClk = newClk;
    int dtValue = digitalRead(ENCODER_DT);
    if (newClk == LOW && dtValue == HIGH) {
      lcd.clear();
      ++num;
      if(num>20)
        num=0;
      updateDisplay();
    }
    if (newClk == LOW && dtValue == LOW) {
      lcd.clear();
      --num;
      if(num<0)
        num=20;
      updateDisplay();
    }
  }
  sw=digitalRead(ENCODER_SW);
  /*Serial.print(state);
  Serial.print(" ");
  Serial.print(ticks);
  Serial.print(" ");
  Serial.println(sw);*/
  if(sw==LOW)
  {
    if(ticks>1000)
    {
      if(state==4)
      {
        state=-1;
        updateDisplay();
        //lastSw=HIGH;
      }
      else if(state>0)
      {
        state=0;
        updateDisplay();
        //lastSw=HIGH;
      }
      else if(state<0)
      {
        state=-1;
        updateDisplay();
        //lastSw=HIGH;
      }
      //lastSw=HIGH;
    }
    ticks++;
  }
  if(sw!=lastSw)
  {
    Serial.print("pressed ");
    Serial.print(lastSw);
    Serial.print(" ");
    Serial.print(sw);
    Serial.print(" ");
    Serial.println(ticks);
    lastSw=sw;
    if(sw==HIGH && ticks<1000)
    {
    
      /*if(state==4 && ticks>2000)
      {
        lcd.clear();
        lcd.print("new code:");
        lcd.print(ticks);
        delay(5000);
      }*/
      
      switch(state)
      {
        case -1:
          state--;
          updateDisplay();
        break;
        case -2:
          state--;
          updateDisplay();
        break;
        case -3:
          pw[0]=num/2;
          state--;
          updateDisplay();
        break;
        case -4:
          pw[1]=num/2;
          state--;
          updateDisplay();
        break;
        case -5:
          pw[2]=num/2;
          state--;
          updateDisplay();
        break;
        case -6:
          pw[3]=num/2;
          lock();
          lcd.print(pw[1]);
          lcd.print(pw[2]);
          lcd.print(pw[3]);
          lcd.print(" ");
          for(int x=0;x<11;++x)
          {
            lcd.print(".");
            delay(100);
          }
          state=0;
          updateDisplay();
        break;
        case 0:
          entry[0]=num/2; 
          state++;
          updateDisplay();
        break;
        case 1:
          entry[1]=num/2; 
          state++;
          updateDisplay();
        break;
        case 2:
          entry[2]=num/2;
          state++;
          updateDisplay();
        break;
        case 3:
          entry[3]=num/2;

          if(entry[0]==pw[0]&&entry[1]==pw[1]&&entry[2]==pw[2]&&entry[3]==pw[3])
          { 
            lcd.clear();
            lcd.print("Opening");
            lcd.setCursor(0,1);
            lcd.print("....");
            unlock();
            for(int x=0;x<12;++x)
            {
              lcd.print(".");
              delay(50);
            }
            lcd.clear();
            lcd.print("Open");

            state++;
          }
          else
          {
             lcd.clear();
             lcd.print("Access Denied!");
             lcd.setCursor(0,1);
             for(int x=0;x<16;++x)
             {
               lcd.print("=");
               delay(1000);
             }
             state=0;
             updateDisplay();
          }
        break;
        case 4:
          lock();
          lcd.clear();
          lcd.print("Locking");
          lcd.setCursor(0,1);
          for(int x=0;x<16;++x)
          {
            lcd.print(".");
            delay(50);
          }
          state=0;
          updateDisplay();
        break;
      }
    } 
    ticks=0;
  }
  delay(1);
}

/*int i=0;
int state=0;

void loop() {
  i=analogRead(A0);
  lcd.clear();
  lcd.print(i/64);
  switch(state)
  {
    case 0:
      if(i>250&&i<260)
        state=1;
        break;
    case 1:
      if(i>260)
        state=0;
      else if(i<70&&i>60)
        state=2;
        break;
    case 2:
      lcd.print("unlocked");
      break;
  }
  delay(100);
}*/