//======================================================
// LIBRARIES
//======================================================

  // Library Include
  #include <ArduinoJson.h>
  #include <TimeTrigger.h>
  #include <Adafruit_NeoPixel.h>

//======================================================
// HARDWARE SETUP & SELECTION
//======================================================
  // Arduino MEGA
  #ifdef  ARDUINO_AVR_MEGA2560
//------------------------------------------------------

    // Library Nessesary
    #ifdef __AVR__
      #include <avr/power.h>
    #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
    #define Bluetooth       Serial1               // We will Connect The HC-05 To Hardware Serial1 In Arduino Mega
    #define isBluetooth     Bluetooth.available() // Bluetooth Serial Buffer Available
    #define flushBluetooth  while(isBluetooth) { Bluetooth.read(); }

    // JSON Object & Memory
    #define CAPACITY        100
    DynamicJsonDocument     JSON(CAPACITY); // Building JSON Buffer

    // LED Strip
    #define LED_STRIP       53        // LED STRIP PIN NUMBER
    #define NUM_PIXELS      14        // NUMBER OF LED'S IN NEO PIXEL STRIP
    Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUM_PIXELS, LED_STRIP, NEO_GRB + NEO_KHZ800);

    // Impact Detection
    #define VTH             14        // THRESHOLD VOLTAGE FOR ZERO POSITIONNING KICKING POWER SENSOR
    #define SENSOR_PIN      A0        // IMPACT SENSOR
    #define RANDOM_SOURCE   A7        // RANDOM NUMBER GENERATOR SOURCE

//------------------------------------------------------
  // Arduino Uno / Nano
  #elif ARDUINO_AVR_UNO || ARDUINO_AVR_NANO
//------------------------------------------------------

    // Library Nessesary
    #ifdef __AVR__
      #include <avr/power.h>
    #endif
    
    /* ARDUINO NANO DOESNOT HAVE A SECOND NATIVE 
    SERIAL PORT SO WE USE SOFTWARE SERIAL LIBRARY */
    #include <SoftwareSerial.h>

    // 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
    SoftwareSerial          Bluetooth(10, 11);    // Pin 10 RX, Pin 11 TX
    #define isBluetooth     Bluetooth.available() // Bluetooth Serial Buffer Available
    #define flushBluetooth  while(isBluetooth) { Bluetooth.read(); }

    // JSON Object & Memory
    #define CAPACITY        100
    DynamicJsonDocument     JSON(CAPACITY); // Building JSON Buffer

    // LED Strip
    #define LED_STRIP       2         // LED STRIP PIN NUMBER
    #define NUM_PIXELS      14        // NUMBER OF LED'S IN NEO PIXEL STRIP
    Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUM_PIXELS, LED_STRIP, NEO_GRB + NEO_KHZ800);

    // Impact Detection
    #define VTH             14        // THRESHOLD VOLTAGE FOR ZERO POSITIONNING KICKING POWER SENSOR
    #define SENSOR_PIN      A0        // IMPACT SENSOR
    #define RANDOM_SOURCE   A4        // RANDOM NUMBER GENERATOR SOURCE

//------------------------------------------------------
  // ESP32 TAKEONE BOARD
  #elif ESP32
//------------------------------------------------------

    /*  ===============================================
     *           - 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
     *  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) 
     */

    /* ESP32 HAVE A NATIVE BLUETOOTH MODULE
    SO WE USE BLUETOOTH SERIAL LIBRARY */
    #include <BluetoothSerial.h>

    // 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(); }

    // JSON Object & Memory
    #define CAPACITY        100
    DynamicJsonDocument     JSON(CAPACITY); // Building JSON Buffer

    // LED Strip
    #define LED_STRIP       33         // LED STRIP PIN NUMBER
    #define NUM_PIXELS      14        // NUMBER OF LED'S IN NEO PIXEL STRIP
    Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUM_PIXELS, LED_STRIP, NEO_GRB + NEO_KHZ800);

    // Impact Detection
    #define VTH             14        // THRESHOLD VOLTAGE FOR ZERO POSITIONNING KICKING POWER SENSOR
    #define SENSOR_PIN      39        // IMPACT SENSOR
    #define RANDOM_SOURCE   34        // RANDOM NUMBER GENERATOR SOURCE
    
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // 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 */

    // Boost Converter
    #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)

    // 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)

    // Function To Make Sure The Pin Is ON
    void LED_BOOST_CHK() {
      if(BOOST_STU == LOW) {
        BOOST_ON;
        delay(1);
      }
    }

    // Floting Point Mapping
    double mapf(double val, double in_min, double in_max, double out_min, double out_max) {
      return (val - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
    }

  #endif

//======================================================
// PREDEFINED JSON STRINGS MESSAGES
//======================================================

  // FIXED MESSAGES
  #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\"}"

//======================================================
// READING IMPACT POWER FROM ANALOG PINS
//======================================================

  // READING THE IMPACT
  int readImpact() 
  {
    int value = analogRead(SENSOR_PIN);
    return value;
  }

//======================================================
// MAIN HARDWARE SETUP
//======================================================

  void setup() 
  {
    // Communications
    Terminal.begin(9600);

    // Make Sure Of The Core
    #ifdef  ESP32
      Bluetooth.begin("KICKER");  // Bluetooth device name
      BOOST_SETUP;                // Boost Pin Setup
      BOOST_OFF;                  // Turn It Off
    #else
      Bluetooth.begin(9600);      // Bluetooth HC-05 / HC-06
    #endif

    // I/O
    pixels.begin();   // Neo Pixel
    LED_CLEAR();      // Turn Off All LED

    // The Random Number Source
    randomSeed(analogRead(RANDOM_SOURCE));

    // POWER UP MESSGAE
    dualcomm(POWERUP);

    // DISPLAY COMPANY COUNTRY FLAG
    LED_BAHRAIN_FLAG();   // Bahrain Flag
    LED_COLOMBIA_FLAG();  // Colombia Flag

    // DisJSON Rainbow
    LED_CLEAR();
  }

//======================================================
// MAIN PROGRAM LOOP
//======================================================

  void loop() 
  {
    // Reading From Bluetooth
    if(isBluetooth) 
    {
      // Reading Message From Bluetooth
      String msg = Bluetooth.readStringUntil('\n');
      flushBluetooth;
      msg.trim();

      // Display On Serial Monitor For Debuging
      terminal(msg);
      
      // 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
        game_selector(game, settings);
      }
      else
      {
        terminal(JSON_ERROR);
      }
      Bluetooth.flush();
    }
  }

//======================================================
// END OF PROGRAM
//======================================================
