// PB: for esp32, must include #include <WiFi.h>
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <ESP8266WebServer.h>
// use ArduinoJson library to make it easier to send/receive json
#include <ArduinoJson.h>

// defines for pins
#define PIN_PIEZZO A0
#define PIN_LED D1

// defines & variables to manage a gameplay
#define STATE_NONE 0 // wait until start
#define STATE_LED 1 // while the led is blinking
#define STATE_TAP 2 // while the player must tap
int stateGamePlay = STATE_NONE;
unsigned long t = 0; // to count the elapsed time
int delayLED = 1000; // by default delay is 1sec
String player = "";

// defines & variable to manage the global behavior of the game
#define STATE_WAIT 0 // wait to receive a start game order -> must act as a server
#define STATE_PLAY 1 // while the game is ongoing
int stateGeneral = STATE_WAIT;

// defines & variables for wifi/http
String apiUrl = "http://192.168.0.2:12345/api";
WiFiClient client;
HTTPClient http;
ESP8266WebServer server(80); // listen on port 80

/* ***************************
  Functions in Web Server mode
**************************** */
// helper function to send back a string result within a JSON
void stringAnswer(int error, int status, String message) {
  JsonDocument doc;
  doc["error"] = error;
  doc["data"] = message;
  String data = "";
  serializeJson(doc, data); // convert Json object into a string
  server.send(status, "application/json; charset=utf-8", data);
}

/*
// helper function to send back a json array result within a JSON
void arrayAnswer(int error, int status, JsonArray& dataDoc) {
  JsonDocument doc;
  doc["error"] = error;
  doc["data"] = dataDoc;
  String data = "";
  serializeJson(doc, data);
  server.send(status, "application/json; charset=utf-8", data);
}
*/

// function to handle starting the game
void handleStart() {
  // assume that the query is .../start?player=name&delay=XXX
  // test if the URI is correct
  if (server.args() != 2) {
    stringAnswer(1, 404, "invalid query");// send back an error
  }
  delayLED = server.arg("delay").toInt();
  player = server.arg("player");
  Serial.println(player+": game begins");
  stringAnswer(0,200,"OK");
  // change the general state to start playing
  stateGeneral = STATE_PLAY;
  stateGamePlay = STATE_LED;
}

/* ***************************
  Functions in Web Client mode
**************************** */
// function to send the result of the gamesession to the API
void sendTimeToAPI() {

  http.begin(client, apiUrl+"/players/"+player+"/result/"+String(t)); // send a request on route /result

  int httpCode = http.GET();
  if (httpCode > 0) {
      Serial.printf("HTTP Response Code: %d\n", httpCode);

      String payload = http.getString();
      Serial.println("Response:");
      Serial.println(payload);
  } else {
      Serial.printf("Request failed: %s\n",
                    http.errorToString(httpCode).c_str());
  }
  http.end();
}

void setupWifi() {
     
  const char *ssid = "testingiot"; // the ssid of the AP        
  const char *password = "testingiot"; // the password of the AP  
  
  WiFi.setAutoConnect(false);  
  WiFi.setSleepMode(WIFI_NONE_SLEEP); 

  WiFi.mode(WIFI_STA); // setup as a wifi client
  WiFi.begin(ssid,password); // try to connect
  while (WiFi.status() != WL_CONNECTED) { // check connection
    delay(500); 
    Serial.print(".");
  } 
  // debug messages
  Serial.print("Connected, IP address: ");
  Serial.println(WiFi.localIP());
}

void setup() {

  Serial.begin(115200);
  pinMode(PIN_LED, OUTPUT);

  delay(100);
  Serial.println("Setup Wifi");  
  setupWifi();
  Serial.println("Wifi OK ! Setup local web server");
  delay(100);

  server.on("/start", HTTP_GET, handleStart); // bind the route /start to its handler
  server.begin();
  Serial.println("HTTP server is started");
}

void loop() {

  // if waiting, just handle HTTP requests
  if (stateGeneral == STATE_WAIT) {
    server.handleClient();
  }
  // otherwise play !
  else if (stateGeneral == STATE_PLAY) {

    if (stateGamePlay == STATE_LED) {
      // light on the LED during the time specified in the start request.
      digitalWrite(PIN_LED, HIGH);
      delay(delayLED);
      digitalWrite(PIN_LED, LOW);
      t = millis();
      stateGamePlay = STATE_TAP;
    }
    else if (stateGamePlay == STATE_TAP) {  
      int level = analogRead(PIN_PIEZZO);
      if ( level > 500) {
        t = millis() - t;
        Serial.println("time taken:" +String(t));
        sendTimeToAPI();
        // whatever the case, go back to waiting fot starting
        stateGeneral = STATE_WAIT;
        stateGamePlay = STATE_NONE;
      }
      delay(5);
    }
  }
}
