#include <iostream>
#include <QFile>
#include <qDebug>
#include <QSettings>
#include <QNetworkProxy>
#include "curl.h"
#include "MinHook.h"

extern "C" IMAGE_DOS_HEADER __ImageBase;

QString getDllDirectory() {
    WCHAR dllPath[MAX_PATH * 4];
    DWORD length = GetModuleFileNameW((HINSTANCE)&__ImageBase, dllPath, MAX_PATH * 4);
    if (length > 0 && length < MAX_PATH * 4) {
        QString path = QString::fromWCharArray(dllPath, length);
        int lastSlash = path.lastIndexOf('\\');
        if (lastSlash != -1)
            return path.left(lastSlash);
    }
    return "";
}

QSettings settings(getDllDirectory() + "\\LauncherOptions.ini", QSettings::IniFormat);

typedef CURLcode (*curl_easy_perform_t)(CURL *curl);
typedef CURLcode (*curl_easy_setopt_t)(CURL *curl, CURLoption option, ...);
typedef CURLcode (*curl_easy_getinfo_t)(CURL *curl, CURLINFO info, ...);

HMODULE curlModule = nullptr;
curl_easy_perform_t _curl_easy_perform = nullptr;
curl_easy_setopt_t _curl_easy_setopt = nullptr;
curl_easy_getinfo_t _curl_easy_getinfo = nullptr;


static CURLcode HookedCurlEasyPerform(CURL *curl)
{
    char *url_;
    _curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &url_);

    QString url = QString::fromLatin1(url_);
    if (url.contains(QRegExp(settings.value("url", "disabledByDefault").toString()))) {

        _curl_easy_setopt(curl, CURLOPT_PROXY, settings.value("proxy", "").toString().toStdString().c_str());

        CURLcode result = _curl_easy_perform(curl);
        _curl_easy_setopt(curl, CURLOPT_PROXY, nullptr);

        return result;
    };
    return _curl_easy_perform(curl);
}


class CustomProxyFactory : public QNetworkProxyFactory
{
public:
    CustomProxyFactory() {}
    ~CustomProxyFactory() {}

    QList<QNetworkProxy> queryProxy(const QNetworkProxyQuery &query = QNetworkProxyQuery()) override {
        QList<QNetworkProxy> proxies;

        QString url = query.url().toString();
        if (url.contains(QRegExp(settings.value("url", "disabledByDefault").toString()))) {

            QUrl urlProxy(settings.value("proxy", "").toString());

            QNetworkProxy::ProxyType proxyType(QNetworkProxy::HttpProxy);
            if (urlProxy.scheme().contains("socks"))
                proxyType = QNetworkProxy::Socks5Proxy;

            QNetworkProxy proxy;
            proxy.setType(proxyType);
            proxy.setHostName(urlProxy.host());
            proxy.setPort(urlProxy.port());
            proxy.setUser(urlProxy.userName());
            proxy.setPassword(urlProxy.password());

            proxies.append(proxy);
        }

        return proxies;
    }
};


void HookInstall()
{
    // hook qt http client
    CustomProxyFactory* customProxyFactory = new CustomProxyFactory();
    QNetworkProxyFactory::setApplicationProxyFactory(customProxyFactory);

    // hook curl http client
    curlModule = GetModuleHandleA("libcurl.dll");
    if (!curlModule) {
        qDebug() << "[HOOK] Failed to find libcurl.dll";
        return;
    }

    _curl_easy_setopt  =  (curl_easy_setopt_t) GetProcAddress(curlModule, "curl_easy_setopt");
    _curl_easy_getinfo = (curl_easy_getinfo_t) GetProcAddress(curlModule, "curl_easy_getinfo");
    FARPROC targetFunc = GetProcAddress(curlModule, "curl_easy_perform");

    if (MH_Initialize() != MH_OK) {
        qDebug() << "[HOOK] Failed to initialize MinHook";
        return;
    }

    if (MH_CreateHook(
            targetFunc,
            &HookedCurlEasyPerform,
            reinterpret_cast<LPVOID*>(&_curl_easy_perform)) != MH_OK) {
        qDebug() << "[HOOK] Failed to create hook for curl_easy_perform";
        return;
    }

    if (MH_EnableHook(targetFunc) != MH_OK) {
        qDebug() << "[HOOK] Failed to enable hook";
        return;
    }
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID lpReserved)
{
    if (reason == DLL_PROCESS_ATTACH) {
        HookInstall();
    }
    return TRUE;
}
