#include "proxy.h"
#include "spoofing.h"

#include <nerutils/log.h>

#include <windows.h>
#include <wininet.h>

bool setProxyAddress(bool enable, const std::string& proxyAddr)
{
    INTERNET_PER_CONN_OPTION_LIST list;
    INTERNET_PER_CONN_OPTION options[3];

    ZeroMemory(&list, sizeof(list));
    ZeroMemory(options, sizeof(options));

    options[0].dwOption = INTERNET_PER_CONN_FLAGS;

    if (enable)
    {
        if (proxyAddr.empty()) return false;

        options[0].Value.dwValue = PROXY_TYPE_PROXY | PROXY_TYPE_DIRECT;

        options[1].dwOption = INTERNET_PER_CONN_PROXY_SERVER;
        options[1].Value.pszValue = const_cast<char*>(proxyAddr.c_str());

        options[2].dwOption = INTERNET_PER_CONN_PROXY_BYPASS;
        options[2].Value.pszValue = (char*)"<local>";

        list.dwOptionCount = 3;
    }
    else
    {
        options[0].Value.dwValue = PROXY_TYPE_DIRECT;
        list.dwOptionCount = 1;
    }

    list.dwSize = sizeof(INTERNET_PER_CONN_OPTION_LIST);
    list.pszConnection = NULL;
    list.pOptions = options;

    if (!InternetSetOption(NULL, INTERNET_OPTION_PER_CONNECTION_OPTION, &list, sizeof(list)))
    {
        Log::error("Failed to set proxy options - error: {}", GetLastError());
        return false;
    }

    InternetSetOption(NULL, INTERNET_OPTION_SETTINGS_CHANGED, NULL, 0);
    InternetSetOption(NULL, INTERNET_OPTION_REFRESH, NULL, 0);

    return true;
}

bool running = true;
Proxy* proxy = nullptr;

void cleanup()
{
    static std::mutex cleanupMutex;
    std::lock_guard<std::mutex> lock(cleanupMutex);
    static bool cleaned = false;
    if (cleaned) return;
    cleaned = true;

    Log::info("Restoring system proxy settings");
    setProxyAddress(false, "");

    if (proxy)
    {
        Log::info("Shutting down proxy");
        proxy->shutdown();
    }
}

BOOL WINAPI consoleHandler(DWORD dwType)
{
    if (dwType == CTRL_C_EVENT || dwType == CTRL_CLOSE_EVENT || dwType == CTRL_LOGOFF_EVENT ||
        dwType == CTRL_SHUTDOWN_EVENT)
    {
        running = false;
        cleanup();
        exit(0);
    }
    return FALSE;
}

int main()
{
    Log::createConsole();
    SetConsoleCtrlHandler(consoleHandler, TRUE);
    atexit(cleanup);

    Log::info("Init");

    /*
        proxy setup
    */
    Log::info("Starting proxy");
    proxy = new Proxy();
    if (!proxy->init())
    {
        Log::error("Proxy failed to start");
        return 1;
    }
    proxy->addWhitelistDomain("bhvrdbd.com");
    setProxyAddress(true, std::format("127.0.0.1:{}", PROXY_PORT));

    /*
        Spoofer setup
    */
    Log::info("Spoofer init");
    Spoofer* spoofer = new Spoofer();

    spoofer->init(proxy);

    /*
        pause
    */
    Log::info("Proxy running (CTRL+C to stop)");
    while (running)
        Sleep(100);

    /*
        cleanup
    */
    cleanup();

    return 0;
}