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

LiquidCrystal_I2C lcd(0x27,16,2); // SDA=A4, SCL=A5

int S0 = 2;
int S1 = 3;
int C2 = 4;

int buzzer = A0;

const byte ROWS = 4;
const byte COLS = 4;
char keys[ROWS][COLS] = {
  {'1','2','3','A'},
  {'4','5','6','B'},
  {'7','8','9','C'},
  {'*','0','#','D'}
};
byte rowPins[ROWS] = {5,6,7,8};
byte colPins[COLS] = {9,10,11,12};
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);

int roundNum;
int p1Score;
int p2Score;
int correctAnswer;
bool showDecimal;
int currentPlayer;

int readAdder() {
  return digitalRead(S0) + digitalRead(S1)*2 + digitalRead(C2)*4;
}

String toBinary3(int val) {
  String s = "";
  for(int i=2;i>=0;i--){
    s += ((val>>i)&1) ? '1':'0';
  }
  return s;
}

// Play buzzer tone
void playTone(int freq,int dur){
  tone(buzzer,freq,dur);
  delay(dur+50);
}

void showModeSelect(){
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Select Mode:");
  lcd.setCursor(0,1);
  lcd.print("A=Normal B=Game");
  playTone(1000,150);
}

void generateQuestion(){
  int a = random(0,4);
  int b = random(0,4);
  correctAnswer = a + b;
  showDecimal = random(0,2); // true = show decimal, false = show binary

  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Round ");
  lcd.print(roundNum);

  lcd.setCursor(0,1);
  if(showDecimal){
    lcd.print("DEC = ");
    lcd.print(correctAnswer);
  } else{
    lcd.print("BIN = ");
    lcd.print(toBinary3(correctAnswer));
  }
}

// Get player input (the other format)
int getPlayerAnswer(){
  lcd.setCursor(0,1);
  lcd.print("Input:      "); // clear old input
  String input = "";
  char key;
  int maxLength = showDecimal ? 3 : 1; 

  while(true){
    key = keypad.getKey();
    if(key){
      // input binary
      if(showDecimal && (key=='0'||key=='1') && input.length()<3){
        input += key;
        lcd.setCursor(7,1);
        lcd.print(input);
      }
      // input decimal
      if(!showDecimal && (key>='0' && key<='6') && input.length()<1){
        input += key;
        lcd.setCursor(7,1);
        lcd.print(input);
      }
      // submit
      if(input.length()>0 && (key=='*'||key=='#')) break;
    }
  }

  int answer = 0;
  if(showDecimal){
    // convert binary string to int
    for(int i=0;i<input.length();i++) answer = answer*2 + (input[i]-'0');
  } else{
    answer = input.toInt();
  }
  return answer;
}

void playGame(){
  roundNum = 1;
  p1Score = 0;
  p2Score = 0;

  while(roundNum <= 3){
    generateQuestion();

    lcd.setCursor(0,0);
    lcd.print("Press *=P1 #=P2");

    while(true){
      char k = keypad.getKey();
      if(k=='*'){currentPlayer=1; break;}
      if(k=='#'){currentPlayer=2; break;}
    }

    int answer = getPlayerAnswer();

    lcd.clear();
    if(answer == correctAnswer){
      if(currentPlayer==1) p1Score++;
      else p2Score++;
      lcd.print("Correct!");
      playTone(2000,200);
    } else{
      lcd.print("Wrong!");
      playTone(500,200);
    }

    delay(1500);
    roundNum++;
  }

  lcd.clear();
  lcd.setCursor(0,0);
  if(p1Score > p2Score){
    lcd.print("Player 1 Wins!");
    playTone(1500,600);
  } else if(p2Score > p1Score){
    lcd.print("Player 2 Wins!");
    playTone(1500,600);
  } else{
    lcd.print("Tie Game!");
    playTone(1200,600);
  }
}

// ---------- Setup ----------
void setup(){
  pinMode(S0,INPUT);
  pinMode(S1,INPUT);
  pinMode(C2,INPUT);
  pinMode(buzzer,OUTPUT);

  Wire.begin(); // SDA=A4, SCL=A5
  lcd.init();
  lcd.backlight();
  randomSeed(analogRead(A1));

  showModeSelect();
}

// ---------- Loop ----------
void loop(){
  char key = keypad.getKey();

  // ---------- Normal Mode ----------
  if(key=='A'){
    int sum = readAdder();
    lcd.setCursor(0,0);
    lcd.print("Decimal: ");
    lcd.print(sum);
    lcd.print("  "); // clear leftover
    lcd.setCursor(0,1);
    lcd.print("Binary: ");
    lcd.print(toBinary3(sum));
    lcd.print("   "); // clear leftover
    delay(300); // refresh every 300ms
  }

  // ---------- Game Mode ----------
  if(key=='B'){
    playGame();
    showModeSelect();
  }
}
