#include "configuration.h"
#include "defines.h"
#include "LoRaWan_APP.h"

#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <Wire.h>  
#include "HT_SSD1306Wire.h"

#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <WiFiAP.h>

#include <sstream>
#include <string>
#include <iomanip>




const uint32_t MAGIC = 0xaffe0001;


config_t myConfig = { 
    .magic = MAGIC,
    .appEui = { 0, 0, 0, 0, 0, 0, 0, 0 },
    .appKey = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    .modbus_poll_slots = {
        { .typ = INPUT_REGISTERS, .id = 7, .address = 0x01 },
        { .typ = INPUT_REGISTERS, .id = 7, .address = 0x02 },
        { .typ = UNASSIGNED, .id = 0, .address = 0 },
    }
};

uint8_t devEui[8];
uint8_t appEui[8];
uint8_t appKey[16];
uint8_t nwkSKey[16];
uint8_t appSKey[16];
uint32_t devAddr;
LoRaMacRegion_t loraWanRegion;
bool overTheAirActivation;


SSD1306Wire  confDisplay(0x3c, 500000, SDA_OLED, SCL_OLED, GEOMETRY_128_64, RST_OLED);; // addr , freq , i2c group , resolution , rst
WebServer server(80);

const char *ssid = "ModbusLoraConf";

#define TEST_PASSWORD
#ifndef TEST_PASSWORD
char password[12];
#else
const char *password = "test1234";
#endif

extern uint8_t devEui[8];

bool configSaved = false;
int configParsingFailed = 0;
enum { PARSING_OK, PARSING_INVALID, PARSING_TOO_MANY, PARSING_TOO_FEW } configParsingError;

static void displayStatus(uint8_t numOfStations) {
	confDisplay.clear();
	confDisplay.display();
	confDisplay.drawString(1, 0, "Config Mode running");
    char buf[64];
    memset(buf, 0, sizeof(buf));
    sprintf(buf, "SSID: %s", ssid);
    confDisplay.drawString(1, 12, buf);
    memset(buf, 0, sizeof(buf));
    sprintf(buf, "Key: %s", password);
    confDisplay.drawString(1, 24, buf);
    confDisplay.display();
    memset(buf, 0, sizeof(buf));
    sprintf(buf, "Num of stations: %d", numOfStations);
    confDisplay.drawString(1, 36, buf);
    confDisplay.display();
}

static void displayInit() {
	digitalWrite(Vext,LOW);
	confDisplay.init();
	confDisplay.setFont(ArialMT_Plain_10);
	confDisplay.setTextAlignment(TEXT_ALIGN_LEFT);
    confDisplay.screenRotate(ANGLE_180_DEGREE);
	confDisplay.clear();
	confDisplay.drawString(1, 0, "Config Mode starting");
	confDisplay.display();
}


static void handleRoot() {
    // bool configValid = (configBlock.magic == MAGIC);
    std::stringstream buffer;
    buffer << "<!doctype html"
              "<html lang=\"en\">"
              "  <head>"
              "    <title>Modbus LoRaWAN Gateway</title>"
              "  </head>"
              "  <body>"
              "    <h1>Modbus LoRaWAN Gateway - Configuration Page</h1>";
    if (configSaved) {
        configSaved = false;
        buffer << "<h2>Configuration saved</h2>";
    }  
    if (configParsingFailed) {
        configParsingFailed = 0;
        buffer << "<h2>Error when parsing field " << configParsingFailed << ", error " << configParsingError << "</h2>";
    }  
    buffer << "    <form action=\"config\" method=\"POST\">"
              "    <table>"
              "      <tr>"
              "        <td>";
    buffer << "          <textarea name=\"config\" cols=\"50\" rows=\"40\">";

    buffer << "# Configuration of the Modbus LoRaWAN Gateway";

    buffer << "          </textarea";
    buffer << "        </td><td valign=\"top\">"
              "          Edit the textarea beside. <br/>"
              "          Make sure to keep all parameters within and follow the shown format.<br/>"
              "          You can use the # char to introduce line-based comments.<br/>"
              "        </td>"
              "      </tr>"
              "      <tr><td colspan=\"2\">"
              "        <button type=\"submit\">Save</button>"
              "      </td></tr>"
              "    </table>"
              "    </form>"
              "  </body"
              "</html";

    server.send(200, "text/html", buffer.str().c_str());
}

static void parseField(char* arg, int fieldNum, int reqTokens, uint8_t *dest) {
    int i = 0;
    for (char *part = strtok(arg, ":"); part; part = strtok(NULL, ":")) {
        Serial.println(part);
        char *errPtr;
        long v = strtol(part, &errPtr, 16);
        if (errPtr && *errPtr) {
            Serial.printf("some error happened, %p %02x\n\r", errPtr, *errPtr);
            configParsingFailed = fieldNum;
            configParsingError = PARSING_INVALID;
            break;
        }
        if (i >= reqTokens) {
            Serial.println("too many tokens");
            configParsingFailed = fieldNum;
            configParsingError = PARSING_TOO_MANY;
            break;
        }
        dest[i] = v;
        i++;
    }
    if ((i != reqTokens) && (configParsingFailed == 0)) {
        Serial.println("too few tokens");
        configParsingFailed = fieldNum;
        configParsingError = PARSING_TOO_FEW;
    }

}

static void handleConfigSave() {
    char *configText = (char*)server.arg("config").c_str();
    Serial.println(configText);


    configSaved = true;
    server.sendHeader("Location", String("/"), true);
    server.send(302, "text/plain", "");
}


void configurationSetup() {
#ifndef TEST_PASSWORD    
    memset(password, 0, sizeof(password));
    for (int i = 0; i < sizeof(password) - 1; i++) {
        password[i] = random(65, 90);
    }
#endif

    WiFi.softAP(ssid, password);
    IPAddress myIP = WiFi.softAPIP();

    displayInit();
    displayStatus(0);

    LoRaWAN.generateDeveuiByChipID();
    server.on("/", handleRoot);
    server.on("/config", handleConfigSave);
    server.begin();
}


void configurationLoop() {
    static uint8_t numOfStations = 255;
    uint8_t currentNumOfStations = WiFi.softAPgetStationNum();
    if (numOfStations != currentNumOfStations) {
        displayStatus(currentNumOfStations);
        numOfStations = currentNumOfStations;
    }

    server.handleClient();
}



void configLoad() {
    memcpy(appEui, myConfig.appEui, sizeof(appEui));
    memcpy(appKey, myConfig.appKey, sizeof(appKey));
    loraWanRegion = LORAMAC_REGION_EU868;
    overTheAirActivation = true;
}