#include"global.h"
//-----------------RFID-----------------------
#define rfid 1

#if rfid
#include <MFRC522v2.h>
//i2c #include <MFRC522DriverI2C.h>
#include <MFRC522DriverSPI.h>
#include <MFRC522DriverPinSimple.h>

#include <MFRC522Debug.h>
#endif

#include <Wire.h>
//#include <string>
#include "ansi.h"
#include"TickTwo.h"


#include <WiFi.h>
#include <ShiftRegister74HC595.h>

#include "DHT.h"
#include <Keypad.h>

bool initPhase=true;
int keyboardStage=0; //0:out, 1:in getcode
int Npos=0;
int nch=0;
char vin[10];

const int PIR_PIN = 34;

#define  DHT_SENSOR_TYPE DHT22
#define  DHT_SENSOR_PIN   12

DHT dht_sensor(DHT_SENSOR_PIN, DHT_SENSOR_TYPE);


#if rfid
/*
Signal	  MFRC522	WROOM-32
RST/Reset	RST	    21
SPI SS	  SDA	    5
SPI MOSI	MOSI	  23
SPI MISO	MISO	  19
SPI SCK	  SCK	    18
*/
//https://github.com/OSSLibraries/Arduino_MFRC522v2/blob/master/examples/CustomI2C/CustomI2C.ino

/*i2c const uint8_t customAddress = 0x28;
TwoWire &customI2C = Wire;
MFRC522DriverI2C driver{customAddress, customI2C}; // Create I2C driver.
MFRC522 mfrc522{driver}; // Create MFRC522 instance.
i2c*/

MFRC522DriverPinSimple ss_pin(10); // Create pin driver. See typical pin layout above.
SPIClass &spiClass = SPI; // Alternative SPI e.g. SPI2 or from library e.g. softwarespi.
const SPISettings spiSettings = SPISettings(SPI_CLOCK_DIV4, MSBFIRST, SPI_MODE0); // May have to be set if hardware is not fully compatible to Arduino specifications.
MFRC522DriverSPI driver{ss_pin, spiClass, spiSettings}; // Create SPI driver.
MFRC522 mfrc522{driver}; // Create MFRC522 instance.
#endif
// =====================Buzzer notifications functions=====================
const int buzPin = 23;


void keyBeep(){
	tone(buzPin, 494,26);
 
	//tone1.play(NOTE_G4);
}

void keyBeep2(){
	tone(buzPin, 694,50);
}

void BeepDecline(){
  tone(buzPin, 1494,20);
	//delay(500);
	tone(buzPin, 494,20);
  tone(buzPin, 1494,20); //294
}

void BeepDZone(){
  tone(buzPin, 1494,20);
	//delay(500);
	tone(buzPin, 494,20);
}
//--------------------------------------------------------------
const char* ssid     =  "Wokwi-GUEST";// "EagleEye";
const char* password = "";//"pkap1234!";
// See the following for generating UUIDs:
// https://www.uuidgenerator.net/
ANSI ansi(&Serial);

StaticJsonDocument<2048> clientData;
StaticJsonDocument<2048> fromUNIT;

#define ROWS  4
#define COLS  4
 
char keyMap[ROWS][COLS] = {
  {'1','2','3', 'A'},
  {'4','5','6', 'B'},
  {'7','8','9', 'C'},
  {'*','0','#', 'D'}
};
 
uint8_t rowPins[ROWS] = {14, 27, 26, 25}; // GIOP14, GIOP27, GIOP26, GIOP25
uint8_t colPins[COLS] = {33, 32, 18, 19}; // GIOP33, GIOP32, GIOP18, GIOP19

Keypad keypad = Keypad(makeKeymap(keyMap), rowPins, colPins, ROWS, COLS );

// set the LCD number of columns and rows
int lcdColumns = 20;
int lcdRows = 4;

LCD_I2C  LCD(0x27, lcdColumns, lcdRows); 

ShiftRegister74HC595<2> sr(4, 17, 16);
//===============================================
#define NTP_SERVER     "pool.ntp.org"
#define UTC_OFFSET     120
#define UTC_OFFSET_DST 60               // 1 hour daylight savings.

int MAX_WIFI_RETRIES= 20;
//===================================================
void updateLCDBacklight()
{
  LCD.noBacklight();
}
TickTwo timerUPDbak(updateLCDBacklight, 15000, 1);  //15000
//--------------------------------------------------
void chkPIR()
{
  int val=digitalRead(PIR_PIN);
 
  if(clientData["pir"]["St"]!=val)
  {
     ansi.println("PIR:"+String(val)+"  Old:"+String(clientData["pir"]["St"]));
     clientData["pir"]["oSt"]=clientData["pir"]["St"];
     clientData["pir"]["St"]=val;
     clientData["change"]=1;
    if(val==1)
    {
     LCD.backlight();
     timerUPDbak.start();
    }
  }
}
TickTwo  timerPIR(chkPIR,3000);
//---------------------------------------------------
void chkDHT()
{
  float temperature = dht_sensor.readTemperature();
  float humidity= dht_sensor.readHumidity();
  
      if(clientData["dht"]["Temp"]!=temperature || clientData["dht"]["Hum"]!=humidity)
      {
        ansi.println("DHT "+String(clientData["dht"]["Temp"])+" "+String(temperature)+" "+String(clientData["dht"]["Hum"])+" "+String(humidity));

        clientData["dht"]["oTemp"]=clientData["dht"]["Temp"];
        clientData["dht"]["oHum"]=clientData["dht"]["Hum"];
        clientData["dht"]["Temp"]=temperature;
        clientData["dht"]["Hum"]=humidity;
        clientData["change"]=1;
        if(!initPhase)
          updateLCD(1);
      }  
}
TickTwo  timerDHT(chkDHT,5000);
//-------------------------------------------------------------

TickTwo timerDZONEbeep(BeepDZone, 500, 35); 
//--------------------------------------------------
//=================================================================
//========================setup====================================
//=================================================================
void setup() {
   Serial.begin(115200);
   initLCD();
 //rtc.setTime(30, 24, 15, 29, 11, 2024);  // 17th Jan 2021 15:24:30
   ansi.clearScreen();
   ansi.println("ALARM Control SYSTEM");
     
    
  LCD.setCursor(0,2);
  LCD.print("NET Connecting.");
  WiFi.begin(ssid, password);
  int i=0;
  while ((WiFi.status() != WL_CONNECTED)&& (i<MAX_WIFI_RETRIES)) {
    delay(500);
    ansi.print(".");
    LCD.print(".");
    i++;
  }
  LCD.setCursor(0,3);
  LCD.print("=OK= Connected!");
  delay(1500);
  LCD.clear();
  
  pinMode(buzPin, OUTPUT);
  pinMode(PIR_PIN,INPUT);

  ansi.println("===============================0");
  chkPIR();
  chkDHT();
  ansi.println("===============================1");

  initEspData(false);
   //==========================================
   //configTime(UTC_OFFSET, UTC_OFFSET_DST, NTP_SERVER);
   ansi.println("===============================2");
   configTime(UTC_OFFSET*60, 60*UTC_OFFSET_DST, NTP_SERVER);
   ansi.println(String("===============================3 ")+String(millis()));
  
  getUnitData(false);
  ansi.println(String("===============================4 ")+String(millis()));

   updateLCD(99);
   ansi.println(String("===============================5 ")+String(millis()));

  timerPIR.start();
  timerDHT.start();
  timerUPDbak.start();
 
}
//---------------------------------------------------------------------
//------------------------------------------------------------------------
void handleKeyPressNew(char key)
{
    // if(key==NO_KEY)
    //    return;
 
     LCD.backlight();
     timerUPDbak.start();
 
  if(!keyboardStage)
  {
    keyBeep2();

   if(key=='A' || key=='B' || key=='C')
   {
    char *buffcl=": Code:[----]";
    char *lblaction[]={"ARM ON ","ARM IN ","ARM OFF"};

    if((key=='A' && fromUNIT["state"]=="ARMED")||
    (key=='B' && fromUNIT["stateIN"]==1)||
    (key=='C' && fromUNIT["state"]!="ARMED"))
    {
      BeepDecline();
      return;
    }
    LCD.setCursor(0,1);
    LCD.print("--------------------");
    LCD.setCursor(0,3);
    LCD.print("--------------------");
    LCD.setCursor(0,2);
    LCD.print(String(lblaction[key-'A'])+String(buffcl));
    LCD.setCursor(15,2);
    LCD.cursor();
    LCD.backlight();
    
    keyboardStage=1;
    clientData["kbKey"]=String(key);
    nch=0;
    Npos=15;
    memset(vin,0,sizeof(vin));
   }
   else  if(key=='D'){
     BeepDecline();
  }
   else  if(key=='4')
   { 
     prJSON(clientData,"clientData"); 
     prJSON(fromUNIT,"fromUNIT"); 
  }
  else if(key=='1')
  {
    BeepDZone();
  }
  }
  else
  {
     
    
    keyBeep();
    	
    if(key=='*')
     {
       if(nch==0)
       {
         keyboardStage=0;
       }
       else
       {
        nch--;
        vin[nch]=NULL;
        LCD.setCursor(Npos+nch,2);
        LCD.print("-");
        LCD.setCursor(Npos+nch,2);
      }
     }
     else{
    vin[nch]=key;
    LCD.setCursor(Npos+nch,2);
		LCD.print('*');	
    nch++;
    if(nch==4)
    {
       keyboardStage=0;
       vin[4]=NULL;
    }
     }
    if(keyboardStage==0)
    {
    String psw=String(vin);
    if(psw!=NULL)
    {
      clientData["change"]=1;
      clientData["kbdata"]=psw;
    }
    ansi.println("The Code is:["+psw+"]");
    LCD.noCursor();
    updateLCD(99);
   
    timerUPDbak.start();
    }

  } 
}
//--------------------------------------------------------------
void dummuCS_ResponseApply();
int ka=0;
unsigned long prmil=0;

//--------------------------------------------------------------
//--------------------------------------------------------------
void loop() 
{
 if(initPhase)
 {
  initPhase=false;
  chkPIR();
  chkDHT();
 }
  /*if(ka==0)
     ansi.println("In the Loop H:");//+String(hour())+" M:"+String(minute()));
  else if(ka==105)
     ka=0;
  ka++;
  ansi.print("In the Loop H:");
  ansi.println(millis()-prmil);
  prmil=millis();
  delay(1000);*/
  if(millis()-prmil>999)
  {
    prmil=millis();
    updateLCD(10);
  }

  timerPIR.update();
  timerUPDbak.update();
  timerDHT.update();

  timerDZONEbeep.update();
  if(timerDZONEbeep.counter()==19)
     timerDZONEbeep.interval(250);
 
     
 
char key = keypad.getKey();
if (key) {
  Serial.print("the key is ");
  Serial.println(key);
   
    //handleKeyPress(key);
    handleKeyPressNew(key);
}

/*
if(millis()-timer_ms>300)
{
  digitalWrite(latchPin595, LOW);
  shiftOut(dataPin595, clockPin595, MSBFIRST, pattern);
  digitalWrite(latchPin595, HIGH);
  timer_ms=millis();
 // pattern++;// = ~pattern; // Invert the pattern
}
*/
/*
 // setting all pins at the same time to either HIGH or LOW
  sr.setAllHigh(); // set all pins HIGH
  delay(500);
  
  sr.setAllLow(); // set all pins LOW
  delay(500); 
   */

   /*
  // setting single pins
  for (int i = 0; i <16; i++) {
      sr.set(i, HIGH); // set single pin HIGH
    delay(80);   
  }
 
 for (int i = 16; i>=0; i--) {
      sr.set(i, LOW); // set single pin HIGH
    delay(80); 
  }*/
  
  // tone(buzPin, 1000,200); // Send 1KHz sound signal...
  //delay(1000);        // ...for 1 sec

  //doubleBeep();
  //delay(500); 
  if(clientData["change"]==1)
  {
    Serial.println(String("Serve the client changes!")+String(clientData["kbKey"]));
    clientData["trns"]=int(clientData["trns"])+1;
    dummuCS_ResponseApply();

    clientData["change"]=0;
  }

  HandleCSResponse(); 
}
//----------------------------------------------------------------------------------------------
void HandleCSResponse()
{
if(fromUNIT["toServe"]==1)
  {
   
     Serial.println(String("Actions from reply!:")+String(fromUNIT["state"])+String(" action:")+String(fromUNIT["action"]));
     fromUNIT["toServe"]=0;
     if(fromUNIT["action"]=="DZONEBEEP")
     {
      fromUNIT["action"]="IDLE";
      
      if(timerDZONEbeep.state()!=RUNNING)
      {
        
        timerDZONEbeep.stop();
        timerDZONEbeep.start();
        timerDZONEbeep.interval(500);
        LCD.backlight();
      }
     }
     else
     {
      if(timerDZONEbeep.state()==RUNNING)
      {
        timerDZONEbeep.stop();
      }
     }
     updateLCD(0);
   } 
}
//----------------------------------------------------------------------------------------------
void dummuCS_ResponseApply()
{
  fromUNIT["trns"]=clientData["trns"];
  fromUNIT["toServe"]=1;

  if(clientData["kbKey"]=="A" && clientData["kbdata"]=="8808")
  {
 
      fromUNIT["state"]="ARMED";
      fromUNIT["action"]="DZONEBEEP";
      fromUNIT["stateIN"]=0;
   }
   if(clientData["kbKey"]=="B" && clientData["kbdata"]=="8808")
   {
      fromUNIT["state"]="ARMED";
      fromUNIT["action"]="DZONEBEEP";
      fromUNIT["stateIN"]=1;
    }
    if(clientData["kbKey"]=="C" && clientData["kbdata"]=="8808")
   {
       fromUNIT["state"]="IDLE";
      fromUNIT["action"]="IDLE";
      fromUNIT["stateIN"]=0;
   }
  else
  Serial.println("Response make NOT A");

  clientData["kbKey"]=="-";
   
}
74HC595
74HC595
Arm