#include <AsyncHTTPRequest_Generic.h>
#include <AsyncHTTPRequest_Generic.hpp>

#include <SevSeg.h>
SevSeg sevseg; //Instantiate a seven segment object

#include <ArduinoJson.h>
#include <ArduinoJson.hpp>
#include <WiFi.h>

const char* ssid = "YOUR_WIFI_ACCESS_POINT_NAME";
const char* password = "YOUR_WIFI_PASSWORD";

String url_host= "http://192.168.43.1:8080";
String url_path = "/api/values?attrs=0&filter=";

bool request_error = false;

int display_mode = 1; // iterate between 1,2,3

#define TEMPERATURE_KEY "vte"
#define BATTERY_KEY "vba"
#define SPEED_KEY "vsp"

#define TEMPERATURE_KEY_LETTER "tE"
#define BATTERY_KEY_LETTER "bA" 
#define SPEED_KEY_LETTER "SP"

String selected_key = BATTERY_KEY;
char selected_key_letter[3] = BATTERY_KEY_LETTER;

StaticJsonDocument<200> json_doc;

AsyncHTTPRequest request;

#define SEGMENT_A_PIN D9
#define SEGMENT_B_PIN D10
#define SEGMENT_C_PIN D4
#define SEGMENT_D_PIN D5
#define SEGMENT_E_PIN D6
#define SEGMENT_F_PIN D8
#define SEGMENT_G_PIN D7

#define DIGIT_1_PIN D2
#define DIGIT_2_PIN D3

#define BUTTON_PIN D1
#define LIGHT_SENSOR_PIN A0

int num = 0; //number displayed

#define REQUEST_DELAY 500UL
unsigned long request_timer = millis();

#define MAX_RESPONSE_DELAY 2000UL
unsigned long max_response_timer = millis();

#define BRIGHTNESS_DELAY 100 

#define BRIGHTNESS_MIN -40 //-200 min
#define BRIGHTNESS_MAX 150 // 200 max
#define BRIGHTNESS_MIN_ANALOG 3000//4095 max
#define BRIGHTNESS_MAX_ANALOG 400 //0 min

unsigned long brightness_timer = millis();
int current_brightness = 0;

#define BUTTON_PRESSED_SHORT 50UL
#define BUTTON_PRESSED_LONG 5000UL
unsigned long button_pressed_timer = millis();

#define SELECTED_KEY_LETTER_DELAY 2000UL
unsigned long selected_key_letter_timer = millis();
bool display_selected_key_letter = true;

void setup(){
  //Serial.begin(115200);

  byte numDigits = 2;
  byte digitPins[] = {DIGIT_1_PIN, DIGIT_2_PIN};
  byte segmentPins[] = {SEGMENT_A_PIN,SEGMENT_B_PIN,SEGMENT_C_PIN,SEGMENT_D_PIN,SEGMENT_E_PIN,SEGMENT_F_PIN,SEGMENT_G_PIN};
  bool resistorsOnSegments = true; // 'false' means resistors are on digit pins
  byte hardwareConfig = COMMON_ANODE; 
  bool updateWithDelays = false;
  bool leadingZeros = false; // Use 'true' if you'd like to keep the leading zeros
  bool disableDecPoint = true; // Use 'true' if your decimal point doesn't exist or isn't connected. Then, you only need to specify 7 segmentPins[]

  sevseg.begin(hardwareConfig, numDigits, digitPins, segmentPins, resistorsOnSegments, updateWithDelays, leadingZeros, disableDecPoint);

  sevseg.setBrightness(current_brightness);

  pinMode(BUTTON_PIN, INPUT);

  sevseg.blank();
  sevseg.setChars(selected_key_letter);
  //sevseg.setNumber(99);

  unsigned long startup_timer = millis();
  while(millis() - startup_timer < 2000){
    sevseg.refreshDisplay();
  }

  WiFi.begin(ssid, password);
  request.onReadyStateChange(requestCB);
}

void loop(){
    // button actions
    if(digitalRead(D1) == LOW){
      button_pressed_timer = millis();
      while(digitalRead(D1) == LOW){
        sevseg.setChars("--");
        sevseg.refreshDisplay();
      }
      if(millis() - button_pressed_timer > BUTTON_PRESSED_SHORT){
        button_pressed_timer = millis();
        toggleDisplayMode();
        selected_key_letter_timer = millis();
        display_selected_key_letter = true;
        sevseg.setChars(selected_key_letter);
      }
    }
    
    sevseg.refreshDisplay();

    if(display_selected_key_letter){
      if(millis() - selected_key_letter_timer > SELECTED_KEY_LETTER_DELAY){
        display_selected_key_letter = false;
      }
    } else {
      if(millis() - request_timer > REQUEST_DELAY){
        request_timer = millis();
        getJson();
      }
    }

    unsigned long current_time = millis();
    unsigned long max_response_timer_diff = current_time - max_response_timer;

    if(max_response_timer_diff > MAX_RESPONSE_DELAY && current_time > max_response_timer){
      if(!request_error){
        request_error = true;
        sevseg.setChars(selected_key_letter);
      }
    } else {
      request_error = false;
    }

    if(millis() - brightness_timer > BRIGHTNESS_DELAY){
        brightness_timer = millis();
        calculateBrightness();
    }
}

void toggleDisplayMode(){
  if(display_mode <= 2){
    display_mode = display_mode + 1;
  } else {
    display_mode = 1;
  }

  if(display_mode == 1){
    selected_key = BATTERY_KEY;
    strcpy(selected_key_letter,BATTERY_KEY_LETTER);
  } else if(display_mode == 2){
    selected_key = SPEED_KEY;
    strcpy(selected_key_letter,SPEED_KEY_LETTER);
  } else if(display_mode == 3){
    selected_key = TEMPERATURE_KEY;
    strcpy(selected_key_letter,TEMPERATURE_KEY_LETTER);
  }
}

void calculateBrightness(){
  int analog_reading = analogRead(A0);
  //Serial.println(analog_reading);
  analog_reading = MIN(BRIGHTNESS_MIN_ANALOG, analog_reading);
  analog_reading = MAX(BRIGHTNESS_MAX_ANALOG, analog_reading);

  int tmp_brightness = map(analog_reading, BRIGHTNESS_MIN_ANALOG, BRIGHTNESS_MAX_ANALOG, BRIGHTNESS_MIN, BRIGHTNESS_MAX);
  
  if(tmp_brightness > current_brightness){
    current_brightness = current_brightness + 1;
  } else if(tmp_brightness < current_brightness){
    current_brightness = current_brightness - 1;
  }

  sevseg.setBrightness(current_brightness);

}

void sendRequest(){
  static bool requestOpenResult;
  if (request.readyState() == readyStateUnsent || request.readyState() == readyStateDone)  {
    String url = url_host + url_path + selected_key;
    requestOpenResult = request.open("GET", url.c_str());
    if (requestOpenResult){
      request.send();
    } 
  }  
}

void requestCB(void *optParm, AsyncHTTPRequest *request, int readyState){
  (void) optParm;
  if (readyState == readyStateDone)  {
    if (request->responseHTTPcode() == 200)    {
      String http_response = request->responseText();
      deserializeJson(json_doc, http_response);
      
      int json_value = json_doc[selected_key]["v"];
      bool is_valid = json_doc[selected_key]["d"];

      if(json_value >= 0 && is_valid){
        max_response_timer = millis();
        num = json_value;
        sevseg.setNumber(num);
        //Serial.println("response ok");

      } 
    }
  }
}

void getJson(){
  if(WiFi.status() == WL_CONNECTED){ 
    sendRequest();
    //Serial.println("sendRequest");
  } 
}
