#include "proxy-configurator.h"

#include "win-platform.h"
#include "log-sink-cout.h"
#include "log-sink-file.h"

#include <minwinbase.h>
#include <wininet.h>
#include <errhandlingapi.h>

#include <seallib/log.h>

ProxyConfigurator::ProxyConfigurator()
{
    _log = new seallib::Logger("ProxyConfigurator");
    _log->addSink(std::make_shared<ConOutSink>());
    _log->addSink(FileSink::getSharedInstance());

    _log->verbose("ProxyConfigurator instantiated");
}

ProxyConfigurator::~ProxyConfigurator()
{
    _log->verbose("ProxyConfigurator destructing");
    clearProxy();
    delete _log;
}

bool ProxyConfigurator::setProxy(const char* ip, unsigned short port)
{
    std::string proxyAddr = std::format("{}:{}", ip, port);

    INTERNET_PER_CONN_OPTION_LIST list;
    INTERNET_PER_CONN_OPTION options[3];

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

    options[0].dwOption = INTERNET_PER_CONN_FLAGS;
    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;

    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);

    _log->info("Set proxy to {}:{}", ip, port);

    return true;
}

bool ProxyConfigurator::clearProxy()
{
    INTERNET_PER_CONN_OPTION_LIST list;
    INTERNET_PER_CONN_OPTION options[3];

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

    options[0].dwOption = INTERNET_PER_CONN_FLAGS;
    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 clear proxy - error: {}", GetLastError());
        return false;
    }

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

    _log->info("Cleared proxy settings");

    return false;
}
