// Configuation
// Port B 0 - 3 - LED Port 4 - 7
// Port D 4 - 7 - LED Port 0 - 3
// PORT C 0 - 3 - Keypad Row 1 - 4
// Port C 4 - 5 - Keypad Col 2 - 3
// Port C 2 -3  - Keypad Col 0 - 1
// Keypad configuation
// 1 - 1; 2 - 2; 3 - 3

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<stdbool.h>

struct keys{
    int rowCol;
    char keyPressed;
  };

void fnAdd(char a[],char  b[],int firstLineLength);
void fnSub(char a[],char b[],int firstLineLength);
void fnMul(char a[],char b[],int firstLineLength);
void fnDiv(char dividend[],char divsor[],int firstLineLength);
long fnToNum(char arr[]);
bool fnValidateInput(char x[],char y[]);
void fnToCalc(char x[],char opr);
void print_string(char x[]);
void print_answer(char x[],int firstLineLength);
void movecursor(void);

void fnCalc(int,int);
void fnForCalculation(char*);


void topassCommand();
void topassData();
void passCommand(char);
void writeData(char);


void lcdReady();


void init_port(void);
//void outport(char);
void delay1(int);
char inport(void);
int scan_keypad(void);
void outportc(char);

void setup() {


    char input[10] = {'0'};

    int i =0;

  int x;
  char keys[]= { 0x00,0x00,0x00,0x00,0x00,0x00,'1', '2', '3','+','4','5', '6','-', '7', '8', '9','*', 'C', '0', '=', '/'};
  //outport(0);
  init_port();
   lcdReady();
  
  x = scan_keypad();
  int nth_term = 0;
  int y;
  while(1)
  {
    while(x == 0)
    {
      x = scan_keypad();
      delay(100);
      y = x;
      

    }
    //Serial.println(y);
   
    
  if(keys[x]== '='){
   // Serial.println(x);
    
    fnForCalculation(input);
    nth_term++;
  }
    delay1(1);
    Serial.println(keys[x]);
     writeData(keys[x]);
    input[i] = keys[x];
    i++;
    
    x = 0;
    
  }
    
    

}
;

void movecursor(void)
{
  topassCommand();
  passCommand(0x15);
  
  
}

// void fnCalc(int x,int n){
  
//   char keys[]= {'1', '2', '3','+','4','5', '6',"-", '7', '8', '9','*', 'C', '0', '=', '/'};
//   bool toCalculate = false;
//   char arr[10] = {'0'};
//   char charArr[10] = {'0'};
//   int index = n;
//   int i;
//   for(i=0;i<16;i++){
//     if(i== (x)){
      
//       arr[index] = keys[x];
//       writeData(x);
//       Serial.println(x);
//        if(arr[index] == '='){
//         toCalculate = true;
//       }
      
//       break;
//     }
//   }

//   if(toCalculate){
//       //Serial.println(keys[n]);
//       for(i=0;i<(n-1);i++){
//           charArr[i] = arr[i];
//       }
//       for(i=0;i<30;i++){
//        movecursor();

//         }

    
//     //   if(strstr(charArr,"+")){

//     //     fnToCalc(charArr,'+');
//     //  }

//     // if(strstr(charArr,"-")){

//     //     fnToCalc(charArr,'-');
//     // }

//     // if(strstr(charArr,"*")){
//     //     fnToCalc(charArr,'*');
//     // }

//     // if(strstr(charArr,"/")){
//     //    fnToCalc(charArr,'/');
//     // }

//   }

    
  

// }


int scan_keypad()
{
 int i,row,col,column,rdata;
  char j,k;

  for(row =0; row<4; row++)
  {
    outportc(~(1<<row));
    for(col=0; col<4; col++)
    {
      column = inport();
      
      if(column != 0x0)
      {
        delay1(1);
        //Serial.println(column);
        for(col=0; col<4; col++)
        {
          if(column == (1 << col))
          {
            rdata = ((row+1) * 4) + (col+1) + 1;
            char data = rdata;
            //Serial.println(data);
            delay1(1);
            return(rdata);
          }
        }

      }
    }
  }
  return(0);
}

char inport(void)
{
  char indata,datad,datac;
  char *portc_data = (char *)0x26; 
  char *portd_data = (char *)0x29; 
  datad = *portd_data;
  datac = *portc_data;
  indata = (((datac & 0x30)>>2) & 0x0F) | ((datad & 0x0C) >> 2);
  indata = ~indata & 0x0F;
  return(indata);
}



void init_port()
{
  char *portb_dir  = (char *)0x24;
  char *portc_dir  = (char *)0x27;
  char *portd_dir  = (char *)0x2A;

  *portb_dir = 0xFF;
  *portd_dir = 0xF0;
  *portc_dir = 0x0F;
}


void outportc( char out_data)
{
  char *portc_data = (char *)0x28; 
  
  *portc_data = out_data ;
}
void passCommand(char outdata){
  char *portb_data = (char *)0x25; 
  char *portd_data = (char *)0x2B;
  char data_b = *portb_data;
 // Serial.println(outdata);
  *portb_data =  (outdata>>4) | data_b;
  *portd_data = outdata<<4;
}

void writeData(char outdata){
  char *portb_data = (char *)0x25; 
  char *portd_data = (char *)0x2B;
  char data_b = *portb_data;
 // Serial.println(outdata);
  *portb_data =  (outdata>>4) | data_b;
  *portd_data = outdata<<4;
  topassData();
}



void lcdReady(){

  char *portb_data = (char *)0x25; 
  char *portd_data = (char *)0x2B;
  topassCommand();
  //8 bit 2 line
 passCommand(0x38);
 //clear display
  topassCommand();
  passCommand(0x01);
  topassCommand();
  //display on cursor blinking
 passCommand(0x0F);
  
  topassCommand();
  //auto increment
  passCommand(0x06);
  topassCommand();
  //auto increment
 
}

void topassData(void){

  char *portb_data = (char *)0x25; 
  char *portd_data = (char *)0x2B;
  char data_b = *portb_data;
  *portb_data = 0b00010000 |  data_b ;
  delay(1);
  *portb_data = 0b00110000 |  data_b ;
  delay(1);
  *portb_data = 0b00010000 |  data_b ;
  delay1(1);


}

void topassCommand(void){
   char *portb_data = (char *)0x25; 
  *portb_data = 0b00100000;
  delay(1);
  *portb_data = 0b00000000;
  delay(1);

}


void delay1(int count)
{
  volatile long i;
  while(count)
  {
    for(i=0; i<1000; i++);
    count--;
  }
}




void loop() {

}

void print_answer(char x[],int length){
 int len = 40-length;
 int i;
//  for(i=0;i<len;i++){
//      movecursor();
//  }

  while(*x != 0)
  {
    writeData(*x);
    x++;
  }

}

void print_string(char *x){
    
  while(*x != 0)
  {
    writeData(*x);
    x++;
  }
}


    //Identifying the operation to be performed using a substring of input

    

bool fnValidateInput(char x[],char y[]){

   int lenX = strlen(x);
   int lenY = strlen(y);
   bool isValid = true;
   int i;
   for(i=0;i<lenX;i++){
       if(x[i]>='0' && x[i]<='9'){
           continue;
       }
       else{
           isValid = false;
           break;
       }

   }
   bool secArrValidity;
   for(i=0;i<lenY;i++){
       if(y[i]>='0' && y[i]<='9'){
           continue;
       }
       else{
            secArrValidity = false;
           isValid = (secArrValidity);
           break;
       }

   }

   return isValid;

}

/*
    function name: fnToCalc
    input params: char arrays
    output type: void

    Description: Input string is splitted to arrays of operands and operators.
    Passing the operands for validation.

    In case of valid input, using operator symbol the inputs are passed to
    the appropriate function to perform desired arithmetic operation.


*/

// void fnCalc(int x,int n){
  

//   struct keys key[16];
//   char arr[10] = {'0'};

//   bool toCalculate = false;

//   key[0].rowCol=6;
//   key[0].keyPressed = '1';

//   key[1].rowCol = 7;
//   key[1].keyPressed = '2';

//   key[2].rowCol = 8;
//   key[2].keyPressed = '3';

//   key[3].rowCol = 9;
//   key[3].keyPressed = '+';

//   key[4].rowCol = 10;
//   key[4].keyPressed = '4';

//   key[5].rowCol = 11;
//   key[5].keyPressed = '5';

//   key[6].rowCol = 12 ;
//   key[6].keyPressed = '6';

//   key[7].rowCol = 13;
//   key[7].keyPressed = '-';

//   key[8].rowCol = 14;
//   key[8].keyPressed = '7';

//   key[9].rowCol = 15;
//   key[9].keyPressed = '8';

//   key[10].rowCol = 16;
//   key[10].keyPressed = '9';

//   key[11].rowCol = 17;
//   key[11].keyPressed = '*';

//   key[12].rowCol = 18;
//   key[12].keyPressed = '<';

//   key[13].rowCol = 19;
//   key[13].keyPressed = '0';

//   key[14].rowCol = 20;
//   key[14].keyPressed = '=';

//   key[15].rowCol = 21;
//   key[15].keyPressed = '/';

//   int i;
//   char pressedKeys[10];
//   int pKey = 0;
//   int index = n;
//   for(i=0;i<16;i++){
//     if(key[i].rowCol == (x)){
        
//         //pressedKeys[pKey] = key[i].keyPressed;
//         memset(pressedKeys[pKey],key[i].keyPressed,sizeof(char)*1);
//         pKey++;
//          //Serial.print(key[i].keyPressed);
//          if(key[i].keyPressed == '+'){
//              writeData('+');
//          }
//          else if(key[i].keyPressed == '-'){
//              writeData('-');
//          }
//          else if(key[i].keyPressed == '*'){
//              writeData('*');
//          }
//          else if(key[i].keyPressed == '/'){
//              writeData('/');
//          }
        
        
//         else if(key[i].keyPressed == '=' ){
//              for(i=0;i<10;i++){
//                   Serial.println(pressedKeys[i]);
//                 }
//             fnForCalculation(pressedKeys);
//         }else{
//             writeData(key[i].keyPressed);
//         }
       

//     }
    
//   }

 
// }

void fnForCalculation(char a[]){
//     Serial.println(a[1]);
//     char x[10] = {'0'};
//   int len = strlen(a);
//   int i;
//   for(i=0;i<(len-1);i++){
//    // x[i] == a[i];
//     memset(x[i],a[i],sizeof(char)*1);
    
//     //delay(100);
//   }

   //Identifying the operation to be performed using a substring of input

    if(strstr(a,"+")){

        fnToCalc(a,'+');
     }

    if(strstr(a,"-")){

        fnToCalc(a,'-');
    }

    if(strstr(a,"*")){
        fnToCalc(a,'*');
    }

    if(strstr(a,"/")){
       fnToCalc(a,'/');
    }


}

void fnToCalc(char x[],char opr){

    int len,oprIndex;
    len = (strlen(x));
    char arr1[10],arr2[10];

        int i;
        for(i=0;i<(len-1);i++){
            if(x[i] == opr){
                oprIndex = i;
            }
        }

        int arr2Index = 0;
        for(i=0;i<len;i++){

            if(i<oprIndex){
                arr1[i] = x[i];
            }
            else if(i>oprIndex){
                arr2[arr2Index] = x[i];
                arr2Index++;
            }
            else{
                continue;
            }
        }

        int firstLineLength = strlen(arr1) + strlen(arr2)+1;

        bool isValidInput = false;
        if(fnValidateInput(arr1,arr2)){
            isValidInput = true;
        }


        if((opr == '+') && isValidInput){
            fnAdd(arr1,arr2,firstLineLength);
        }
        else if((opr == '-') && isValidInput){
            fnSub(arr1,arr2,firstLineLength);
        }
        else if((opr == '*') && isValidInput){
            fnMul(arr1,arr2,firstLineLength);
        }
        else if((opr == '/') && isValidInput){
            fnDiv(arr1,arr2,firstLineLength);
        }
        else{
            print_answer("Invalid input\n",firstLineLength);
        }

}
/*
    function name: fnAdd
    input params: char arrays of operand 1 and 2
    output type: void

    Description: performs addition operation of each
    character of the array after converting to numbers.
    Carry is forwarded when looping from
    one digit to next digit.
*/



void fnAdd(char a[],char b[],int firstLineLength){
    int i,carry = 0;
    int len1 = strlen(a);
    int len2 = strlen(b);

    //looplength -- determining the number of iterations
    //using the length of the input arrays
    int loopLength;
    loopLength = len1>len2 ? len1:len2;

    //The added results is stored in 'result' array
    char result[20] = {'0'};

    //Index of the result array incremented after every looping
    int actualIndex = 0;

    //Digit-by-digit operations using the indexes of input arrays
    //To iterate through every element of input array, length
    //of the arrays are used
    int firstArrIndex = (len1-1);
    int secondArrIndex = (len2-1);

    while(loopLength>0){
        int x,y;
        //convert from character to integer
        x = a[firstArrIndex] - '0';
        y = b[secondArrIndex] - '0';

        //Negative index value signifies there is no array
        //value for that iteration in the array
        //Values of x or y are declared as zero
        if(firstArrIndex<0){
            x = 0;
        }
        if(secondArrIndex<0){
            y = 0;
        }

        //adding the two integers and carry from the previous operation
        int sum = x+y+carry;
        if(sum>9){
            carry = sum/10;
            sum   = sum%10;
        }
        //added value is converted back to character
        if(sum >0){
        result[actualIndex] = (sum+'0');
        }
        else{
         result[actualIndex] = (0+'0');
        }
        //Incrementing the index of the result array
        actualIndex++;
        //Decrementing the index values of the input arrays
        firstArrIndex--;
        secondArrIndex--;
        //Decrementing the value of 'n'th time iterated
        loopLength--;
    }

    //Identifying the first non-zero index to avoid preceding zeros
    //of the result array
    int firstNonZeroIndex;
    for(i=(actualIndex-1);i>=0;i--){
        if((result[i]-'0') != 0){
            firstNonZeroIndex = i;
            // printf("%d\n",i);
            break;
        }

    }

    // if(firstNonZeroIndex==(actualIndex-1)){
    //     firstNonZeroIndex = 0;
    // }

    //  int len = 5 ;

    // for(i=0;i<len;i++){
    //    movecursor();

    //     }

    //Adding carry to the result array
    //when the last operation is performed results in value
    //greater than 10
    if(carry >0 && (len1 == len2)){
        char firstDigit = (carry+'0');
        //printf("%c",firstDigit);
        writeData(firstDigit);
    }

    if((firstNonZeroIndex>0) && firstNonZeroIndex<(actualIndex-1)){
    for(i=(actualIndex-1);i>=firstNonZeroIndex;i--){

        writeData(result[i]);
    }
    }
    else if(firstNonZeroIndex==(actualIndex-1)){
        for(i=(actualIndex-1);i>=0;i--){

        writeData(result[i]);
    }
    }
    else{
        writeData(result[0]);
    }


}

/**
    function name: fnSub
    input params: char arrays of operand 1 and 2
    output type: void

    Description: performs difference operation of each
    character of the array after converting to numbers.
    Borrow is forwaded when looping from
    one digit to next digit.
**/

void fnSub(char a[],char b[],int firstLineLength){

    int i;

    //boolean value is set if borrow operation is performed
    bool isBorrowed = false;
    int len1 = strlen(a);
    int len2 = strlen(b);

    //looplength -- determining the number of iterations
    //using the length of the input arrays
    int loopLength;
    loopLength = (len1>=len2 ? len1:len2);

    int actualIndex = 0;

    char result[20] = {'0'};

    int firstArrIndex = (len1-1);
    int secondArrIndex = (len2-1);

    char mode;
    for(i=(loopLength-1);i>=0;i--){
        int x,y;
        int difference;
        x = a[firstArrIndex] - '0';
        y = b[secondArrIndex] - '0';

        if(firstArrIndex<0){
            x = 0;
        }
        if(secondArrIndex<0){
            y = 0;
        }


        //Determing the mode of operation
        //Negative mode : first number < second number
        //Positive mode : first number > second number
        //Mode is determined using length of the input
        //arrays and value of the last value of the digit of
        //the array

        int nth_positionA = a[0]-'0';
        int nth_positionB = b[0] - '0';

        if(len1>len2){
            x = isBorrowed ? (x-1):x;
            difference = x - y;
        }
        else if((len1 == len2) && (nth_positionA >= nth_positionB)){
            x = isBorrowed ? (x-1):x;
            difference = x - y;
        }

        else if((len1 == len2) && (nth_positionA < nth_positionB)){
            y = isBorrowed ? (y-1):y;
            difference = y - x;
            mode = '-';
        }
        else{
            y = isBorrowed ? (y-1):y;
            difference = y - x;
            mode = '-';
        }
        //In case of negative difference
        //equivalent positive value is obtained by adding '10' to
        //the value
        //setting the boolean value for borrow operation as 'true'
        if(difference<0){
            difference += 10;
            isBorrowed = true;
        }
        else{
            isBorrowed = false;
        }

        result[actualIndex] = difference + '0';
        actualIndex++;
        firstArrIndex--;
        secondArrIndex--;


    }

    int firstNonZeroIndex = -1;

    for(i=(actualIndex-1);i>=0;i--){
        if((result[i]-'0') != 0){
            //printf("%c\n",result[i]);
            firstNonZeroIndex = i;
            break;
        }

    }

    if(firstNonZeroIndex == (actualIndex-1)){
        firstNonZeroIndex = 0;
    }


    //   int len = 40-firstLineLength;
    
    // for(i=0;i<len;i++){
    //    movecursor();

    //     }

    if(mode=='-' && (firstNonZeroIndex>=0)){
        char negative = '-';
        
         for(i=(actualIndex-1);i>=(firstNonZeroIndex);i--){
        writeData(result[i]);
         }
    }

    else if(firstNonZeroIndex>=0){
        for(i=(actualIndex-1);i>=firstNonZeroIndex;i--){
        writeData(result[i]);
         }
    }
    else{
        char zero = '0';
        writeData(zero);
    }






}

/**
    function_name : fnMul
    input_params  : char arrays
    output_value  : void

    Description   : Multiplying digit by digit through iterating the
    input arrays. The result of each digit is maintained in a integer
    array
**/

void fnMul(char a[],char b[],int firstLineLength){

    int len1 = strlen(a),i;
    int len2 = strlen(b);

    int resultLen = len1+len2;
    int result[20]={0},j;
    //powerFactor -- Upon multiplication of each digit
    //The place value during addition of multiplied values
    //are adjusted using this integer value
    int powerFactor = 0,k=resultLen-1;
    int carry = 0;
    int ivalue, jvalue;

    //outer loop is iterated by more count than the innerloop
    //for multiplication in case of unequal length of arrays/numbers
    //The number of iterations are set using the length of the
    //input arrays
    //len1 - length of 1st array
    //len2 - length of 2nd array
    if(len1>len2){
        ivalue = len1;
        jvalue = len2;
    }
    else if(len1 == len2){
        ivalue = len1;
        jvalue = len2;
    }
    else{
        ivalue = len2;
        jvalue = len1;
    }
    int finalCarry = 0;
    int placeCarry[10] = {0};
    for(i=(ivalue-1);i>=0;i--){


        for(j=(jvalue-1);j>=0;j--){
            int x,y;

            if(len1<len2){
                x = b[i] - '0';
                y = a[j] - '0';
            }
            else{
                x = a[i]-'0';
                y = b[j]-'0';
            }




            int mul = (x*y)+placeCarry[k];
            placeCarry[k] = 0;



            result[k] += (mul);




            if(result[k]>9){
                placeCarry[k-1] +=result[k]/10;
                result[k] = result[k]%10;
            }
            k--;
            if(k==0){
                finalCarry +=placeCarry[0];
            }
        }
        //powerfactor is incremented on looping through the successive
        //digit of the multiplicand
        powerFactor++;

        //Index value of result array calculated by difference value of
        //length of result array and powerfactor
        k = resultLen - powerFactor - 1;
    }


    int firstNonZeroIndex = -1;
    for(i=0;i<resultLen;i++){

        if(result[i] != 0){
            firstNonZeroIndex = i;
            break;
        }

    }

    
    // int len = 40-firstLineLength;
    
    // for(i=0;i<len;i++){
    //    movecursor();

    //     }

    if(finalCarry>0 ){
        writeData(finalCarry);
    }
    if(firstNonZeroIndex>=0){
        for(i=firstNonZeroIndex;i<resultLen;i++){
        writeData(result[i]);
        }
    }
    else{
        char a = '0';
        writeData(a);
    }

}
/**
    function_name: fnToNum
    input_params : char array
    output_type  : long integer

    description: Convert the string of numbers to
    a long integer value
**/

long fnToNum(char arr[]){
    long num = 0;
    int i;

    int len=0;
    //Determing length of array of numbers
    for(i=0;;i++){
        if(!arr[i]){
            break;
        }
        len++;
    }

    //On every iteration previous value is multiplied by 10
    //and adding the new value to set the correct place values
    //of final integer
    for(i=0;i<len;i++){
        int temp = (arr[i]-'0');
        num = (num*10)+temp;
    }

    return num;
}

/**
    function_name: fnDiv
    input_params : char arrays
    output_type  : void

    Description  : performs division operation looping through
    the each digit of the dividend and comparing it with divisor.
    Based on this comparision, quotient is incremented
**/


void fnDiv(char dividend[],char divsor[],int firstLineLength){

    long divisor = fnToNum(divsor);

    static char quotient[10];
    long temp=0;
    int i=0,j=0;

    while(dividend[i]){

         temp = temp*10 + (dividend[i] -'0');
         if(temp<divisor){

             quotient[j++] = '0';

         }
         else{
             quotient[j++] = (temp / divisor) + '0';
             temp = temp % divisor;

         }
         i++;
    }

    quotient[j] = '\0';
    long remainder = temp;




    

    int firstNonZeroIndex;
    for(i=0;i<=j;i++){
        if(quotient[i] != '0'){
            firstNonZeroIndex = i;
            break;
        }

    }

    // int len = 40-firstLineLength;

    // for(i=0;i<len;i++){
    //    movecursor();

    //     }

     for(i=firstNonZeroIndex;i<j;i++){
        writeData(quotient[i]);
    }

    for(i=0;i<2;i++){
       movecursor();

        }


    
    if(remainder>0){
        char remainderArr[10] = {'0'};
        long num = remainder;
        int index = 0;
        while(num>0){
            int digit = num%10;
            remainderArr[index] = digit+'0';
            long temp = num/10;
            num = temp;

        }

        for(i=10;i>=0;i--){
            if(remainderArr[i]){
                writeData(remainderArr[i]);
            }
        }

    }
    

}