#include "main.h"
#include <windows.h>

std::vector<windowInfo> winvector;
int countWindows;

BOOL CALLBACK EnumChildProc(HWND hwnd, LPARAM lparam) {
    TCHAR strName[MAX_PATH];
    CHAR strText[MAX_PATH];

    int idControl = GetDlgCtrlID(hwnd);
    if (idControl && GetClassName(hwnd, strName, MAX_PATH) && GetWindowText(hwnd, strText, MAX_PATH)) {
        winvector.push_back({idControl, strName, strText});
    }

    return TRUE;
}

BOOL CALLBACK EnumWindowsVisibleProc(HWND hwnd, LPARAM lparam) {
    if (hwnd == (HWND)lparam) {
        if (IsWindowVisible(hwnd)) {
            winvector.push_back({0, "", ""});
        }
        return FALSE;
    } 

    return TRUE;
}

BOOL CALLBACK EnumWindowsByProcessProc(HWND hwnd, LPARAM lparam) {
    windowFilter* winfilter = reinterpret_cast<windowFilter*>(lparam);
    
    DWORD pid;
    GetWindowThreadProcessId(hwnd, &pid);

    DWORD dwSize = MAX_PATH;
    CHAR exeName[MAX_PATH];

    if (!winfilter->exeName.empty()) {
        HANDLE handle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, pid);
        if (!handle || !QueryFullProcessImageName(handle, 0, exeName, &dwSize)) {
            exeName[0] = '\0';
        }
    }

    bool passfilter = true;

    if (passfilter && winfilter->pid != 0 && pid != winfilter->pid) {
        passfilter = false;
    }

    if (passfilter && !winfilter->exeName.empty()) {
		PCHAR pch = strstr(exeName, winfilter->exeName.c_str());
        if (!pch) {
            passfilter = false;
        }
    }

    if (passfilter) {
        countWindows++;
    }

    return TRUE;
}

napi_value getActiveWindow(napi_env env, napi_callback_info args) {
    napi_value obj;
    napi_value value;
    napi_status status;

    HWND hwnd = GetForegroundWindow();
    if (!hwnd) return nullptr;

    DWORD pid;
    GetWindowThreadProcessId(hwnd, &pid);

    DWORD dwSize = MAX_PATH;
    CHAR exeName[MAX_PATH];

    HANDLE handle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, pid);
    if (!handle || !QueryFullProcessImageName(handle, 0, exeName, &dwSize)) {
		exeName[0] = '\0';
    }

    status = napi_create_object(env, &obj);
    if (status != napi_ok) return nullptr;

    status = napi_create_int64(env, reinterpret_cast<int64_t>(hwnd), &value);
    if (status != napi_ok) return nullptr;
  
    status = napi_set_named_property(env, obj, "handle", value);
    if (status != napi_ok) return nullptr;

    status = napi_create_int32(env, int(pid), &value);
    if (status != napi_ok) return nullptr;

    status = napi_set_named_property(env, obj, "pid", value);
    if (status != napi_ok) return nullptr;

    status = napi_create_string_utf8(env, exeName, NAPI_AUTO_LENGTH, &value);
    if (status != napi_ok) return nullptr;
  
    status = napi_set_named_property(env, obj, "path", value);
    if (status != napi_ok) return nullptr;

    return obj;
}

napi_value getChildWindows(napi_env env, napi_callback_info args) {
    napi_value obj;
    napi_value arr;
    napi_value value;
    napi_status status;

    size_t argsLength = 1;
    napi_value argInfo[1];

    status = napi_get_cb_info(env, args, &argsLength, argInfo, NULL, NULL);
    if (status != napi_ok) return nullptr;

    int64_t handle;
    
    status = napi_get_value_int64(env, argInfo[0], &handle);
    if (status != napi_ok) return nullptr;

    winvector.clear();
    
    EnumChildWindows(HWND(handle), EnumChildProc, NULL);

    status = napi_create_array(env, &arr);
    if (status != napi_ok) return nullptr;

    int i = 0;
    for (windowInfo wininfo : winvector) {
        status = napi_create_object(env, &obj);
        if (status != napi_ok) return nullptr;
    
        status = napi_create_int64(env, wininfo.windowId, &value);
        if (status != napi_ok) return nullptr;

        status = napi_set_named_property(env, obj, "windowId", value);
        if (status != napi_ok) return nullptr;

        status = napi_create_string_utf8(env, wininfo.className.c_str(), NAPI_AUTO_LENGTH, &value);
        if (status != napi_ok) return nullptr;

        status = napi_set_named_property(env, obj, "className", value);
        if (status != napi_ok) return nullptr;

        status = napi_create_string_utf8(env, wininfo.windowText.c_str(), NAPI_AUTO_LENGTH, &value);
        if (status != napi_ok) return nullptr;

        status = napi_set_named_property(env, obj, "windowText", value);
        if (status != napi_ok) return nullptr;

        status = napi_set_element(env, arr, i++, obj);
        if (status != napi_ok) return nullptr;
    }

    return arr;
}

napi_value findWindowVisible(napi_env env, napi_callback_info args) {
    napi_value value;
    napi_status status;

    size_t argsLength = 1;
    napi_value argInfo[1];

    status = napi_get_cb_info(env, args, &argsLength, argInfo, NULL, NULL);
    if (status != napi_ok) return nullptr;

    int64_t handle;
    
    status = napi_get_value_int64(env, argInfo[0], &handle);
    if (status != napi_ok) return nullptr;

    winvector.clear();
    
    EnumWindows(EnumWindowsVisibleProc, LPARAM(handle));

    status = napi_create_int32(env, winvector.size(), &value);
    if (status != napi_ok) return nullptr;

    return value;
}

napi_value isWindowVisible(napi_env env, napi_callback_info args) {
    napi_value value;
    napi_status status;

    size_t argsLength = 1;
    napi_value argInfo[1];

    status = napi_get_cb_info(env, args, &argsLength, argInfo, NULL, NULL);
    if (status != napi_ok) return nullptr;

    int64_t handle;
    
    status = napi_get_value_int64(env, argInfo[0], &handle);
    if (status != napi_ok) return nullptr;

    status = napi_create_int32(env, IsWindowVisible(HWND(handle)), &value);
    if (status != napi_ok) return nullptr;

    return value;
}

napi_value isWindowMinimized(napi_env env, napi_callback_info args) {
    napi_value value;
    napi_status status;

    size_t argsLength = 1;
    napi_value argInfo[1];

    status = napi_get_cb_info(env, args, &argsLength, argInfo, NULL, NULL);
    if (status != napi_ok) return nullptr;

    int64_t handle;
    
    status = napi_get_value_int64(env, argInfo[0], &handle);
    if (status != napi_ok) return nullptr;

    LONG style = GetWindowLongA(HWND(handle), GWL_STYLE);

    status = napi_create_int32(env, (style & WS_MINIMIZE), &value);
    if (status != napi_ok) return nullptr;

    return value;
}

napi_value minimizeWindow(napi_env env, napi_callback_info args) {
    napi_status status;

    size_t argsLength = 1;
    napi_value argInfo[1];

    status = napi_get_cb_info(env, args, &argsLength, argInfo, NULL, NULL);
    if (status != napi_ok) return nullptr;

    int64_t handle;
    
    status = napi_get_value_int64(env, argInfo[0], &handle);
    if (status != napi_ok) return nullptr;

    ShowWindow(HWND(handle), SW_MINIMIZE);

    return nullptr;
}

napi_value countWindowsByProcess(napi_env env, napi_callback_info args) {
    napi_value obj;
    napi_value value;
    napi_status status;

    bool hasproperty;

    size_t argsLength = 1;
    napi_value argInfo[1];

    status = napi_get_cb_info(env, args, &argsLength, argInfo, NULL, NULL);
    if (status != napi_ok) return nullptr;

    status = napi_coerce_to_object(env, argInfo[0], &obj);
    if (status != napi_ok) return nullptr;

    windowFilter winfilter {0, ""};

    status = napi_get_named_property(env, obj, "pid", &value);
    if (status != napi_ok) return nullptr;

    status = napi_has_named_property(env, obj, "pid", &hasproperty);
    if (status != napi_ok) return nullptr;

    if (hasproperty) {
        status = napi_get_value_int32(env, value, &winfilter.pid);
        if (status != napi_ok) return nullptr;
    }

    status = napi_has_named_property(env, obj, "exename", &hasproperty);
    if (status != napi_ok) return nullptr;

    if (hasproperty) {
        status = napi_get_named_property(env, obj, "exename", &value);
        if (status != napi_ok) return nullptr;

        size_t result;
        CHAR exeName[MAX_PATH];

        status = napi_get_value_string_utf8(env, value, exeName, MAX_PATH, &result);
        if (status != napi_ok) return nullptr;

        winfilter.exeName = exeName;
    }

    countWindows = 0;

    EnumWindows(EnumWindowsByProcessProc, reinterpret_cast<LPARAM>(&winfilter));

    status = napi_create_int32(env, countWindows, &value);
    if (status != napi_ok) return nullptr;

    return value;
}