////////////////////////////////////////////////////
// File Name: KeyboardProjectLeft (MASTER)
// Revision: 1.0
// Date started: 5/19/2023
// Designed and programmed by Lucas Meyers

//pin D2: I2C SDA  |   usb   |   pin A3:  aux. pot
//pin D3: I2C SCL  |         |   pin A2:  col 8
//pin D4: col 7    |         |   pin A1:  col 6
//pin D5: row 5    |   top   |   pin A0:  col 5
//pin D6: row 4    |         |   pin D15: col 4
//pin D7: row 3    |         |   pin D14: col 3
//pin D8: row 2    |         |   pin D16: col 2
//pin D9: row 1    |_________|   pin D10: col 1
///////////////////////////////////////////////////

#include <Keyboard.h>  //comment out for debugging
#include <Wire.h> //i2c connection 

const int SLAVE_ADDY = 8;
const int ROWS = 5;
const int COLS = 6;
const int RIGHTCOLS = 8;
const int rowPins[ROWS] = {9, 8, 7, 6, 5};
const int colPins[COLS] = {A1, A0, 15, 14, 16, 10};

int enable = 1;
int byteCount = 0; //used for counting bytes recieved
int initialDelay = 70; //number of cycles held before sending second char (not time based) (500 ~ 392 ms)  
int repeatDelay = 5; //number of cycles held before sending another char (not time based) (50 ~ 39 ms)

byte bytes[5] = {0, 0, 0, 0, 0};  //2 unused
byte bytesRight[5] = {0, 0, 0, 0, 0};  //byte storage for right (5 rows of 8)

unsigned int keyHoldCount [ROWS][COLS]; //this will increment every cycle
bool keyLongHold [ROWS][COLS]; //this will be true only if key is held for initial cycle delay

unsigned int keyHoldCountRight [ROWS][RIGHTCOLS]; //this will increment every cycle
bool keyLongHoldRight [ROWS][RIGHTCOLS]; //this will be true only if key is held for initial cycle delay

char keyMap[ROWS][COLS] = {
  {'`' , '1' , '2' , '3' , '4' , '5'},
  {179,  'q' , 'w' , 'e' , 'r' , 't'},  //tab
  {193,  'a' , 's' , 'd' , 'f' , 'g'},  //caps lock
  {129,  'z' , 'x' , 'c' , 'v' , 'b'},  //shift
  {128,  128,  131,  130,  ' ' , '*'},  //ctrl, ctrl, win, alt
};

char keyMapRight[ROWS][RIGHTCOLS] = {
  {'6' , '7' , '8' , '9' , '0' , '-' , '=', 178 }, //backspace
  {'y' , 'u' , 'i' , 'o' , 'p' , '[' , ']', '\\'},
  {'h' , 'j' , 'k' , 'l' , ';' , '\'', 176, '*' }, //enter, not used
  {'n', 'm' , ',' , '.' , '/' , 133 , 218, 132  }, //shift, up, ctrl
  {'*', ' ',  134,  132,  132,  216 , 217, 215  }, //alt, ctrl, ctrl, left, down, right 
};

/////////funciton prototypes: ///////////////
void keyPressed(int row, int col);
void keyReset(int row, int col);
void keyPressedRight(int row, int col);
void keyResetRight(int row, int col);
void printByteArray(const unsigned char* array, size_t length);
////////////////////////////////////////////
void setup() {
  Wire.begin();
  Serial.begin(9600);
  
  for (int i = 0; i < ROWS; i++) {
    pinMode(rowPins[i], OUTPUT);
  }

  for (int j = 0; j < COLS; j++) {
    pinMode(colPins[j], INPUT_PULLUP);
  }
  
  pinMode(A3, INPUT);
  if(digitalRead(A3) == HIGH){
    enable = 0;
  }
  delay(3000); //for debugging/uploading
  Keyboard.begin(); 

}

void loop() {

  if(digitalRead(A3) == HIGH){  //temporary switch for debugging
    enable = 0;
  }
  else{
    enable = 1;
  }
  
  if(enable){
    
    Wire.requestFrom(8, 5);    // request 5 bytes from slave (8)
    while (Wire.available()) { // slave may send less than requested
      bytesRight[byteCount] = Wire.read(); // receive a byte as character
      byteCount++;
    }
    
    byteCount = 0;  //reset byte count
    //printByteArray(bytesRight, 5);  //print current status for debugging

    
    for (int i = 0; i < ROWS; i++) { //iterate through each row
      
      digitalWrite(rowPins[i], LOW);  //set row to be checked low
      delayMicroseconds(100);  //for stabilization
  
      for (int j = 0; j < COLS; j++) { //iterate through each col
        
        if(digitalRead(colPins[j]) == LOW) {
          //keyPressed(i,j);   //call keyPressed function when on keypress. UPDATE: can't use due to modifier logic
          bytes[i] |= (1 << j); //set bit
        }
        
        else{  
          bytes[i] &= ~(1 << j); //clear bit
        }
        
      }
  
      digitalWrite(rowPins[i], HIGH);  //set row high once checked
      delayMicroseconds(100);  //for stabilization
    }
    
    for(int k = 0; k < ROWS; k++){
    
        for(int m = (COLS - 1); m >= 0; m--){
          if((bytes[k] >> m) & 1){  //if key is pressed, run keyPressed funciton
            keyPressed(k,m);
          }
          else{
              if(!((k == 3 && m == 0) || (k == 4 && m == 0) || (k == 4 && m == 1) || (k == 4 && m == 2) || (k == 4 && m == 3))){  //if not modifier
                keyReset(k,m);    //if key is no longer held, reset
              }
              else {
                Keyboard.release(keyMap[k][m]);  //if modifier
              }
          }
        }
      }
  
    for(int n = 0; n < ROWS; n++){
  
      for(int p = (RIGHTCOLS - 1); p >= 0; p--){
        if((bytesRight[n] >> p) & 1){  //if key is pressed, run keyPressedRight function
          keyPressedRight(n,(7-p));
        }
        else{
            if(!((n == 3 && p == (7-5)) || (n == 3 && p == (7-7)) || (n == 4 && p == (7-2)) || (n == 4 && p == (7-3)) || (n == 4 && p == (7-4)))){ // 7-x for reverse bit order
              keyResetRight(n,(7-p));    //if key is no longer held, reset
            }
            else{
              Keyboard.release(keyMapRight[n][p]);  //if modifier
            }
        }
      }
    }

    
  }  //end of if enable
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void keyPressed(int row, int col) {
  if(!((row == 3 && col == 0) || (row == 4 && col == 0) || (row == 4 && col == 1) || (row == 4 && col == 2) || (row == 4 && col == 3))){
     if(keyHoldCount[row][col] == 0){         //if the function is called for the first time for this key, print it
      //Serial.print(keyMap[row][col]);  //debugging
      Keyboard.write(keyMap[row][col]);
    }
    
    else if(keyLongHold[row][col] && keyHoldCount[row][col] > repeatDelay){ // if key is held, print every number of repeatDelay cycles
      //Serial.print(keyMap[row][col]);  //debugging
      Keyboard.write(keyMap[row][col]);
      keyHoldCount[row][col] = 1;  //this is to reset count so repeatDelay is constant
    }
    
    else if(keyHoldCount[row][col] > initialDelay){ //if the key has been held for initial delay, set long hold true
      keyLongHold[row][col] = true;
    }
  
    keyHoldCount[row][col]++;  //increment key count
  }
  else{
     Keyboard.press(keyMap[row][col]);
  }
  
}


void keyPressedRight(int row, int col) {
  if(!((row == 3 && col == 5) || (row == 3 && col == 7) || (row == 4 && col == 2) || (row == 4 && col == 3) || (row == 4 && col == 4))){

     if(keyHoldCountRight[row][col] == 0){         //if the function is called for the first time for this key, print it
      //Serial.print(keyMapRight[row][col]);  //debugging
      Keyboard.write(keyMapRight[row][col]);
      }
      
      else if(keyLongHoldRight[row][col] && keyHoldCountRight[row][col] > repeatDelay){ // if key is held, print every number of repeatDelay cycles
        //Serial.print(keyMapRight[row][col]);  //debugging
        Keyboard.write(keyMapRight[row][col]);
        keyHoldCountRight[row][col] = 1;  //this is to reset count so repeatDelay is constant
      }
      
      else if(keyHoldCountRight[row][col] > initialDelay){ //if the key has been held for initial delay, set long hold true
        keyLongHoldRight[row][col] = true;
      }
      keyHoldCountRight[row][col]++;  //increment key count
      
    }
    else{
      Keyboard.press(keyMapRight[row][col]);
    }
}



void keyReset(int row, int col) {
  keyHoldCount[row][col] = 0;
  keyLongHold[row][col] = false;
}

void keyResetRight(int row, int col) {
  keyHoldCountRight[row][col] = 0;
  keyLongHoldRight[row][col] = false;
}


//CHAT GPT CODE FOR DEBUGGING:
void printByteArray(const unsigned char* array, size_t length) {
    for (size_t i = 0; i < length; i++) {
        for (int j = 7; j >= 0; j--) {
            Serial.print(((array[i] >> j) & 1));
        }
        Serial.print(" ");
    }
    Serial.print("\n");
}