/*******************************************************************************
 * Copyright (c) 2014, Jan Koester jan.koester@gmx.net
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer in the
 *      documentation and/or other materials provided with the distribution.
 * Neither the name of the <organization> nor the
 *      names of its contributors may be used to endorse or promote products
 *      derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *******************************************************************************/

#include <iostream>
#include <sstream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>

#include <config.h>

#ifndef Windows
#include <unistd.h>
#else
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <VersionHelpers.h>
#endif

#include <http.h>
#include <httpd.h>
#include <exception.h>

#include <netplus/eventapi.h>


#include "htmlpp/exception.h"
#include "htmlpp/html.h"

#include "header_png.h"
#include "favicon_ico.h"

#ifndef Windows
#include <sys/utsname.h>
#include <sys/resource.h>
#endif // !Windows

#define INDEXPAGE "<!DOCTYPE html><html lang=\"en\" ><head><title>sysinfo</title></head><body style=\"color:rgb(239, 240, 241); background:rgb(79, 83, 88);\">\
<div id=\"mainbar\" style=\"border-radius: 38px; background:rgb(35, 38, 41); width:1280px; margin:0px auto;\">\
<div id=\"headerimage\"><img alt=\"headerimage\" src=\"images/header.png\"></div>\
<div id=\"sysinfo\" style=\"padding:40px 20px;\"><span class=\"sysheader\">System Info:</span><br>\
</div></div></body></html>"

libhtmlpp::HtmlPage     SysPage;
libhtmlpp::HtmlElement  RootNode;

class Sysinfo{
public:

    const char *getArchText(int arch){
        switch (arch){
        case 6:
            return "x64";
        case 9:
            return "x64";
        case 0:
            return "x32";
        default:
            return "Warning: architecture detected, but not supported in this example";
        }
    }

    void TimeInfo(libhtmlpp::HtmlElement &index) {
        time_t t = time(0);
        tm* mytime = localtime(&t);
        libhtmlpp::HtmlString html;
        html << "<div>"
             << "<span>Date & Time:</span><br/>"
             << "<span>Date:" << mytime->tm_mday << "."
                              << mytime->tm_mon << "."
                              << mytime->tm_year << " </span>"
                              << "<span>Time:" << mytime->tm_hour << ":"
                              << mytime->tm_min << ":"
                              << mytime->tm_sec << "</span>"
            << "</div>";
        libhtmlpp::HtmlElement *sysdiv=nullptr;
        sysdiv=index.getElementbyID("sysinfo");
        if(sysdiv)
            sysdiv->appendChild(html.parse());
    }
    
    void KernelInfo(libhtmlpp::HtmlElement &index){
        #ifndef Windows
        struct utsname usysinfo;
        uname(&usysinfo);
        /*create htmltable widget*/
        libhtmlpp::HtmlTable htmltable;

        /*create table rows*/
        htmltable << (libhtmlpp::HtmlTable::Row() << "Operating system:" << usysinfo.sysname);
        htmltable << (libhtmlpp::HtmlTable::Row() << "Release Version :" << usysinfo.release);
        htmltable << (libhtmlpp::HtmlTable::Row() << "Hardware        :" << usysinfo.machine);

        libhtmlpp::HtmlElement table;
        /*convert htmltable to dom HtmlElement*/
        htmltable.insert(&table);
        /*set css class for the table*/
        table.setAttribute("class","kinfo");

        /*HtmlString Accepts Html Raw input*/
        libhtmlpp::HtmlString html;
        html << "<div><span>KernelInfo:</span> <br/> </div> ";
        /*convert Htmlstring to Html Dom element*/
        libhtmlpp::HtmlElement *div=(libhtmlpp::HtmlElement*)&html.parse();

        /*append table to dom element div*/
        div->appendChild(&table);

        /* search HtmlElement with id sysinfo */
        libhtmlpp::HtmlElement *sysdiv=index.getElementbyID("sysinfo");
        if(sysdiv)
            /*append table to dom element sysdiv*/
            sysdiv->appendChild(div);
        #else

        /*create htmltable widget*/
        libhtmlpp::HtmlTable htmltable;

        libhtmlpp::HtmlString rver;
        if (IsWindows10OrGreater()) {
            rver = "10+";
        }
        else if (IsWindows8Point1OrGreater()) {
            rver = "8.1";
        }
        else if (IsWindows8OrGreater()) {
            rver = "8";
        }
        else if (IsWindows7OrGreater()) {
            rver = "7";
        }
        else if (IsWindowsVistaOrGreater()) {
            rver = "Vista";
        }
        else if (IsWindowsXPOrGreater()) {
            rver = "XP";
        }
        else {
            rver = "Unbekannt";
        }

        SYSTEM_INFO usysinfo;
        memset(&usysinfo, 0, sizeof(SYSTEM_INFO));
        GetSystemInfo(&usysinfo);

        /*create table rows*/
        htmltable << (libhtmlpp::HtmlTable::Row() << "Operating system: " << "Windows");
        htmltable << (libhtmlpp::HtmlTable::Row() << "Release Version: " << rver);
        htmltable << (libhtmlpp::HtmlTable::Row() << "Hardware: " << getArchText(usysinfo.wProcessorArchitecture));

        libhtmlpp::HtmlElement table;
        /*convert htmltable to dom HtmlElement*/
        htmltable.insert(&table);
        /*set css class for the table*/
        table.setAttribute("class", "kinfo");

        /*HtmlString Accepts Html Raw input*/
        libhtmlpp::HtmlString html;
        html << "<div><span>KernelInfo:</span> <br/> </div> ";
        /*convert Htmlstring to Html Dom element*/
        libhtmlpp::HtmlElement* div = (libhtmlpp::HtmlElement*)&html.parse();

        /*append table to dom element div*/
        div->appendChild(&table);

        /* search HtmlElement with id sysinfo */
        libhtmlpp::HtmlElement* sysdiv = index.getElementbyID("sysinfo");
        if (sysdiv)
            /*append table to dom element sysdiv*/
            sysdiv->appendChild(div);
        #endif
    }

};

class Controller : public libhttppp::HttpEvent {
public:
    Controller(std::vector<netplus::socket*> serversocket) : HttpEvent(serversocket){
        
    };
    
    void IndexController(libhttppp::HttpRequest &curreq){
        try{
            libhttppp::HttpResponse curres;
            curres.setState(HTTP200);
            std::cout << curreq.getRequestURL() << std::endl;
            if(curreq.getRequestURL()=="/"){
                curres.setContentType("text/html");
                libhtmlpp::HtmlString html;
                libhtmlpp::HtmlElement index;
                index=RootNode;
                Sysinfo sys;
                sys.TimeInfo(index);
                sys.KernelInfo(index);
                libhtmlpp::print(index,html);
                curres.send(curreq,html.data(),html.length());
            }else if(curreq.getRequestURL()=="/images/header.png"){
                curres.setContentType("image/png");
                curres.setContentLength(header_png_size);
                curres.send(curreq,"", -1);
            }else if(curreq.getRequestURL()=="/favicon.ico"){
                curres.setContentType("image/x-icon");
                curres.setContentLength(favicon_ico_size);
                curres.send(curreq,"", -1);
            }else{
                curres.setState(HTTP404);
                curres.send(curreq,"", 0);
            }
        }catch(libhttppp::HTTPException &e){
            std::cerr << e.what() << std::endl;
        }
    }
    /*virtual method from event will call for every incomming Request*/
    void RequestEvent(libhttppp::HttpRequest& curreq, const int /*tid*/,ULONG_PTR /*args*/){
        try{
            /*self implemented controller see above*/
            IndexController(curreq);
        }catch(libhttppp::HTTPException &e){
            std::cerr << e.what() <<std::endl;
        }catch (const std::exception& e) {
            std::cerr << e.what() << std::endl;
        }catch (...) {
        }
    }

    void ResponseEvent(libhttppp::HttpRequest& curreq, const int /*tid*/,ULONG_PTR /*args*/){
        if (curreq.getRequestURL()=="/images/header.png") {
            size_t rem = header_png_size - curreq.SendData.pos;
            size_t si  = (rem > CHUNKSIZE) ? CHUNKSIZE : rem;
            curreq.SendData.append((const char*)header_png + curreq.SendData.pos, si);
            curreq.SendData.pos += si;
        } else if(curreq.getRequestURL()=="/favicon.ico"){
            if(curreq.SendData.pos < favicon_ico_size){
                size_t si = CHUNKSIZE < (favicon_ico_size-curreq.SendData.pos) ? CHUNKSIZE : (favicon_ico_size-curreq.SendData.pos);
                curreq.SendData.append((const char*)favicon_ico+curreq.SendData.pos, si);
                curreq.SendData.pos+=si;
            }
        }
    }
private:

};



class HttpConD : public libhttppp::HttpD {
public:
    HttpConD(int argc, char *argv[]) : HttpD(argc, argv) {
        Controller controller(getServerSockets());
        controller.runEventloop();
    };
};

int main(int argc, char* argv[]) {
    try{
        /*loads Macro INDEXPAGE into class HtmlPage for parsing and
         *return the first element from indexpage normaly <!DOCTYPE html>
         */
        SysPage.loadString(RootNode,INDEXPAGE);
        /*parse cmd for port , bind address and etc ...
         *after that mainloop will started so you
         * to send interuppt signal if you want running the code afterwards
         */
        HttpConD httpConD(argc,argv);
        return 0;
    }catch(libhttppp::HTTPException &e){
        std::cerr << e.what() <<std::endl;
        if(e.getErrorType()==libhttppp::HTTPException::Note 
            || libhttppp::HTTPException::Warning)
            return 0;
        else
            return -1;
    };
}
