#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include "main.h"


extern const uint8_t data_index_html_start[] asm("_binary_data_index_html_start");
extern const uint8_t data_index_html_end[] asm("_binary_data_index_html_end");
extern const uint8_t data_script_js_start[] asm("_binary_data_script_js_start");
extern const uint8_t data_script_js_end[] asm("_binary_data_script_js_end");
extern const uint8_t data_style_css_start[] asm("_binary_data_style_css_start");
extern const uint8_t data_style_css_end[] asm("_binary_data_style_css_end");

AsyncWebServer server(80);
// static AsyncWebSocket ws("/ws");
// static AsyncEventSource events("/events");
//
void handleNotFound(AsyncWebServerRequest *request) {
  request->send(404, "text/plain", "Not found");
}
//
// void onEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) {
//     if (type == WS_EVT_CONNECT) {
//         // client connected
//         info("ws[%s][%u] connect\n", server->url(), client->id());
//         // String str = get_settings();
//         // client->printf("%s", str.c_str());
//         client->ping();
//     } else if (type == WS_EVT_DISCONNECT) {
//     // client disconnected
//         info("ws[%s][%u] disconnect: %u\n", server->url(), client->id());
//     } else if (type == WS_EVT_ERROR) {
//     // error was received from the other end
//         info("ws[%s][%u] error(%u): %s\n", server->url(), client->id(),
//         *((uint16_t *)arg), (char *)data);
//     } else if (type == WS_EVT_PONG) {
//     // pong message was received (in response to a ping request maybe)
//         info("ws[%s][%u] pong[%u]: %s\n", server->url(), client->id(), len, (len) ? (char *)data : "");
//     } else if (type == WS_EVT_DATA) {
//     // data packet
//     AwsFrameInfo *info = (AwsFrameInfo *)arg;
//     if (info->final && info->index == 0 && info->len == len) {
//     // the whole message is in a single frame and we got all of it's
//     // data info("ws[%s][%u] %s-message[%llu]: ", server->url(),
//     // client->id(), (info->opcode == WS_TEXT) ? "text" : "binary",
//     // info->len);
//     if (info->opcode == WS_TEXT) {
//         data[len] = 0;
//         info("data: %s\n", (char *)data);
//         //        parse_cmd((char *)data, client);
//     }
//     }
//     }
//   }
//
static void redirectHomeResponse(AsyncWebServerRequest *request, String location) {
  AsyncWebServerResponse *response = request->beginResponse(303, "text/plain", "");
  response->addHeader("Location", location);
  response->addHeader("Access-Control-Allow-Origin", "*");
  request->send(response);
}
//
  static void insertPage(AsyncWebServerRequest *request) {
    uint32_t address = 0;
    int ric = -1;
    int fun = 0;
    String message;
    //
    if (request->hasArg("text")) {
        message = request->arg("text");
        message = txHandleUmlauts(message);
    }
    if (request->hasArg("ric")) {
        ric = request->arg("ric").toInt();
        address = ric + 0;
    }
    if (request->hasArg("fun")) {
        fun = request->arg("fun").toInt();
    }
    Serial.printf("RIC: %d -> %d MSG: %s\n\n", ric, address, message);
   
    if (ric > 0) {
        char *messageText = new char[message.length() + 1]{};
        message.toCharArray(messageText, message.length() + 1);
        // messageText[ message.length() ] = 0x20;
        txControllerInsert(address, fun + 0, messageText);
        delete []messageText;

        if (request->hasArg("addtx")) {
          txControllerBatchStart();
        }
    }
    redirectHomeResponse(request, "/");
  }
  //
  static void triggerTimeBeacon(AsyncWebServerRequest *request) {
    broadcastTriggerTimeBeacon();
    redirectHomeResponse(request, "/#time");
  }
  
  static void transmit(AsyncWebServerRequest *request) {
    txControllerBatchStart();
    redirectHomeResponse(request, "/");
  }
  static void clearQueue(AsyncWebServerRequest *request) {
    redirectHomeResponse(request, "/");
  }
  static void resetConfigReboot(AsyncWebServerRequest *request) {
    redirectHomeResponse(request, "/");
    create_default_config();
    esp_cpu_reset(1);
    esp_cpu_reset(0);
  }
  static void justReboot(AsyncWebServerRequest *request) {
    redirectHomeResponse(request, "/");
    esp_cpu_reset(1);
    esp_cpu_reset(0);
  }
  //
  static void setAPCfg(AsyncWebServerRequest *request) {
    String ssid, pass;
    //
    if (request->hasArg("ssid")) ssid = request->arg("ssid");
    if (request->hasArg("pass")) pass = request->arg("pass");
    cfg_startTransaction();
    cfg_adjust("ap_ssid", ssid);
    cfg_adjust("ap_pass", pass);
    cfg_write();
    redirectHomeResponse(request, "/#wifi");
  }
  static void setWifi1Cfg(AsyncWebServerRequest *request) {
    String ssid, pass;
    //
    if (request->hasArg("ssid")) ssid = request->arg("ssid");
    if (request->hasArg("pass")) pass = request->arg("pass");
    cfg_startTransaction();
    cfg_adjust("wifi1_ssid", ssid);
    cfg_adjust("wifi1_pass", pass);
    cfg_write();
    redirectHomeResponse(request, "/#wifi");
  }
  static void setWifi2Cfg(AsyncWebServerRequest *request) {
    String ssid, pass;
    //
    if (request->hasArg("ssid")) ssid = request->arg("ssid");
    if (request->hasArg("pass")) pass = request->arg("pass");
    cfg_startTransaction();
    cfg_adjust("wifi2_ssid", ssid);
    cfg_adjust("wifi2_pass", pass);
    cfg_write();
    redirectHomeResponse(request, "/#wifi");
  }
  //
  static void setTransmitterCfg(AsyncWebServerRequest *request) {
    float tx_freq, tx_dev;
    int tx_power, tx_baud;
    bool tx_empty_queue, pocsag_german;
    //
    cfg_startTransaction();
    if (request->hasArg("tx_freq")) {
      tx_freq = request->arg("tx_freq").toFloat();
      cfg_adjust("tx_freq", tx_freq);
    }
    if (request->hasArg("tx_baud")) {
      tx_baud = request->arg("tx_baud").toInt();
      cfg_adjust("tx_baud", tx_baud);
    }
    if (request->hasArg("tx_dev")) {
      tx_dev = request->arg("tx_dev").toFloat();
      cfg_adjust("tx_dev", tx_dev);
    }
    if (request->hasArg("tx_power")) {
      tx_power = request->arg("tx_power").toInt();
      cfg_adjust("tx_power", tx_power);
    }
    if (request->hasArg("tx_empty_queue")) {
      tx_empty_queue = request->arg("tx_empty_queue").equals("on");
      cfg_adjust("tx_empty_queue", tx_empty_queue);
    } else {
      cfg_adjust("tx_empty_queue", false);
    }
    if (request->hasArg("pocsag_german")) {
      pocsag_german = request->arg("pocsag_german").equals("on");
      cfg_adjust("pocsag_german", pocsag_german);
    } else {
      cfg_adjust("pocsag_german", false);
    }    
    cfg_write();
    redirectHomeResponse(request, "/#config");
  }
  static void setMQTTCfg(AsyncWebServerRequest *request) {
    String mqtt_host, mqtt_user, mqtt_pass, mqtt_topic;
    int mqtt_port;
    //
    cfg_startTransaction();
    if (request->hasArg("mqtt_host")) {
      mqtt_host = request->arg("mqtt_host");
      cfg_adjust("mqtt_host", mqtt_host);
    }
    if (request->hasArg("mqtt_topic")) {
      mqtt_topic = request->arg("mqtt_topic");
      cfg_adjust("mqtt_topic", mqtt_topic);
    }
    if (request->hasArg("mqtt_user")) {
      mqtt_user = request->arg("mqtt_user");
      cfg_adjust("mqtt_user", mqtt_user);
    }
    if (request->hasArg("mqtt_pass")) {
      mqtt_pass = request->arg("mqtt_pass");
      cfg_adjust("mqtt_pass", mqtt_pass);
    }
    if (request->hasArg("mqtt_port")) {
      mqtt_port = request->arg("mqtt_port").toInt();
      cfg_adjust("mqtt_port", mqtt_port);
    }
    cfg_write();
    redirectHomeResponse(request, "/#config");
  }
  //
  static void setDWDMoWaS(AsyncWebServerRequest *request) {
    int broadcast_ric, broadcast_fun;
    bool dwd_enable, mowas_enable;
    int dwd_interval, mowas_interval;
    int dwd_fun, mowas_fun;
    String dwd_region, mowas_region;
    cfg_startTransaction();

    //broadcast
    if (request->hasArg("broadcast_ric")) {
      broadcast_ric = request->arg("broadcast_ric").toFloat();
      cfg_adjust("broadcast_ric", broadcast_ric);
    }
    if (request->hasArg("broadcast_fun")) {
      broadcast_fun = request->arg("broadcast_fun").toInt();
      cfg_adjust("broadcast_fun", broadcast_fun);
    }

    // dwd
    if (request->hasArg("dwd_enable")) {
      dwd_enable = request->arg("dwd_enable").equals("on");
      cfg_adjust("dwd_enable", dwd_enable);
    } else {
      cfg_adjust("dwd_enable", false);
    }
    if (request->hasArg("dwd_interval")) {
      dwd_interval = request->arg("dwd_interval").toInt();
      cfg_adjust("dwd_interval", dwd_interval);
    }
    if (request->hasArg("dwd_fun")) {
      dwd_fun = request->arg("dwd_fun").toInt();
      cfg_adjust("dwd_fun", dwd_fun);
    }
    if (request->hasArg("dwd_region")) {
      dwd_region = request->arg("dwd_region");
      cfg_adjust("dwd_region", dwd_region);
    }
    // mowas
    if (request->hasArg("mowas_enable")) {
      mowas_enable = request->arg("mowas_enable").equals("on");
      cfg_adjust("mowas_enable", mowas_enable);
    } else {
      cfg_adjust("mowas_enable", false);
    }
    if (request->hasArg("mowas_interval")) {
      mowas_interval = request->arg("mowas_interval").toInt();
      cfg_adjust("mowas_interval", mowas_interval);
    }
    if (request->hasArg("mowas_fun")) {
      mowas_fun = request->arg("mowas_fun").toInt();
      cfg_adjust("mowas_fun", mowas_fun);
    }
    if (request->hasArg("mowas_region")) {
      mowas_region = request->arg("mowas_region");
      cfg_adjust("mowas_region", mowas_region);
    }
    
    cfg_write();
    redirectHomeResponse(request, "/#dwdmowas");
  }
  //
  static void setTimebeacon(AsyncWebServerRequest *request) {
    int time_ric, time_fun;
    bool time_enable;
    int time_interval, time_mode;
    String time_zone;
    cfg_startTransaction();
    if (request->hasArg(CFG_TIME_RIC)) {
      time_ric = request->arg(CFG_TIME_RIC).toFloat();
      cfg_adjust(CFG_TIME_RIC, time_ric);
    }
    if (request->hasArg("time_fun")) {
      time_fun = request->arg("time_fun").toInt();
      cfg_adjust("time_fun", time_fun);
    }
    if (request->hasArg("time_enable")) {
      time_enable = request->arg("time_enable").equals("on");
      cfg_adjust("time_enable", time_enable);
    } else {
      cfg_adjust("time_enable", false);
    }
    if (request->hasArg("time_interval")) {
      time_interval = request->arg("time_interval").toInt();
      cfg_adjust("time_interval", time_interval);
    }
    if (request->hasArg("time_mode")) {
      time_mode = request->arg("time_mode").toInt();
      cfg_adjust("time_mode", time_mode);
    }
    if (request->hasArg("time_zone")) {
      time_zone = request->arg("time_zone");
      cfg_adjust("time_zone", time_zone);
    }
    cfg_write();
    redirectHomeResponse(request, "/#timebeacon");
  }
  static void setIdlebeacon(AsyncWebServerRequest *request) {
    bool idle_enable;
    int idle_interval, idle_mode;
    cfg_startTransaction();
    if (request->hasArg("idle_enable")) {
      idle_enable = request->arg("idle_enable").equals("on");
      cfg_adjust("idle_enable", idle_enable);
    } else {
      cfg_adjust("idle_enable", false);
    }
    if (request->hasArg("idle_interval")) {
      idle_interval = request->arg("idle_interval").toInt();
      cfg_adjust("idle_interval", idle_interval);
    }
    if (request->hasArg("idle_mode")) {
      idle_mode = request->arg("idle_mode").toInt();
      cfg_adjust("idle_mode", idle_mode);
    }
    cfg_write();
    redirectHomeResponse(request, "/#idle");
  }
  //
  static void setDevCfg(AsyncWebServerRequest *request) {
    int oled_timeout;
    //
    cfg_startTransaction();
    if (request->hasArg("oled_timeout")) {
      oled_timeout = request->arg("oled_timeout").toInt();
      cfg_adjust("oled_timeout", oled_timeout);
    }
    if (request->hasArg("device_name")) {
        String device_name = request->arg("device_name");
        cfg_adjust("device_name", device_name);
    }
    cfg_write();
    redirectHomeResponse(request, "/#config");
  }
//
void webserver_setup() {
  // ws.onEvent(onEvent);
  // server.addHandler(&ws);

  // // attach AsyncEventSource
  // server.addHandler(&events);
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request)
  {
    AsyncWebServerResponse *response = request->beginResponse_P(
        200, "text/html", data_index_html_start,
        data_index_html_end - data_index_html_start - 1);
    response->addHeader("Access-Control-Allow-Origin", "*");
    request->send(response);
  });
  server.on("/script.js", HTTP_GET, [](AsyncWebServerRequest *request)
  {
    AsyncWebServerResponse *response = request->beginResponse_P(
        200, "application/javascript", data_script_js_start,
        data_script_js_end - data_script_js_start - 1);
    response->addHeader("Access-Control-Allow-Origin", "*");
    request->send(response);
  });
  server.on("/style.css", HTTP_GET, [](AsyncWebServerRequest *request)
  {
    AsyncWebServerResponse *response = request->beginResponse_P(
        200, "text/css", data_style_css_start,
        data_style_css_end - data_style_css_start - 1);
    response->addHeader("Access-Control-Allow-Origin", "*");
    request->send(response);
  });
  server.on("/config.json", HTTP_GET, [](AsyncWebServerRequest *request) {
    String output = cfg_tostring();
    AsyncWebServerResponse *response =
        request->beginResponse(200, "application/json", output);
    response->addHeader("Access-Control-Allow-Origin", "*");
    request->send(response);
  });
  server.on("/contacts.json", HTTP_GET, [](AsyncWebServerRequest *request) {
    String output = contacts_tostring();
    AsyncWebServerResponse *response =
        request->beginResponse(200, "application/json", output);
    response->addHeader("Access-Control-Allow-Origin", "*");
    request->send(response);
  });
  server.on("/contacts.json", HTTP_POST, [](AsyncWebServerRequest * request){
    if (request->hasParam("body", true)) { // This is important, otherwise the sketch will crash if there is no body
      contacts_write(request->getParam("body", true)->value());
      request->send(200, "text/plain", "ok\n");
    } else {
      Serial.println("No body?!");
      request->send(200, "text/plain", "No body?!\n");
    }
  });

  server.on("/setap", setAPCfg);
  server.on("/setwifi1", setWifi1Cfg);
  server.on("/setwifi2", setWifi2Cfg);
  server.on("/settransmitter", setTransmitterCfg);
  server.on("/setmqtt", setMQTTCfg);
  server.on("/setdwdmowas", setDWDMoWaS);
  server.on("/settimebeacon", setTimebeacon);
  server.on("/setidlebeacon", setIdlebeacon);
  server.on("/setdev", setDevCfg);

  server.on("/page", insertPage);
  server.on("/trigger-time", triggerTimeBeacon);
  server.on("/transmit", transmit);
  server.on("/clear", clearQueue);
  server.on("/rstcfgrbt", resetConfigReboot);
  server.on("/reboot", justReboot);

  server.onNotFound(handleNotFound);
  server.begin();
}