/*
 * Stairs
 * Copyright 2017 (c) Mark van Renswoude
 *
 * https://git.x2software.net/pub/Stairs
*/
#include "./settings.h"
#include <ArduinoJson.h>
#include <IPAddress.h>
#include <ESP8266WiFi.h>
#include <EspSaveCrash.h>
#include "./shared.h"
#include "../assets/version.h"
#include "../config.h"
#include "../debug.h"
#include "../global.h"
#include "../settings/connection.h"

extern "C" {
  #include <user_interface.h>
}


bool clearedResetReason = false;


void handleStatus(AsyncWebServerRequest *request)
{
  _dln("API :: status");

  DynamicJsonBuffer jsonBuffer(JSON_OBJECT_SIZE(6));

  JsonObject& root = jsonBuffer.createObject();
  root["systemID"] = String(ESP.getChipId(), HEX);
  root["version"] = String(VersionFullSemVer);

  if (ntpClient != nullptr && hasTimezone)
  {
    root["time"] = ntpClient->getEpochTime();
    root["timeOffset"] = timezoneOffset;
  }
  else
  {
    root["time"] = false;
    root["timeOffset"] = 0;
  }

  root["resetReason"] = clearedResetReason ? 0 : ESP.getResetInfoPtr()->reason;
  root["stackTrace"] = SaveCrash.count() > 0;

  AsyncResponseStream *response = request->beginResponseStream("application/json");
  root.printTo(*response);

  request->send(response);
}


void handleConnectionStatus(AsyncWebServerRequest *request)
{
  _dln("API :: connection status");

  WiFiMode_t mode = WiFi.getMode();


  DynamicJsonBuffer jsonBuffer((2 * JSON_OBJECT_SIZE(2)) + JSON_OBJECT_SIZE(3));

  JsonObject& root = jsonBuffer.createObject();
  JsonObject& ap = root.createNestedObject("ap");
  ap["enabled"] = (mode == WIFI_AP || mode == WIFI_AP_STA);
  ap["ip"] = WiFi.softAPIP().toString();

  JsonObject& station = root.createNestedObject("station");
  station["enabled"] = (mode == WIFI_STA || mode == WIFI_AP_STA);
  station["status"] = (uint8_t)WiFi.status();
  station["ip"] = WiFi.localIP().toString();

  AsyncResponseStream *response = request->beginResponseStream("application/json");
  root.printTo(*response);

  request->send(response);
}


void handleGetConnection(AsyncWebServerRequest *request)
{
  _dln("API :: get connection");

  AsyncResponseStream *response = request->beginResponseStream("application/json");
  connectionSettings->toJson(*response);
  request->send(response);
}


void handlePostConnection(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total)
{
  _dln("API :: post connection");

  bool changed;
  if (connectionSettings->fromJson((char*)data, &changed))
  {
    connectionSettings->write();

    if (changed)
      connectionSettingsChanged = true;

    request->send(200);
  }
  else
    request->send(400);
}


void handleGetSystem(AsyncWebServerRequest *request)
{
  _dln("API :: get system");

  AsyncResponseStream *response = request->beginResponseStream("application/json");
  systemSettings->toJson(*response);
  request->send(response);
}


void handlePostSystem(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total)
{
  _dln("API :: post system");

  bool changed;
  if (systemSettings->fromJson((char*)data, &changed))
  {
    systemSettings->write();

    if (changed)
      systemSettingsChanged = true;

    request->send(200);
  }
  else
    request->send(400);
}


void handleGetSteps(AsyncWebServerRequest *request)
{
  _dln("API :: get steps");

  AsyncResponseStream *response = request->beginResponseStream("application/json");
  stepsSettings->toJson(*response);
  request->send(response);
}


void handlePostSteps(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total)
{
  _dln("API :: post steps");

  bool changed;
  if (stepsSettings->fromJson((char*)data, &changed))
  {
    stepsSettings->write();

    if (changed)
      stepsSettingsChanged = true;

    request->send(200);
  }
  else
    request->send(400);
}


void handleGetStackTrace(AsyncWebServerRequest *request)
{
  _dln("API :: get stack trace");

  if (SaveCrash.count() == 0)
  {
    request->send(404);
    return;
  }

  AsyncResponseStream *response = request->beginResponseStream("application/octet-stream");
  response->addHeader("Content-Disposition", "attachment; filename=\"stacktrace.txt\"");
  SaveCrash.print(*response);
  request->send(response);
}


void handleDeleteStackTrace(AsyncWebServerRequest *request)
{
  _dln("API :: delete stack trace");

  clearedResetReason = true;
  SaveCrash.clear();
  request->send(200);
}


#ifdef EnableCrashAPI
#pragma "!!! Crash API is enabled on this build !!!"

void handleCrashException(AsyncWebServerRequest *request)
{
  _dln("API :: crash exception");

  int* i = nullptr;
  *i = 42;
}

void handleCrashSoftWDT(AsyncWebServerRequest *request)
{
  _dln("API :: crash soft WDT");

  while (true);
}

void handleCrashWDT(AsyncWebServerRequest *request)
{
  _dln("API :: crash WDT");

  ESP.wdtDisable();
  while (true);
}
#endif


void registerSettingsRoutes(AsyncWebServer* server)
{
  server->on("/api/status", HTTP_GET, handleStatus);

  server->on("/api/connection/status", HTTP_GET, handleConnectionStatus);

  server->on("/api/connection", HTTP_GET, handleGetConnection);
  server->on("/api/connection", HTTP_POST, devNullRequest, devNullFileUpload, handlePostConnection);

  server->on("/api/system", HTTP_GET, handleGetSystem);
  server->on("/api/system", HTTP_POST, devNullRequest, devNullFileUpload, handlePostSystem);

  server->on("/api/steps", HTTP_GET, handleGetSteps);
  server->on("/api/steps", HTTP_POST, devNullRequest, devNullFileUpload, handlePostSteps);

  server->on("/api/stacktrace/get", HTTP_GET, handleGetStackTrace);
  server->on("/api/stacktrace/delete", HTTP_GET, handleDeleteStackTrace);

  #ifdef EnableCrashAPI
  server->on("/api/crash/exception", HTTP_GET, handleCrashException);
  server->on("/api/crash/softwdt", HTTP_GET, handleCrashSoftWDT);
  server->on("/api/crash/wdt", HTTP_GET, handleCrashWDT);
  #endif
}