#include <Arduino.h>
#include <freertos/task.h>
#include <WiFiManager.h>            //WiFi
#include <MFRC522.h>                //MFRC522 (RFID Reader)
#include <Keypad.h>                 //keypad library
#include <LiquidCrystal_I2C.h>      //LCD Library
#include <Firebase_ESP_Client.h>    //Firebase library (might not use after demo)
#include "addons/TokenHelper.h"

//keypad initialization
const byte ROWS = 4; //four rows
const byte COLS = 4; //three columns
char keys[ROWS][COLS] = {
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'*','0','#','D'}
};
byte rowPins[ROWS] = {26, 25, 33, 32}; //connect to the row pinouts of the kpd
byte colPins[COLS] = {13, 12, 14,27}; //connect to the column pinouts of the kpd
Keypad kpd = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS ); //keypad object

TaskHandle_t xhandlemode;
TaskHandle_t xhandletopup;
int nilai;
char mode;

//Firebase (not used after demo)
#define API_KEY "AIzaSyCNJugv3_95Fwq36fm9lmyRuho2sJ8OOM8"
#define DATABASE_URL "https://smartdoorlock-40077-default-rtdb.asia-southeast1.firebasedatabase.app/" 
FirebaseData fbdo;
FirebaseData fbda;
FirebaseJson json;
FirebaseAuth auth;
FirebaseConfig config;
bool signupOK = false;

//LCD
#define I2C_SDA 21
#define I2C_SCL 22
LiquidCrystal_I2C lcd(0x27, 16, 2);

//MFRC522
#define RST_PIN         15          
#define SS_PIN          5    
MFRC522 mfrc522(SS_PIN, RST_PIN); 
MFRC522::MIFARE_Key key;  
char uid[10];

void printHex(byte *buffer, byte bufferSize) {
  for (byte i = 0; i < bufferSize; i++) {
    Serial.print(buffer[i] < 0x10 ? " 0" : " ");
    Serial.print(buffer[i], HEX);
  }
}

//Upload Data
void kirim_data(void * parameters)
{
    int data_f;
    for(;;)
    {
        if (Firebase.ready() && signupOK ){
            char find[50];
            sprintf(find,"/kasir/%s",uid);
            Serial.println(find);
            if (Firebase.RTDB.getInt(&fbdo, find)) {
                Serial.println("Datafetched");
                Serial.println(fbdo.to<int>());
            } else {
                Serial.println(fbdo.errorReason());
            }
            switch (mode)
            {
            case 'a':
                data_f = fbdo.to<int>() + nilai;
                Serial.print(data_f);
                break;
            case 'b':
                data_f = fbdo.to<int>() - nilai;
                break;
            default:
                data_f = fbdo.to<int>();
                lcd.clear();
                lcd.setCursor(0,0);
                lcd.print(data_f);
                vTaskDelay(2000/portTICK_PERIOD_MS);
                vTaskResume(xhandlemode);
                vTaskDelete(NULL);
                break;
            }
            
            if (data_f<0)
            {
                lcd.clear();
                lcd.print("Saldo tdk cukup");
                lcd.setCursor(0,1);
                lcd.print("Saldo:");
                lcd.setCursor(7,1);
                lcd.print(fbdo.to<int>());
                vTaskDelay(2000/portTICK_PERIOD_MS);
            } else
            {
                    if (Firebase.RTDB.set(&fbdo,find,data_f))
                {
                    Serial.println("PASSED");
                    lcd.clear();
                    lcd.setCursor(0,0);
                    lcd.print("Done");
                    lcd.setCursor(0,1);
                    lcd.print("Saldo:");
                    lcd.setCursor(6,1);
                    lcd.print(data_f);
                    vTaskDelay(2000/portTICK_PERIOD_MS);
                }
                else 
                {
                    Serial.println("FAILED");
                    Serial.println("REASON: " + fbdo.errorReason());
                    lcd.clear();
                    lcd.setCursor(0,0);
                    lcd.print("Failed");
                    lcd.setCursor(0,1);
                    lcd.print("Please try again");
                    vTaskDelay(2000/portTICK_PERIOD_MS);
                }
                
            }

            vTaskResume(xhandlemode);
            vTaskDelete(NULL);
            
            
            
            

        }
    }
}

//Scan Kartu
void baca_kartu(void * parameters)
{
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("Scan Your Card!");
  for(;;){
    if ( ! mfrc522.PICC_IsNewCardPresent()) {
    
	}

	// Select one of the cards
	if ( ! mfrc522.PICC_ReadCardSerial()) {

    } 
    else
    {
        sprintf(uid, "%d %d %d %d", mfrc522.uid.uidByte[0],mfrc522.uid.uidByte[1],mfrc522.uid.uidByte[2],mfrc522.uid.uidByte[3]);
        Serial.print("Uid is:");
        printHex(mfrc522.uid.uidByte, mfrc522.uid.size);
        //lcd section
        lcd.clear();
        lcd.setCursor(0,0);
        lcd.print("Processing..");
        lcd.setCursor(0,1);
        lcd.print(uid);
        Serial.println();
        Serial.println();
        mfrc522.PICC_HaltA();
        mfrc522.PCD_StopCrypto1();
        xTaskCreate(kirim_data,"topup",10000,NULL,1,NULL);
        vTaskDelete(NULL);
    }
  
  }
}

//Verifikasi
void verifikasi(void * parameters)
{
    TaskHandle_t passedhandler;
    passedhandler = (TaskHandle_t) parameters;
    Serial.println("Masuk verifikasi");
    lcd.clear();
    lcd.setCursor(0,0);
    if (mode=='a')
    {
        lcd.print("Isi:");
        lcd.setCursor(4,0);
    }
    else
    {
        lcd.print("Potong:");
        lcd.setCursor(7,0);
    }
    lcd.printf("%d ?",nilai);
    lcd.setCursor(0,1);
    lcd.print("A.Yes B.No");

    for(;;)
    {
        if(kpd.getKeys())
        {
            for(int i=0; i<LIST_MAX; i++)
            {
                if (kpd.key[i].kstate == PRESSED)
                {
                    switch (kpd.key[i].kchar)
                    {
                    case 'A':
                        Serial.println("Go to scan data"); 
                        xTaskCreate(baca_kartu,"Baca kartu untuk topup",2000,NULL,1,NULL);
                        vTaskDelete(passedhandler);
                        vTaskDelete(NULL);
                        break;
                    case 'B':
                        Serial.println("Mundur");
                        vTaskResume(passedhandler);
                        vTaskDelete(NULL);
                        break;
                    
                    default:
                        Serial.print("Ini verifikasi");
                        break;
                    }
                }
                
            }
        }
    }
}

//TopUp
void vTopUpTask(void *pvParameters)
{
    int c = 0;
    int cur;
    char tulisan[10];
    switch (mode)
    {
    case 'a':
        sprintf(tulisan,"IsI:");
        cur=4;
        break;
    case 'b':
        sprintf(tulisan,"Potong:");
        cur=7;
        break;
    default:
        break;
    }
    
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print(tulisan);
    lcd.setCursor(cur,0);
    lcd.print(nilai);
    lcd.setCursor(0,1);
    lcd.print("A.Ok B.Clr C.<-");
    Serial.printf("%s %d\n", tulisan, nilai);
    for (;;)
    {
        vTaskDelay(200/portTICK_PERIOD_MS);
        if (kpd.getKeys())
        {
            for (int i=0; i<LIST_MAX; i++)   // Scan the whole key list.
            {
                if ( kpd.key[i].kstate == PRESSED )   // Only find keys that have changed state.
                {
                    if (c<7 && kpd.key[i].kchar>='0' && kpd.key[i].kchar <='9')
                        {
                            nilai = nilai * 10 + atoi(&kpd.key[i].kchar);
                            c=c+1;
                            Serial.printf("%s ",tulisan);
                            char str[10];
                            sprintf(str, "%d\n", nilai);
                            Serial.print(str);
                            lcd.setCursor(cur,0);
                            lcd.print(str);
                            Serial.println(c);
                        }
                    else
                        {
                            switch (kpd.key[i].kchar)
                                {
                                case 'A':
                                    Serial.println("Verifikasi"); //verivikasi -> kirimdata
                                    xTaskCreate(verifikasi,"verifikasi topup",1500,xhandletopup,1,NULL);
                                    vTaskSuspend(NULL);
                                    break;
                                case 'B':
                                    nilai=0;
                                    c=0;
                                    Serial.printf("%s 0\n",tulisan);
                                    lcd.clear();
                                    lcd.setCursor(0,0);
                                    lcd.printf("%s",tulisan);
                                    lcd.setCursor(cur,0);
                                    lcd.print(nilai);
                                    lcd.setCursor(0,1);
                                    lcd.print("A.Ok B.Clr C.<-");
                                    break;
                                case 'C':
                                    Serial.print("Bye\n");
                                    vTaskResume(xhandlemode);
                                    vTaskDelete(NULL);
                                    break;
                                
                                default:
                                    Serial.print("mleset bro");
                                    break;
                                }
                        }
                } 
                
            }
        }
        
    }
}

//ModeControlTask
void vModeControl (void *pvParameters)
{
    bool s=true;
    for(;;)
    {
        if(s==true){
            Serial.print("Welcome.. Option:\n");
            Serial.println("[A] Top up");
            Serial.println("[B] Potong Saldo");
            Serial.println("[C] Cek Saldo");
            lcd.clear();
            lcd.setCursor(0,0);
            lcd.print("Choose: C.Check");
            lcd.setCursor(0,1);
            lcd.print("A.Top-up B.Pay");
            nilai=0;
            s=false;
        }else
        {
            if (kpd.getKeys())
            {
                for (int i=0; i<LIST_MAX; i++)   // Scan the whole key list.
                {
                    if ( kpd.key[i].kstate == PRESSED)   // Only find keys that have changed state.
                    {
                        switch (kpd.key[i].kchar)
                            {
                            case 'A':
                                mode='a';
                                xTaskCreate(vTopUpTask,"topup",2500,NULL,1,&xhandletopup);
                                s=true;
                                vTaskSuspend(NULL);
                                break;

                            case 'B':
                                mode='b';
                                xTaskCreate(vTopUpTask,"potong",2500,NULL,1,&xhandletopup);
                                s=true;
                                vTaskSuspend(NULL);
                                break;

                            case 'C':
                                mode='c';
                                xTaskCreate(baca_kartu,"bacakartu",2000,NULL,1,NULL);
                                s=true;
                                vTaskSuspend(NULL);
                                break;

                            default:
                            
                                break;
                            }
                    }
                }
            }
        }
    }
}



void setup() {
    Serial.begin(9600);

    //MFRC Setup
    while (!Serial);		
	SPI.begin();			
	mfrc522.PCD_Init();		
	delay(4);				
	mfrc522.PCD_DumpVersionToSerial();	
	for (byte i = 0; i < 6; i++) {
    	key.keyByte[i] = 0xFF;
    }

    //LCD setup
    lcd.init(I2C_SDA, I2C_SCL); 
	lcd.backlight();

    //WiFi setup
    WiFiManager wm;
    bool done;
    lcd.setCursor(0,0);
    lcd.print("Please Connect :");
    lcd.setCursor(0,1);
    lcd.print("to Alat Kasir");
    done=wm.autoConnect("Kasir","password");
    Serial.println();
    lcd.clear();
    lcd.setCursor(0,0);
    Serial.print("Connected to : ");
    lcd.print("Connected to : ");
    Serial.println(wm.getWiFiSSID());
    lcd.setCursor(0,1);
    lcd.print(wm.getWiFiSSID());
    delay(5000);
    Serial.println();

    // Firebase (only in demo)
    config.api_key = API_KEY;
    config.database_url = DATABASE_URL;

    while (!Firebase.signUp(&config, &auth, "", "")){
        Serial.printf("%s\n", config.signer.signupError.message.c_str());
        Firebase.signUp(&config, &auth, "", "");
    }
    Serial.println("ok");
    signupOK = true;

    /* Assign the callback function for the long running token generation task */
    config.token_status_callback = tokenStatusCallback; //see addons/TokenHelper.h
    Firebase.begin(&config, &auth);
    Firebase.reconnectWiFi(true);
        
    xTaskCreate(vModeControl,"controlmode",1500,NULL,0,&xhandlemode);
}


void loop() {
}  