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

#include <nerutils/log.h>

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

bool setProxy(bool enable, const std::string& proxyAddr)
{
    INTERNET_PER_CONN_OPTION_LIST list;
    INTERNET_PER_CONN_OPTION options[3];
    unsigned long listSize = sizeof(INTERNET_PER_CONN_OPTION_LIST);

    options[0].dwOption = INTERNET_PER_CONN_FLAGS;
    if (enable)
        options[0].Value.dwValue = PROXY_TYPE_PROXY | PROXY_TYPE_DIRECT;
    else
        options[0].Value.dwValue = 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 = const_cast<char*>("<local>");

    list.dwSize = sizeof(INTERNET_PER_CONN_OPTION_LIST);
    list.pszConnection = NULL;
    list.dwOptionCount = 3;
    list.dwOptionError = 0;
    list.pOptions = options;

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

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

    return true;
}

bool running = true;
Proxy* g_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");
    setProxy(false, "");

    if (g_Proxy)
    {
        Log::info("Shutting down proxy");
        g_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");
    g_Proxy = new Proxy();
    if (!g_Proxy->init())
    {
        Log::error("Proxy failed to start");
        return 1;
    }
    setProxy(true, std::format("127.0.0.1:{}", PROXY_PORT));

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

    spoofer->init(g_Proxy);

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

    /*
        cleanup
    */
    cleanup();

    return 0;
}