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

#include<LiquidCrystal_I2C.h>
#include <ArduinoJson.h>
#include "ansi.h"


#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>
#include <BLE2902.h>
 
BLEServer *pServer = NULL;
BLECharacteristic * pTxCharacteristic;
bool deviceConnected = false;
bool oldDeviceConnected = false;
uint8_t txValue = 0;
 
// See the following for generating UUIDs:
// https://www.uuidgenerator.net/
 
#define SERVICE_UUID           "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" // UART service UUID
#define CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
#define CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"
 
 
class MyServerCallbacks: public BLEServerCallbacks {
    void onConnect(BLEServer* pServer) {
      deviceConnected = true;
    };
 
    void onDisconnect(BLEServer* pServer) {
      deviceConnected = false;
    }
};
 
class MyCallbacks: public BLECharacteristicCallbacks {
    void onWrite(BLECharacteristic *pCharacteristic) {
     // std::string rxValue = pCharacteristic->getValue();
      String rS= pCharacteristic->getValue();
      std::string rxValue =rS.c_str();

      if (rxValue.length() > 0) {
        Serial.println("*********");
        Serial.print("Received Value: ");
        for (int i = 0; i < rxValue.length(); i++)
          Serial.print(rxValue[i]);
 
        Serial.println();
        Serial.println("*********");
      }
    }
};
 

ANSI ansi(&Serial);


 StaticJsonDocument<1200> sumData;

#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


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

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

LiquidCrystal_I2C LCD = LiquidCrystal_I2C(0x27, lcdColumns, lcdRows); 

const int dataPin595 = 4;   /* DS */
const int clockPin595 = 17;  /* SHCP */
const int latchPin595 = 16;  /* STCP */

const int dataPin165 = 5;   /* Q7 */
const int clockPin165 = 2;  /* CP */
const int latchPin165 = 15;  /* PL */


void setup() {
   Serial.begin(115200);
  sumData["version"]=1;
  sumData["tamper"]=1;

  
  Serial.println("Welcome to  ESP32-S3");
  Serial.println("===============================");
  serializeJson(sumData, Serial);

  LCD.init();
  LCD.backlight();
  LCD.setCursor(0, 0);
  LCD.print("Welcome to Entry!");
  LCD.setCursor(0, 1);
  LCD.print("at your disposal! ");
 
  pinMode(dataPin595, OUTPUT);
  pinMode(clockPin595, OUTPUT);
  pinMode(latchPin595, OUTPUT);

  pinMode(dataPin165, INPUT);
  pinMode(clockPin165, OUTPUT);
  pinMode(latchPin165, OUTPUT);
  //---------------------------------------
    // Create the BLE Device
    LCD.setCursor(0, 2);
  LCD.print("pre A BLE setup");
  BLEDevice::init("UART Service For ESP32");
 
  LCD.setCursor(0, 2);
  LCD.print("pre BLE setup");
  // Create the BLE Server
  pServer = BLEDevice::createServer();
  pServer->setCallbacks(new MyServerCallbacks());
  LCD.setCursor(0, 2);
  LCD.print("pre 0 BLE setup");
  // Create the BLE Service
  BLEService *pService = pServer->createService(SERVICE_UUID);
 
  // Create a BLE Characteristic
  pTxCharacteristic = pService->createCharacteristic(
                                        CHARACTERISTIC_UUID_TX,
                                        BLECharacteristic::PROPERTY_NOTIFY
                                    );
                      
  pTxCharacteristic->addDescriptor(new BLE2902());
 
  BLECharacteristic * pRxCharacteristic = pService->createCharacteristic(
                                             CHARACTERISTIC_UUID_RX,
                                            BLECharacteristic::PROPERTY_WRITE
                                        );
 
  pRxCharacteristic->setCallbacks(new MyCallbacks());
 
 
  // Start the service
  pService->start();
 
  // Start advertising
  pServer->getAdvertising()->start();
  Serial.println("Waiting a client connection to notify...");

  LCD.setCursor(0, 3);
  LCD.print("After BLE setup");
}

int pattern = 0b10101010;
unsigned long timer_ms = 0;
int pulseWidth165=10;
int oldOptionSwitch = 0;   // previous state of all the inputs
//------------------------------------
void loop() {
  
  Serial.println("In the Loop");
char key = keypad.getKey();
if (key) {

Serial.print("the key is ");
Serial.println(key);
LCD.setCursor(0, 3);
     
LCD.print(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
}

  digitalWrite( latchPin165, LOW);    
  delayMicroseconds( pulseWidth165);
  digitalWrite( latchPin165, HIGH);

  // Reading one 74HC165 at a time and combining them into a 32 bit variable
  // The last 74HC165 is at the bottom, but the switches start numbering
  // at the top. So the first byte has to be shifted into the highest place.
  uint32_t optionSwitch = 0;
 
 //   optionSwitch = ((uint32_t) ReadOne165());
 for( int i=16; i>=0; i-=8)
  {
    optionSwitch |= ((uint32_t) ReadOne165()) << i;
  }

if(oldOptionSwitch!=optionSwitch)
 {
  pattern=oldOptionSwitch=optionSwitch;
  LCD.setCursor(0, 2);
  char s [20];
  sprintf (s, " %02x", pattern);
  LCD.print(s);
 }

 if (deviceConnected) {
        pTxCharacteristic->setValue(&txValue, 1);
        pTxCharacteristic->notify();
        txValue++;
       delay(10); // bluetooth stack will go into congestion, if too many packets are sent
    }
 
    // disconnecting
    if (!deviceConnected && oldDeviceConnected) {
        delay(500); // give the bluetooth stack the chance to get things ready
        pServer->startAdvertising(); // restart advertising
        Serial.println("start advertising");
        oldDeviceConnected = deviceConnected;
    }
    // connecting
    if (deviceConnected && !oldDeviceConnected) {
        // do stuff here on connecting
        oldDeviceConnected = deviceConnected;
    }

}

byte ReadOne165()
{
  byte ret = 0x00;

  // The first one that is read is the highest bit (input D7 of the 74HC165).
  for( int i=7; i>=0; i--)
  {
    if( digitalRead( dataPin165) == HIGH)
      bitSet( ret, i);

    digitalWrite( clockPin165, HIGH);
    delayMicroseconds( pulseWidth165);
    digitalWrite( clockPin165, LOW);
  }

  return( ret);
}
74HC595
74HC165
74HC165