#include "l_squirrel.h"
#include "l_squirrel_register.hpp"
#include <spdlog/spdlog.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/sinks/basic_file_sink.h>
#include <iostream>
#include <ctime>
#include <sstream>
#include <chrono>
#include <asio.hpp>
#include <asio/ssl.hpp>

static char szGamePathA[256];
void setupLogger()
{
    getConfigPath(szGamePathA, sizeof(szGamePathA));
    std::string Path = std::string(szGamePathA);
    std::string log_filename = "log/dps_log/" + Path.substr(Path.find("cfg") + 4) + "/log_";
    char time_buffer[100];
    std::time_t now = std::time(nullptr);
    std::tm *local_time = std::localtime(&now);
    strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%d_%H-%M-%S", local_time);
    log_filename += std::string(time_buffer) + ".txt";

    auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
    console_sink->set_level(spdlog::level::info);
    console_sink->set_pattern("[multi_sink_example] [%^%l%$] %v");

    auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(log_filename, true);
    file_sink->set_level(spdlog::level::info);
    file_sink->set_pattern("[%Y-%m-%d %H:%M:%S] [%^%l%$] %v");

    auto logger = std::make_shared<spdlog::logger>("logger", spdlog::sinks_init_list{console_sink, file_sink});
    logger->set_level(spdlog::level::info);
    logger->flush_on(spdlog::level::info);

    logger->info("欢迎使用Dps插件");

    spdlog::register_logger(logger);
    spdlog::set_default_logger(logger);
}

// 虚拟机对象
HSQUIRRELVM v;
// Lock
std::recursive_mutex SqMtx;

// static void printfunc(HSQUIRRELVM v, const SQChar *s, ...)
// {
//     fflush(stdout);
//     va_list vl;
//     va_start(vl, s);
//     scvprintf(stdout, s, vl);
//     va_end(vl);
//     printf("\n");
//     fflush(stdout);
// }
static void printfunc(HSQUIRRELVM v, const SQChar *s, ...)
{
    va_list vl;
    va_start(vl, s);
    char buf[1024];
    vsnprintf(buf, sizeof(buf), s, vl);
    va_end(vl);

    spdlog::info(buf);
}

// static void errorfunc(HSQUIRRELVM v, const SQChar *s, ...)
// {
//     fflush(stderr);
//     va_list vl;
//     va_start(vl, s);
//     scvprintf(stderr, s, vl);
//     va_end(vl);
//     printf("\n");
//     fflush(stderr);
// }
static void errorfunc(HSQUIRRELVM v, const SQChar *s, ...)
{
    va_list vl;
    va_start(vl, s);
    char buf[1024];
    vsnprintf(buf, sizeof(buf), s, vl);
    va_end(vl);

    spdlog::error(buf);
}

// 加密函数
static std::string encryptDecrypt(const std::string &input, const std::string &key)
{
    std::string output = input;
    for (size_t i = 0; i < input.size(); i++)
    {
        output[i] = input[i] ^ key[i % key.size()]; // 使用异或运算进行加密
    }
    return output;
}

// 判断是否处理加密
static std::string IsencryptDecrypt(const std::string &input, const std::string &FileName)
{
    if (FileName.find(".nut") != std::string::npos)
        return input;
    else
        return encryptDecrypt(input, "Rindro-Aurora");
}

static void ReloadingScript(HSQUIRRELVM v, std::string FilePath)
{
    // 爬取出所有的脚本文件
    std::vector<std::string> vec = Tool::GetListFilesR(FilePath);
    std::map<std::string, std::string> SquirrelFilePath;

    for (auto it = vec.cbegin(); it != vec.cend(); ++it)
    {
        std::string FileName = FilePath + *it;
        if (FileName.find(".nut") == std::string::npos && FileName.find(".sut") == std::string::npos)
            continue;
        std::fstream F;
        F.open((FileName).c_str(), std::ios::in);
        std::stringstream ContentStringStream;
        ContentStringStream << F.rdbuf();
        std::string ContentString(ContentStringStream.str());
        F.close();
        std::string RealContentString = IsencryptDecrypt(ContentString, FileName);
        SquirrelFilePath[FileName] = RealContentString;
    }

    std::map<std::string, std::string> SquirrelLastFilePath;

    for (auto it = SquirrelFilePath.begin(); it != SquirrelFilePath.end(); it++)
    {
        std::string Sourcename = it->first;
        std::string ContentString = it->second;
        if (SQ_SUCCEEDED(sq_compilebuffer(v, (SQChar *)(ContentString.c_str()), ContentString.length(), (SQChar *)(Sourcename.c_str()), true)))
        {
            sq_pushroottable(v);
            sq_call(v, 1, 1, 1);
            sq_pop(v, 1);
        }
        else
        {
            SquirrelLastFilePath[Sourcename] = ContentString;
        }
    }

    while (SquirrelLastFilePath.size() > 0)
    {
        std::map<std::string, std::string> FailMapBuffer;
        for (auto it = SquirrelLastFilePath.begin(); it != SquirrelLastFilePath.end(); it++)
        {
            std::string Sourcename = it->first;
            std::string ContentString = it->second;

            if (SQ_SUCCEEDED(sq_compilebuffer(v, (SQChar *)(ContentString.c_str()), ContentString.length(), (SQChar *)(Sourcename.c_str()), true)))
            {
                sq_pushroottable(v);
                if (SQ_FAILED(sq_call(v, 1, 1, 1)))
                {
                    FailMapBuffer[Sourcename] = ContentString;
                };
                sq_pop(v, 1);
            };
        }

        SquirrelLastFilePath.clear();
        if (FailMapBuffer.size() > 0)
        {
            for (auto it = FailMapBuffer.begin(); it != FailMapBuffer.end(); it++)
            {
                std::string Sourcename = it->first;
                std::string ContentString = it->second;
                SquirrelLastFilePath[Sourcename] = ContentString;
            }
        }
    }
}

static SQInteger SqReloadScript(HSQUIRRELVM v)
{
    ReloadingScript(v, "/dp_s/Dps_A/");
    ReloadingScript(v, "/dp_s/Dps_B/");
    ReloadingScript(v, "/dp_s/Dps_C/");
    return 0;
}

void InitSquirrel()
{
    std::cout << "OpenSSL version: " << OPENSSL_VERSION_TEXT << std::endl;

    asio::io_context io_context;
    asio::ssl::context ctx(asio::ssl::context::tlsv12_client);

    asio::ssl::stream<asio::ip::tcp::socket> socket(io_context, ctx);

    asio::ip::tcp::resolver resolver(io_context);
    auto endpoints = resolver.resolve("www.rindro.cn", "https");

    asio::connect(socket.lowest_layer(), endpoints);

    socket.handshake(asio::ssl::stream_base::handshake_type::client);

    std::string post_data = "key1=value1&key2=value2";
    std::string request = "POST /c/user/getServiceUrl HTTP/1.1\r\n";
    request += "Host: www.rindro.cn\r\n";
    request += "Content-Length: " + std::to_string(post_data.size()) + "\r\n";
    request += "Content-Type: application/x-www-form-urlencoded\r\n";
    request += "\r\n";
    request += post_data;

    asio::write(socket, asio::buffer(request));

    asio::streambuf response;
    asio::read_until(socket, response, "\r\n");

    std::istream response_stream(&response);
    std::string http_version;
    response_stream >> http_version;

    std::stringstream ss;
    ss << &response;
    std::string response_data = ss.str();
    std::cout << "Server Response: " << response_data << std::endl;

    std::lock_guard<std::recursive_mutex> lock(SqMtx);

    v = sq_open(4096); // 创建虚拟机，其栈的初始大小为1024

    sq_pushroottable(v);

    sqstd_register_bloblib(v);
    sqstd_register_iolib(v);
    sqstd_register_systemlib(v);
    sqstd_register_mathlib(v);
    sqstd_register_stringlib(v);
    sqstd_seterrorhandlers(v);

    setupLogger();
    sq_setprintfunc(v, printfunc, errorfunc); // sets the print function

    // 输出版本信息
    // scfprintf(stdout, _SC("%s %s (%d bits)\n"), SQUIRREL_VERSION, SQUIRREL_COPYRIGHT, (int)sizeof(SQInteger) * 8);

    // 注册全局NutApi
    GlobaRegisterSquirrel(v);

    // 加载基础脚本文件
    ReloadingScript(v, "/dp_s/Dps_A/");
    ReloadingScript(v, "/dp_s/Dps_B/");
    ReloadingScript(v, "/dp_s/Dps_C/");

    // 执行虚拟机Main函数
    SQInteger top = sq_gettop(v); // saves the stack size before the call
    sq_pushroottable(v);          // pushes the global table
    sq_pushstring(v, _SC("main"), -1);
    if (SQ_SUCCEEDED(sq_get(v, -2)))
    {                                   // gets the field 'foo' from the global table
        sq_pushroottable(v);            // push the 'this' (in this case is the global table)
        sq_call(v, 1, SQFalse, SQTrue); // calls the function
    }
    sq_settop(v, top); // restores the original stack size

    sq_pushroottable(v);
    sq_pushstring(v, "sq_ReloadScript", -1);
    sq_newclosure(v, SqReloadScript, 0); // create a new function
    sq_newslot(v, -3, SQFalse);
    sq_pop(v, 1); // pops the root table
}
