/*
 * Cppcheck - A tool for static C/C++ code analysis
 * Copyright (C) 2007-2023 Cppcheck team.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

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

#include "cppcheck.h"
#include "version.h"

static void unencode(const char *src, char *dest)
{
    for (; *src; src++, dest++) {
        if (*src == '+')
            *dest = ' ';
        else if (*src == '%') {
            unsigned int code;
            if (std::sscanf(src+1, "%2x", &code) != 1)
                code = '?';
            *dest = code;
            src += 2;
        } else
            *dest = *src;
    }
    *dest = '\0';
}

static FILE *logfile = nullptr;

class CppcheckExecutor : public ErrorLogger {
private:
    const std::time_t stoptime;
    CppCheck cppcheck;

public:
    CppcheckExecutor()
        : ErrorLogger()
        , stoptime(std::time(nullptr)+2U)
        , cppcheck(*this, false, nullptr) {
        cppcheck.settings().addEnabled("all");
        cppcheck.settings().certainty.enable(Certainty::inconclusive);
    }

    void run(const char code[]) {
        cppcheck.check("test.cpp", code);
    }

    void reportOut(const std::string & /*outmsg*/, Color /*c*/) override {}
    void reportErr(const ErrorMessage &msg) override {
        const std::string s = msg.toString(true);

        std::cout << s << std::endl;

        if (logfile != nullptr)
            std::fprintf(logfile, "%s\n", s.c_str());
    }

    void reportProgress(const std::string& /*filename*/,
                        const char /*stage*/[],
                        const std::size_t /*value*/) override {
        if (std::time(nullptr) >= stoptime) {
            std::cout << "Time to analyse the code exceeded 2 seconds. Terminating.\n\n";
            Settings::terminate();
        }
    }
};


int main()
{
    std::cout << "Content-type: text/html\r\n\r\n"
              << "<!DOCTYPE html>\n";

    char data[4096] = {0};

    const char *query_string = std::getenv("QUERY_STRING");
    if (query_string)
        std::strncpy(data, query_string, sizeof(data)-2);

    const char *lenstr = std::getenv("CONTENT_LENGTH");
    if (lenstr) {
        int len = std::min(1 + std::atoi(lenstr), (int)(sizeof(data) - 2));
        std::fgets(data, len, stdin);
    }

    if (data[4000] != '\0') {
        std::cout << "<html><body>For performance reasons the code must be shorter than 1000 chars.</body></html>";
        return EXIT_SUCCESS;
    }

    const char *pdata = data;
    if (std::strncmp(pdata, "code=", 5)==0)
        pdata += 5;

    char code[4096] = {0};
    unencode(pdata, code);

    logfile = std::fopen("democlient.log", "at");
    if (logfile != nullptr)
        std::fprintf(logfile, "===========================================================\n%s\n", code);

    std::cout << "<html><body>Cppcheck " CPPCHECK_VERSION_STRING "<pre>";

    CppcheckExecutor cppcheckExecutor;
    cppcheckExecutor.run(code);

    std::fclose(logfile);

    std::cout << "</pre>Done!</body></html>";

    return EXIT_SUCCESS;
}
