  /*  ===============================================
   *           - THIS IS TAKEONE 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
   */
  
  // Include Library
  #include  <FS.h>
  #include  <SD.h>
  #include  <SPI.h>
  #include  <Wire.h>
  #include  <Button.h>
  #include  <ArduinoJson.h>
  #include  <BluetoothSerial.h>
  #include  <Adafruit_NeoPixel.h>

  //----------------------------------------------------------------
  // Communication Models
  //----------------------------------------------------------------

  // Serial Port As Terminal
  #define   terminal  Serial

  // Clearing Terminal Remainning
  #define   flush_terminal    while(terminal.available()) { terminal.read(); }

  // Bluetooth Object 
  BluetoothSerial     bluetooth;
  
  // Bluetooth Connection Status
  bool bluetooth_conn = false;

  // Clearing Bluetooth Remainning
  #define   flush_bluetooth   while(bluetooth.available()) { bluetooth.read(); }

  // Fixed JSON Strings
  #define POWERUP         "{\"status\":\"POWER UP\"}"
  #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\"}"

  // JSON CAPACITY
  #define capacity        250

  //----------------------------------------------------------------
  // TAKEONE BOARD HARDWARE DEFINITIONS
  //----------------------------------------------------------------
  
  // On Board MPU9250 I2C Address
  #define   MPU_ADD       0x69

  // Connected I2C OLED Address
  #define   OLED_ADD      0x3C

  // Connected I2C LCD Address
  #define   LCD_ADD       0x2C

  // Define Impact Sensor
  #define   PIEZO         analogRead(34)
  #define   VOLTS         mapf(PIEZO, 0, 4095, 0, 3.3)

  // Panel Push Buttons Pin Assignment Sections
  Button    TLB(32);      // Top Left Button
  Button    BLB(15);      // Bottom Left Button
  Button    CB(33);       // Center Button
  Button    BRB(27);      // Bottom Right Button
  Button    TRB(12);      // Top Right Button 33

  // Panel Push Buttons Command Assignment
  #define   PrvMenu       TLB.pressed()
  #define   NxtMenu       TRB.pressed()
  #define   SelMenu       CB.pressed()
  #define   IncBtn        BLB.pressed()
  #define   DecBtn        BRB.pressed()

  // NEO Pixel Strip Section
  #define   NEOPIXEL      26  // Define Pin Number
  #define   NUMPIXELS     22  // Define Number Of LED's

  // Battery Level Monitoring
  #define   BATTERY       (float)(((analogRead(35) * (3.3 / 4096)) * 2) + 0.31)
  #define   CHARGE        mapf(BATTERY, 3.27, 4.31, 0, 100)

  // Power Latch Holder Pin
  #define   POWER_PIN     14
  #define   POWER_SETUP   pinMode(POWER_PIN, OUTPUT)
  #define   POWER_ON      digitalWrite(POWER_PIN, HIGH)
  #define   POWER_OFF     digitalWrite(POWER_PIN, LOW)

  // Battery To 5V Level Booster Setup & Control
  #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)

  // On Board LED Setup & Control
  #define   LED_PIN       13
  #define   LED_SETUP     pinMode(LED_PIN, OUTPUT)
  #define   LED_ON        digitalWrite(LED_PIN, HIGH)
  #define   LED_OFF       digitalWrite(LED_PIN, LOW)

  // For Timming
  unsigned long timetick  = 0;
  unsigned long prevtick  = 0;

  /* Display Change Flag Explaination
   *  This flag is used to prevent continous writing to the oled display
   *  once a change has happened we drive the display_changed to false this will tell
   *  the micro controller that a need to write new data is needed, after writing the new data to display
   *  then we drive the display_changed to true that means the nessary changes has been written
   */
   
  bool      display_changed   = false;
  uint8_t   menu_count        = 0;

//--------------------------------------------------------------------
//  Bluetooth Connection Status Call Back
//--------------------------------------------------------------------

  void callback(esp_spp_cb_event_t event, esp_spp_cb_param_t *param)
  {
    if(event == ESP_SPP_SRV_OPEN_EVT){
      //Serial.println("Client Connected");
      bluetooth_conn = true;
    }
  
    if(event == ESP_SPP_CLOSE_EVT ){
      //Serial.println("Client disconnected");
      bluetooth_conn = false;
    }
  }

//--------------------------------------------------------------------
//  CHIP MAIN SETUP
//--------------------------------------------------------------------

  void setup()
  {
    // Starting Serial Communications
    terminal.begin(115200);   // Starting Main USB UART Communication Interface
    terminal.println();       // New Line Seperator 1
    terminal.println();       // New Line Seperator 2
    
    // Starting The Bluetooth Module 
    if(!bluetooth.begin("KICKER")) {
      terminal.println("An error occurred initializing Bluetooth");
    } else {
      terminal.println("Bluetooth initialized");
    }

    // Checking The Connection Status
    bluetooth.register_callback(callback);
    
    // Pin Assignments
    BOOST_SETUP;            // Boost Converter Pin Setup
    LED_SETUP;              // OnBoard LED Pin Setup

    // Turn Off The Pins
    BOOST_OFF;              // Turn Off The Booster
    LED_OFF;                // Turn Off The LED

    // The Five Panel Buttons
    TLB.begin();            // Setup The Top Left Panel Button
    BLB.begin();            // Setup The Bottom Left Panel Button
    BRB.begin();            // Setup The Bottom Right Panel Button
    TRB.begin();            // Setup The Top Right Panel Button
    CB.begin();             // Setup The Center Panel Button

    // I2C Section
    Wire.begin();           // Starting I2C Communication Interface
    scanI2C();              // Starting I2C Communicatable Devices Detection
    oled_init();            // Starting OLED Display I2C Communications
    kicker_logo();          // Display Kicker Main Logo Display
    
    // Starting SDCARD Interface
    sdcard_init();          // Starting SDCARD Reader Communication Protocol

    // Neo Pixel Section
    neo_init();             // Starting Neo Pixel
    neo_color(0, 0, 0);     // Clear All LED's
    delay(10);              // Delay 10 Milli Seconds
    neo_color(255, 0, 0);   // Display RED LED Strip
    delay(1000);            // Delay 1 Second
    neo_color(0, 255, 0);   // Display GREEN LED Strip
    delay(1000);            // Delay 1 Second
    neo_color(0, 0, 255);   // Display BLUE LED Strip
    delay(1000);            // Delay 1 Second
    neo_turnoff();          // Power Off Neo Pixels & Their Power

    // THE PROGRAM STATE = MENU
    display_changed = false;  // Allow Display Change
  }

//--------------------------------------------------------------------
//  CHIP MAIN PROGRAM
//--------------------------------------------------------------------
  
  void loop()
  {
    // Checking The Bluetooth Connection
    bluetooth.register_callback(callback);

    // Condition The Preiority Is For The Bluetooth
    if(bluetooth_conn == true)
    {
      // Wait For Game JSON String
      read_game_json();
    }
    
    // Condition To Stand Alone Mode - Manual Mode
    else 
    {
      // Previous Menu ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      if(TLB.pressed()) {
        if(menu_count == 0) {
          menu_count = 4;
        } else {
          menu_count--;
        }
        display_changed = false;
      }
      
      // Next Menu ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      if(TRB.pressed()) {
        if(menu_count == 4) {
          menu_count = 0;
        } else {
          menu_count++;
        }
        display_changed = false;
      }
      
      // Select A Game ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      if(CB.pressed()) {
        select_game();
      }
      
      // Increase Power ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      if(BLB.pressed()) {
        
      }
      
      // Decrease Power ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      if(BRB.pressed()) {
        
      }
      
      // Display Target Menu ~~~~~~~~~~~~~~~~~~~~~~~~~~
      switch_menu();
    }
    
  }

//======================================================
// Read Game JSON String Command From Mobile
//======================================================

  void read_game_json()
  {  
    // Reading From Bluetooth
    if(bluetooth.available()) 
    {
      // Reading Message From Bluetooth
      String msg = bluetooth.readStringUntil('\n');

      // Clearing The Receive Buffer
      flush_bluetooth;

      // Clean The Message
      msg.trim();

      // Display On Sertial
      terminal.println(msg);
      
      // Parsing Objects Message Objects
      DynamicJsonDocument   root(capacity);
      
      // Deserialize the JSON document
      DeserializationError error = deserializeJson(root, msg);

      // If The System Understood The Message Clearly
      if(!error)
      {
        // Sending Status Message
        terminal.println(UNDERSTOOD);
        bluetooth.println(UNDERSTOOD);

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

        // Clearing JSON Document Buffer
        root.clear();
        
        // Entering To Game Selector
        game_selector(game, settings);
      }
      else
      {
        // Display On Serial
        terminal.println(JSON_ERROR);
      }

      // Bluetooth Serial RXD Flush
      flush_bluetooth;
    }
  }

//======================================================
// Game Selector Function
//======================================================

  void game_selector(String game, String settings)
  {
    // Kick Counter Game
    if(game == "ct") {
      terminal.println(GM_COUNTER);
      //game_counter(settings);
      return;
    }

    // Time Attack Game
    else if(game == "ta") {
      terminal.println(GM_TATTACK);
      //game_timeAttack(settings);
      return;
    }

    // Time Attack Game
    else if(game == "ra") {
      terminal.println(GM_REACTION);
      //game_reaction(settings);
      return;
    }

    // Unknown Game
    else {
      terminal.println(UNKNOWN_GAME);
      bluetooth.println(UNKNOWN_GAME);
      return;
    }
  }
