  /*  ===============================================
   *        - THIS IS TAKEONE ESP32 BOARD -
   *  ===============================================
   *  MPU9250 Address     : 0X69 (ADDRESS)
   *  OLED 128x64 Address : 0X3C (ADDRESS)
   *  -----------------------------------------------
   *  Description         : PIN GPIO#
   *  -----------------------------------------------
   *  NEOPIXEL LED STRIP  : A0/GPIO 26
   *  PIEZO SENSOR        : A2/GPIO 34 - ANALOG ONLY INPUT
   *  RANDOM SEED         : A3/GPIO 39 - ANALOG ONLY INPUT
   *  BUTTONS             : TLB (35), BLB (15), CB (33), BRB (27), TRB(12)
   *  I2C PIN             : SDA 21, SCL 22 (DIGITAL)
   *  SPI PIN             : CS 5, SCK 18, MISO 19, MOSI 23 (DIGITAL)
   *  BOOST CONTROL PIN   : 14 (DIGITAL) OUTPUT ON PADS (TP1 +5V, TP2 GND)
   *  LED PIN             : 13 (DIGITAL)
   *  BATTERY MONITOR PIN : 35 (ANALOG)
   *  -----------------------------------------------
   *  BUTTONS PINOUTS     : PIN GPIO
   *  -----------------------------------------------
   *  TOP LEFT BUTTON     : 15 - SWITCH PREVIOUS MENU / CHANGE PARAMETER
   *  TOP RIGHT BUTTON    : 12 - SWITCH NEXT MENU / CHANGE PARAMETER
   *  BOTTOM LEFT BUTTON  : 32 - POWER INC & BACK & INC PARAMETER
   *  BOTTOM RIGHT BUTTON : 27 - POWER DEC & SAVE & DEC PARAMETER
   *  CENTER BUTTON       : 33 - POWER ON & SELECT
   *  -----------------------------------------------
   *  Exclusive Features Of TAKEONE BOARD
   *  -----------------------------------------------
   *  ESP32 TAKEONE Board has some unique hardware features
   *  1. Battery Voltage Level Monitoring Via Analog Pin 35
   *  2. Boost Converter In Case We Need 5V Supply From Li-Ion Battery
   *  3. I2C : Motion Sensor IMU/MPU9250 9 Degrees Of Fredom 
   *  4. SPI : SDCARD Reader */
//======================================================
//  LIBRARIES
//======================================================
  #include  <Wire.h>
  #include  <EEPROM.h>
  #include  <Button.h>
  #include  <TimeTrigger.h>
  #include  <ArduinoJson.h>
  #include  <NeoPixelBus.h>
  #include  <BluetoothSerial.h>
//======================================================
// PUBLIC VARIABLES
//======================================================
  uint8_t   MENU_COUNT          = 1;
  bool      BLUETOOTH_STATUS    = false;
  bool      CHARGE_STATE        = false;
  bool      DISPLAY_CHANGED     = false;
//======================================================
// HARDWARE DEFINITIONS
//======================================================
  // Nessesary Definition For Bluetooth Library
  #if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
    #error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
  #endif
//------------------------------------------------------
// Serial Terminal
//------------------------------------------------------
  #define Terminal        Serial                // SERIAL PORT USED AS TERMINAL WINDO FOR DEBUG
  #define isTerminal      Terminal.available()  // Terminal Serial Buffer Available
  #define flushTerminal   while(isTerminal) { Terminal.read(); }
//------------------------------------------------------
// Bluetooth Terminal
//------------------------------------------------------
  BluetoothSerial         Bluetooth;  // Here We Use the Native Bluetooth In The ESP32
  #define isBluetooth     Bluetooth.available() // Bluetooth Serial Buffer Available
  #define flushBluetooth  while(isBluetooth) { Bluetooth.read(); }
//------------------------------------------------------
// Buttons
//------------------------------------------------------
  Button  BTN_PREVIOUS(15);   // TL : 15 - SWITCH PREVIOUS MENU / CHANGE PARAMETER
  Button  BTN_NEXT(12);       // TR : 12 - SWITCH NEXT MENU / CHANGE PARAMETER
  Button  BTN_CENTER(33);     // CT : 33 - POWER INC & BACK & INC PARAMETER
  Button  BTN_INCREASE(32);   // BL : 32 - POWER DEC & SAVE & DEC PARAMETER
  Button  BTN_DECREASE(27);   // BR : 27 - POWER ON & SELECT
//------------------------------------------------------
// Boost 3V3 To 5V
//------------------------------------------------------
  #define BOOST_PIN       14
  #define BOOST_SETUP     pinMode(BOOST_PIN, OUTPUT)
  #define BOOST_ON        digitalWrite(BOOST_PIN, HIGH)
  #define BOOST_OFF       digitalWrite(BOOST_PIN, LOW)
  #define BOOST_STU       digitalRead(BOOST_PIN)
//------------------------------------------------------
// Keep Power On
//------------------------------------------------------
  #define POWER_PIN       13
  #define POWER_SETUP     pinMode(POWER_PIN, OUTPUT)
  #define POWER_OFF       digitalWrite(POWER_PIN, LOW)
  #define POWER_ON        digitalWrite(POWER_PIN, HIGH)
  TimeTrigger POWER_PRESS(4000);  // The Interval In mSeconds
//------------------------------------------------------
//  Piezo Transducer
//------------------------------------------------------
  #define VTH             15  // THRESHOLD VOLTAGE FOR ZERO POSITIONNING KICKING POWER SENSOR
  #define PIEZO_PIN       34  // IMPACT SENSOR
  #define RANDOM_SOURCE   39  // RANDOM NUMBER GENERATOR SOURCE
  #define PIEZO_READ      analogRead(PIEZO_PIN)
  #define PIEZO_MAP       map(PIEZO_READ, VTH, 4095, 0, 100)
//------------------------------------------------------
//  Battery Level Monitoring
//------------------------------------------------------
  #define BATTERY_PIN     35
  #define BATTERY         (float)(((analogRead(BATTERY_PIN) * (3.3 / 4096)) * 2) + 0.31)
  #define CHARGE          round(mapf(BATTERY, 3.27, 4.31, 0, 100), 1)
//------------------------------------------------------
//  Power Charging Status
//------------------------------------------------------
  #define STAT_PIN        25
  #define STAT_SETUP      pinMode(STAT_PIN, INPUT)
  #define STAT_READ       digitalRead(STAT_PIN)
//------------------------------------------------------
//  Neo Pixel LED Strip
//------------------------------------------------------
  #define PIXEL_COUNT     21  // make sure to set this to the number of pixels in your strip
  #define PIXEL_PIN       26  // make sure to set this to the correct pin, ignored for Esp8266
  // three element pixels, in different order and speeds
  NeoPixelBus<NeoGrbFeature, Neo800KbpsMethod> PIXEL_STRIP(PIXEL_COUNT, PIXEL_PIN);
//------------------------------------------------------
//  JSON Object & Memory
//------------------------------------------------------
  #define CAPACITY        200
  DynamicJsonDocument     JSON(CAPACITY); // Building JSON Buffer
//  FIXED OPERATION MODE MESSAGES ~~~~~~~~~~~~~~~~~~~~~~
  #define BLUETOOTH_START         "{\"status\":\"BLUETOOTH STARTED\"}"
  #define BLUETOOTH_FAIL          "{\"status\":\"BLUETOOTH FAILED\"}"
  #define BLUETOOTH_CONNECTED     "{\"status\":\"BLUETOOTH CONNECTED\"}"
  #define BLUETOOTH_DISCONNECTED  "{\"status\":\"BLUETOOTH DISCONNECTED\"}"
  #define STAND_ALONE_MODE        "{\"status\":\"STAND ALONE MODE\"}"
//  FIXED MESSAGES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  #define POWERUP         "{\"status\":\"POWER UP\"}"
  #define POWEROFF        "{\"status\":\"POWER OFF\"}"
  #define UNDERSTOOD      "{\"status\":\"OK\"}"
  #define JSON_ERROR      "{\"status\":\"ERROR JSON\"}"
  #define TARGET_MET      "{\"status\":\"TARGET MEET\"}"
  #define GAME_OVER       "{\"status\":\"GAME OVER\"}"
  #define TIME_OVER       "{\"status\":\"TIME OVER\"}"
  #define UNKNOWN_GAME    "{\"status\":\"UNKNOWN GAME\"}"
//  GAME NAMES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  #define GM_COUNTER      "{\"gm\":\"COUNTER\"}"
  #define GM_TATTACK      "{\"gm\":\"TIME ATTACK\"}"
  #define GM_REACTION     "{\"gm\":\"REACTION\"}"
  #define GM_DECISION     "{\"gm\":\"DECISION\"}"
  #define GM_STAMINA      "{\"gm\":\"STAMINA\"}"
//  COMMAND LIST ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  #define CM_START        "{\"cm\":\"START\"}"
  #define CM_RESET        "{\"cm\":\"RESET\"}"
  #define CM_STOP         "{\"cm\":\"STOP\"}"
  #define CM_EXIT         "{\"cm\":\"EXIT\"}"
//======================================================
// MAIN SETUP
//======================================================
  void setup()
  {
    // Power Hold
    POWER_SETUP;
    POWER_ON;

    // Turn On Booster
    BOOST_SETUP;
    BOOST_OFF;

    // Charging Status Pin
    STAT_SETUP;
    
    // I2C Module & OLED 
    Wire.begin();
    delay(5);
    OLED_INIT();
    OLED_CLEAR();
    OLED_SHOW();

    // Start Buttons Function
    BTN_PREVIOUS.begin();
    BTN_NEXT.begin();
    BTN_CENTER.begin();
    BTN_INCREASE.begin();
    BTN_DECREASE.begin();

    // Communication Ports
    Terminal.begin(9600); Terminal.println();

    // To Detect Connection / Disconnection Event
    Bluetooth.register_callback(callback);

    // Starting Bluetooth Wireless With Name (KICKER)
    if(!Bluetooth.begin("KICKER")) {
      terminal(POWERUP);
      terminal(BLUETOOTH_FAIL);
    } else {
      dualcomm(POWERUP);
      dualcomm(BLUETOOTH_START);
    }

    // The Random Number Source
    randomSeed(analogRead(RANDOM_SOURCE));
    
    // Display Welcome Screen
    KICKER_LOGO();
    delay(2000);
  }
//======================================================
// MAIN PROGRAM LOOP
//======================================================
  void loop() 
  {
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // This Is For Bluetooth Mode
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    if(BLUETOOTH_STATUS == true) {
      // Initialization Of Bluetooth Connected Mode
      OLED_BLUETOOTH_MODE();    // Display Bluetooth Connected Mode
      DISPLAY_CHANGED = false;  // Give Permition To Change The Content
      delay(3000);              // Hold The Display Content For 3 Seconds
      
      // While You Are On Bluetooth Connected Mode
      while(BLUETOOTH_STATUS == true) {
        ListenToBluetooth();    // Reading Only From Bluetooth
      }

      // Give Permition To Change The Content
      DISPLAY_CHANGED = false;
    } 

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // This Is For Stand Alone Mode
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    else {
      // Initialization Of Stand Alone Mode
      OLED_STANDALONE_MODE();   // Display Stand Alone Mode
      DISPLAY_CHANGED = false;  // Give Permition To Change The Content
      delay(3000);              // Hold The Display Content For 3 Seconds

      // While You Are On Stand Alone Mode
      while(BLUETOOTH_STATUS == false) {
        ListenToStandAlone();   // Enter Listen To Stand Alone Buttons
      }

      // Once The Stand Alone Mode Is Over
      DISPLAY_CHANGED = false;  // Give Permition To Change The Display
    }
  }
//------------------------------------------------------
//  LISTEN TO BLUETOOTH
//------------------------------------------------------
  void ListenToBluetooth()
  {
    if(isBluetooth)
    {
      // Reading Message From Bluetooth
      String msg = Bluetooth.readStringUntil('\n');
      flushBluetooth;
      msg.trim();

      // Display On Serial Monitor For Debuging
      terminal(msg);

      // If It Was A Command To Power OFf
      if(msg == POWEROFF) {
        OLED_POWERING_DOWN();
      }
      
      // Deserialize the JSON document
      DeserializationError error = deserializeJson(JSON, msg);

      // If The System Understood The Message Clearly
      if(!error)
      {
        // Sending Status Message
        dualcomm(UNDERSTOOD);

        // Reading Game Name & Settings
        String game     = JSON["gm"];
        String settings = JSON["set"];

        // Clearing JSON Buffer
        JSON.clear();
        
        // Entering To Game Selector
        bluetooth_game_selector(game, settings);
      }
      else
      {
        terminal(JSON_ERROR);
      }
    }
  }
//------------------------------------------------------
//  LISTEN TO STAND ALONE
//------------------------------------------------------
  void ListenToStandAlone()
  {
    // SHIFT TO PREVIOUS GAME MENU
    if(BTN_PREVIOUS.pressed()) 
    {
      // Checking The Condition Of Order Number
      if(MENU_COUNT == 1 || MENU_COUNT < 1) {
        MENU_COUNT = 3; // If The Value Equals To Zero Then Roll Back To 4
      } else {
        MENU_COUNT -= 1;
      }
      
      // Give Permition To Change The Screen
      DISPLAY_CHANGED = false;
    }
    
    // SHIFT TO NEXT GAME MENU
    else if(BTN_NEXT.pressed()) 
    {
      // Checking The Condition Of Order Number
      if(MENU_COUNT == 3 || MENU_COUNT > 3) 
      {
        MENU_COUNT = 1; // If The Value Exceeds 4 Then Roll Back To 1
      } else {
        MENU_COUNT += 1;
      }
      
      // Give Permition To Change The Screen
      DISPLAY_CHANGED = false;
    }
    
    // SELECT CURRENT DISPLAYED MENU
    else if(BTN_CENTER.pressed()) 
    {
      // IF LONG PRESS -> POWER OFF KICKER
      DETECT_LONG_PRESS();

      // IF SHORT PRESS -> PLAY GAME CORESPONDING TO NUMBER
      switch(MENU_COUNT)
      {
        case 1: // Play Counter Game
                Terminal.println("SELECTED -> GAME COUNTER");
                game_counter_standalone();
                break;
        case 2: // Play Time Attack Game
                Terminal.println("SELECTED -> GAME TIME ATTACK");
                time_attack_standalone();
                break;
        case 3: // Play Reaction Game
                Terminal.println("SELECTED -> GAME REACTION");
                game_reaction_standalone();
                break;
      }
    }

    // Display The Game Pages
    if(DISPLAY_CHANGED == false) 
    {
      // IF Previous / Next Buttons Are Pressed -> Display Menu Selection On Terminal
      switch(MENU_COUNT) 
      {
        case 1: // Play Counter Game
                //Terminal.println("MENU #" + String(MENU_COUNT) + " - " + "GAME COUNTER");
                GAME_TITLE_DISPLAY(MENU_COUNT, "COUNTER");
                break; 
        case 2: // Play Time Attack Game
                //Terminal.println("MENU #" + String(MENU_COUNT) + " - " + "GAME TIME ATTACK");
                GAME_TITLE_DISPLAY(MENU_COUNT, "TIME ATTACK");
                break;
        case 3: // Play Reaction Game
                //Terminal.println("MENU #" + String(MENU_COUNT) + " - " + "GAME REACTION");
                GAME_TITLE_DISPLAY(MENU_COUNT, "REACTION");
                break;
      }
    }
  }
