#include <SDL2/SDL_mouse.h>
#include <cstdio>
#include <cstdlib>
#include <SDL2/SDL.h>
#include <ctime>
#include <string>
#include <bits/stdc++.h>
#include <json11.hpp>

#include "imgui.h"
#include "serialib.h"

#include "ssup.h"

using namespace std;
using namespace json11;

string cutJSON(string input){
    int start = input.find_first_of('{');
    int end = input.find_last_of('}');
    
    return input.substr(start, (end - start) + 1);
}

void restartDevice(serialib &serial){
    serial.clearDTR();
    SDL_Delay(10);
    serial.setDTR();
}

void renderConnectModule(){

    if (ImGui::BeginTabItem(appLang == LANG::ENGLISH ? "Connect" : "Verbindung", NULL)){
        
        static bool firstScan = true;
        static bool scan = true;
        
        static bool wait = false;
        static vector<string> device;
        static int selectedDevice;
        static char error = 1;
        
        static string gotBuffer = "";
        static string fetchLine = "";
        static string currentLine = "";
        
        bool newContent = false;
        bool newLine = false;
        
        const string combo_preview_value = (device.size() == 0 ? (appLang == LANG::ENGLISH ? "-- No ports found --" : "-- Keine Ports gefunden --") : device[selectedDevice]);
        if (device.size() == 0) ImGui::PushStyleColor(ImGuiCol_Text, 0xFF555555);
        
        if (ImGui::BeginCombo("COM Port", combo_preview_value.c_str())) {
            for (int i = 0; i < (int)device.size(); i++) {
                const bool is_selected = selectedDevice == i;
                
                if (ImGui::Selectable(device[i].c_str(), is_selected)) selectedDevice = i;
                if (is_selected) ImGui::SetItemDefaultFocus();
            }
            ImGui::Separator();
            static char mbuffer[64] = {0};
            bool mtrue = ImGui::InputText("Manually specify port###MANPORT", mbuffer, sizeof(mbuffer), ImGuiInputTextFlags_EnterReturnsTrue);
            ImGui::SameLine();
            if (ImGui::Button("Ok###MANPORTBTN") || mtrue){
                if (strlen(mbuffer) > 0) {
                    device.clear();
                    device.push_back(mbuffer);
                    scan = false;
                }
            }
            
            ImGui::EndCombo();
        }
        
        if (device.size() == 0) ImGui::PopStyleColor();
        
        ImGui::SameLine();
        if (ImGui::Button("Rescan Ports") || ((time(NULL) % 32 == 0 || firstScan) && scan)) {
            device.clear();
            device.push_back("/dev/ttyUSB0");
        }
        
        firstScan = false;

        if (!connected && ImGui::Button(appLang == LANG::ENGLISH ? "Connect" : "Verbinden")){
            serial.closeDevice();
            error = serial.openDevice(device[selectedDevice].c_str(), 9600);
            restartDevice(serial);
        }
        
        if (connected && ImGui::Button(appLang == LANG::ENGLISH ? "Disconnect" : "Trennen")){
            serial.closeDevice();
            connected = false;
        }

        ImGui::SameLine();
        ImGui::BeginDisabled(!connected);
        if (ImGui::Button(appLang == LANG::ENGLISH ? "Restart SSUP" : "SSUP neustarten")){
            restartDevice(serial);
        }
        ImGui::EndDisabled();

        ImGui::Separator();

        if (error == 1){
            if (serial.isDeviceOpen()){
                ImGui::PushStyleColor(ImGuiCol_Text, 0xFF00AA00);
                ImGui::Text(appLang == LANG::ENGLISH ? "Connected" : "Verbunden");
                connected = true;
                
                config.eeprom_size = 16;
                config.toc_size = 512;
            } else {
                ImGui::PushStyleColor(ImGuiCol_Text, 0xFF0000FF);
                ImGui::Text(appLang == LANG::ENGLISH ? "Not connected" : "Nicht verbunden");
                connected = false;
            }
        } else {
            ImGui::PushStyleColor(ImGuiCol_Text, 0xFF0000FF);
            ImGui::Text(appLang == LANG::ENGLISH ? "Error opening %s" : "Fehler beim öffnen von %s", device[selectedDevice].c_str());
            connected = false;
        }
        ImGui::PopStyleColor();

        if (serial.available()){
            
            while (serial.available()){
                static uint8_t got;
                serial.readBytes(&got, 1);
                
                if (got == 0xFA) wait = true;
                else if (got == 0xFB) wait = false;
                else {
                    gotBuffer.push_back((char)got);
                    newContent = true;
                    
                    newLine = false;
                    if ((char)got == '\n' || (char)got == 0){
                        currentLine = fetchLine;
                        fetchLine = "";
                        newLine = true;
                        break;
                    } else  fetchLine.push_back((char)got);  
                    
                }
            }
        }

        bool jsonError = false;
        static int fetchedSettings = 0;
        static string jsonErrorString;
        
        if (newLine){
            if (currentLine == "SSUP") serial.writeString("g");
            else if (currentLine.find("!SYSINFO:") != string::npos){
                string jsonRawData = cutJSON(currentLine);
                
                jsonErrorString.clear();
                Json jsonData = Json::parse(jsonRawData, jsonErrorString);
                
                if (jsonErrorString.length() > 0) jsonError = true;
                else {
                    config.firmware = jsonData["FIRMWARE"].int_value();
                    config.model = jsonData["MODEL"].int_value();
                    config.textx = jsonData["TEXTX"].int_value();
                    config.texty = jsonData["TEXTY"].int_value();
                    config.imagex = jsonData["IMAGEX"].int_value();
                    config.imagey = jsonData["IMAGEY"].int_value();
                    
                    jsonError = false;
                    fetchedSettings++;
                }
                
            } else if (currentLine.find("!SETTINGS:") != string::npos){
                string jsonRawData = cutJSON(currentLine);
                
                jsonErrorString.clear();
                Json jsonData = Json::parse(jsonRawData, jsonErrorString);
                
                if (jsonErrorString.length() > 0) jsonError = true;
                else {
                    config.eeprom_address   = jsonData["EEPROM_ADDR"].int_value();
                    config.eeprom_size      = jsonData["EEPROM_SIZE"].int_value();
                    config.screen_address   = jsonData["SCREEN_ADDR"].int_value();
                    config.toc_size         = jsonData["TOC_SIZE"].int_value();
                    config.threshhold       = jsonData["THRESHHOLD"].int_value();
                    //config.= jsonData["PAGE"].int_value();
                    config.contrast         = jsonData["CONTRAST"].int_value();
                    config.locked           = (jsonData["LOCKED"].int_value() == 1);
                    
                    jsonError = false;
                    fetchedSettings++;
                }
                
                
            }
        }

        if (connected){
            if (ImGui::CollapsingHeader("SSUP Info:", ImGuiTreeNodeFlags_DefaultOpen)){
                if (jsonError){
                    ImGui::PushStyleColor(ImGuiCol_Text, 0xFFAAAAAA);
                    ImGui::Text(appLang == LANG::ENGLISH ? "Error while parsing device JSON!\n%s" : "Fehler beim interpretieren des JSON!\n%s", jsonErrorString.c_str());
                    ImGui::PopStyleColor();
                }
                
                ImGui::SmallButton("?###LOCKED"); ImGui::SameLine();
                ImGui::SetItemTooltip(appLang == LANG::ENGLISH ? 
                    "SSUP locked state. In case of emergency one can lock the device.\nThat means it won't show anything until it is unlocked right here.\nThe calculator stays functional troughout.\nHow you lock SSUP is model specific. Please refer to the manual or to the help tab" :
                    "SSUP Sperrstatus. Im Notfall kann man den Spicker sperren.\nDas bedeutet, dass der Spicker nichts anzeigen wird und nicht bedienbar ist, solange er hier nicht ensperrt wurde.\nDer Taschenrechner bleibt dabei funktionstüchtig.\nWie man den Spicker sperrt ist modellspezifisch und steht im Handbuch und im 'Hilfe' Tab.");
                ImGui::Text(appLang == LANG::ENGLISH ? "Locked:" : "Gesperrt:"); ImGui::SameLine();
                if (config.locked){
                    ImGui::PushStyleColor(ImGuiCol_Text, 0xFF0000FF);
                    ImGui::Text(appLang == LANG::ENGLISH ? "Yes" : "Ja");
                    ImGui::PopStyleColor();
                    ImGui::SameLine();
                    
                    if (ImGui::SmallButton(appLang == LANG::ENGLISH ? "Unlock" : "Entsperren")) {
                        serial.writeString("ug");
                    }
                } else {
                    ImGui::PushStyleColor(ImGuiCol_Text, 0xFF00AA00);
                    ImGui::Text(appLang == LANG::ENGLISH ? "No" : "Nein");
                    ImGui::PopStyleColor();
                }
                
                if (fetchedSettings < 1) ImGui::Text(appLang == LANG::ENGLISH ? "Fetching..." : "Verarbeiten...");
                else {
                    ImGui::Text("Firmware: %d", config.firmware);
                    ImGui::Text("Model: %d > %s", config.model, modelLookup[config.model - 1].c_str());
                }
            }
            
            if (ImGui::CollapsingHeader("Nerd Info:")){
                if (fetchedSettings < 2) ImGui::Text("Fetching...");
                else {
                    ImGui::Text("Text Size: %dx%d", config.textx, config.texty);
                    ImGui::Text("Image Size: %dx%d", config.imagex, config.imagey);
                    ImGui::Text("EEPROM Size: %d Kilobytes", config.eeprom_size);
                    ImGui::Text("EEPROM Address: %d", config.eeprom_address);
                    ImGui::Text("Screen Address: %d", config.screen_address);
                    ImGui::Text("TOC Size: %d Bytes", config.toc_size);
                    ImGui::Text("Threshhold: %d", config.threshhold);
                }
            }
            
        } else {
            ImGui::PushStyleColor(ImGuiCol_Text, 0xFFAAAAAA);
            ImGui::Text(appLang == LANG::ENGLISH ? "Please connect SSUP" : "Bitte SSUP verbinden");
            ImGui::PopStyleColor();
        }

        if (ImGui::CollapsingHeader("Log:", ImGuiTreeNodeFlags_DefaultOpen)) {
            ImGui::PushItemWidth(ImGui::GetWindowWidth()-20);
            
            ImGui::BeginChild(ImGui::GetID("###LOGFRAME"), ImVec2(0, 150), ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_Borders);
            ImGui::Text(gotBuffer.c_str());
            if (newContent) ImGui::SetScrollHereY(0.999f);
            ImGui::EndChild();
            
            ImGui::PopItemWidth();
            
            ImGui::BeginDisabled(!connected);
            static char commandBuffer[512];
            bool sendCommand = ImGui::InputText(appLang == LANG::ENGLISH ? "Send###commandText" : "Senden###commandText", commandBuffer, sizeof(commandBuffer),
                                                ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_ElideLeft);
            ImGui::SameLine();
            if (ImGui::Button(">>") || sendCommand) {
                serial.writeString(commandBuffer);
                commandBuffer[0] = 0;
            }
            ImGui::EndDisabled();
            
            if (ImGui::Button(appLang == LANG::ENGLISH ? "Clear Log" : "Log löschen")) gotBuffer.clear();
        }

        ImGui::EndTabItem();
        //ImGui::End();
    }
}
