#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <WebSocketsClient.h>
#include "arduino_secrets.h"

#define SENSOR_PIN A0
#define TRIGER_PIN D6
#define LASER_PIN D5
#define LED_PIN D4

#define KEY_LENGTH 8
#define TIME_OF_DEATH 5000
#define SEUIL_OF_DEATH 800

#define DEBUG_SERIAL Serial
#define WS_URL "192.168.134.144"
#define WS_PORT 41848

const char ssid[] = SECRET_SSID;
const char pass[] = SECRET_PASS;

WebSocketsClient webSocket;

String localId;
String serverId = String("Server");
char separateur[] = ":";
char packetBuffer[256]; // buffer to hold incoming packet

unsigned long chestCheckInterval = 10;
unsigned long lastUpdate = millis();
bool isAlive = true;
int tmpOfDeath = 0;
bool connected = false;

void webSocketEvent(WStype_t type, uint8_t *payload, size_t length)
{
  switch (type)
  {
    case WStype_DISCONNECTED:
      DEBUG_SERIAL.printf("[WSc] Disconnected!\n");
      connected = false;
      break;
    case WStype_CONNECTED:
      DEBUG_SERIAL.printf("[WSc] Connected to url: %s\n", payload);
      connected = true;

      // send message to server when Connected
      DEBUG_SERIAL.println("[WSc] SENT: Connected");
      sendMsg("CONNECT");
      break;
    case WStype_TEXT:
      DEBUG_SERIAL.printf("[WSc] RESPONSE: %s\n", payload);
      sprintf(packetBuffer, "%s", payload);
      parseData();
      break;
    case WStype_BIN:
      DEBUG_SERIAL.printf("[WSc] get binary length: %u\n", length);
      hexdump(payload, length);
      break;
    case WStype_PING:
      // pong will be send automatically
      DEBUG_SERIAL.printf("[WSc] get ping\n");
      break;
    case WStype_PONG:
      // answer to a ping we send
      DEBUG_SERIAL.printf("[WSc] get pong\n");
      break;
  }
}

void setup()
{
  pinMode(SENSOR_PIN, INPUT);
  pinMode(TRIGER_PIN, INPUT);
  pinMode(LASER_PIN, OUTPUT);
  pinMode(LED_PIN, OUTPUT);

  DEBUG_SERIAL.begin(115200);
  //DEBUG_SERIAL.setDebugOutput(true);
  DEBUG_SERIAL.println();
  DEBUG_SERIAL.println();
  DEBUG_SERIAL.println();

  for (uint8_t t = 4; t > 0; t--)
  {
    DEBUG_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t);
    DEBUG_SERIAL.flush();
    delay(1000);
  }

  /* initialize random seed: */
  srand(time(0));
  localId = hexaGenerator(KEY_LENGTH);

  WiFi.begin(ssid, pass);

  while ( WiFi.status() != WL_CONNECTED ) {
    delay ( 500 );
    Serial.print ( "." );
  }
  DEBUG_SERIAL.print("Local IP: "); DEBUG_SERIAL.println(WiFi.localIP());
  // server address, port and URL
  webSocket.begin(WS_URL, WS_PORT, "/");

  // event handler
  webSocket.onEvent(webSocketEvent);
  // try ever 5000 again if connection has failed
  webSocket.setReconnectInterval(5000);
}



void loop() {
  webSocket.loop();
  if (isAlive)
  {
    shootLaser();
    if (connected && lastUpdate + chestCheckInterval < millis()) {
      chestState();
      lastUpdate = millis();
    }
  }
  else
  {
    respawn();
  }

}

void setDead(bool state)
{
  if (state)
  {
    digitalWrite(LASER_PIN, LOW);
    digitalWrite(LED_PIN, HIGH);
    isAlive = false;
  }
  else
  {
    digitalWrite(LED_PIN, LOW);
    tmpOfDeath = 0;
    isAlive = true;
  }
}

void respawn()
{
  if (tmpOfDeath < TIME_OF_DEATH)
  {
    tmpOfDeath++;
    delay(1);
  }
  else
  {
    setDead(false);
  }
}

void chestState()
{
  if (analogRead(SENSOR_PIN) > SEUIL_OF_DEATH)
  {
    sendMsg("HIT");
    setDead(true);
  }
}

void shootLaser()
{
  if (digitalRead(TRIGER_PIN) == HIGH)
  {
    digitalWrite(LASER_PIN, HIGH);
  }
  else
  {
    digitalWrite(LASER_PIN, LOW);
  }
}

void parseData()
{
  String token = String(strtok(packetBuffer, separateur));
  if (token == localId)
  {
    token = strtok(NULL, separateur);
    if (token == serverId)
    {
      token = strtok(NULL, separateur);
      if (token == "SERVER_TEST")
      {
        sendMsg("TEST_RECEIVED");
      }
      else if (token == "PING")
      {
        sendMsg("PONG");
        setDead(true);
      }
    }
  }
}

void sendMsg(String msg)
{
  String str = serverId;
  str.concat(separateur);
  str.concat(localId);
  str.concat(separateur);
  str.concat(msg);
  sendWs(str);
}

void sendWs(String string)
{

  DEBUG_SERIAL.print("sendUDP : ");
  DEBUG_SERIAL.println(string);

  // convert string to char array
  char msg[255];
  string.toCharArray(msg, 255);

  webSocket.sendTXT(msg);
}

String hexaGenerator(int length)
{
  int val = rand();
  char hex[length];
  itoa(val, hex, 16);
  String result = String(hex);
  Serial.print("\nEquivalent Hex Byte: ");
  Serial.println(result);
  return result;
}

void diagWifiStatus()
{
  /**
     Return value available :
     0 : WL_IDLE_STATUS when Wi-Fi is in process of changing between statuses
     1 : WL_NO_SSID_AVAIL in case configured SSID cannot be reached
     3 : WL_CONNECTED after successful connection is established
     4 : WL_CONNECT_FAILED if connection failed
     6 : WL_CONNECT_WRONG_PASSWORD if password is incorrect
     7 : WL_DISCONNECTED if module is not configured in station mode
  */
  DEBUG_SERIAL.printf("Connection status: %d\n", WiFi.status());
}
