#include "loveplus.h"

#include <windows.h>

#include "avs/game.h"
#include "hooks/graphics/graphics.h"
#include "hooks/devicehook.h"
#include "touch/touch.h"
#include "util/detour.h"
#include "util/libutils.h"
#include "util/logging.h"
#include "util/utils.h"

namespace games::loveplus {

    // touch stuff
    static bool TOUCH_ENABLE = false;
    static bool TOUCH_ATTACHED = false;

    void touch_update() {

        // check if touch enabled
        if (!TOUCH_ENABLE) {
            return;
        }

        // attach touch module
        if (!TOUCH_ATTACHED) {

            // Find the game window.
            // We check the foreground window first, then fall back to searching for the window title
            // All game versions seem to have their model first in the window title
            HWND wnd = GetForegroundWindow();
            if (!string_begins_with(GetActiveWindowTitle(), "LovePlus")) {
                wnd = FindWindowBeginsWith(avs::game::MODEL);
            }

            // attach touch hook
            if (wnd) {
                log_info("loveplus", "using window handle for touch: {}", fmt::ptr(wnd));
                touch_create_wnd(wnd);
            } else {
                log_info("loveplus", "falling back to the DirectX window handle for touch");
                touch_attach_dx_hook();
            }

            // show cursor
            if (GRAPHICS_SHOW_CURSOR) {
                ShowCursor(1);
            }

            // set attached
            TOUCH_ATTACHED = true;
        }
    }

    static int __cdecl lib_touchpanel_init() {
        touch_update();

        return 0;
    }

    static int __cdecl lib_touchpanel_calibrate(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) {
        return 0;
    }

    // FIXME: test this calling convention change!
    static int __cdecl lib_touchpanel_diagnostics(void *a1) {
        return 0;
    }

    static int __cdecl lib_touchpanel_get_touch(DWORD *a1) {
        touch_update();

        // get touch points
        std::vector<TouchPoint> touch_points;
        touch_get_points(touch_points);

        // check first touch point
        if (!touch_points.empty()) {
            auto &tp = touch_points[0];

            a1[0] = tp.y;
            a1[1] = tp.x;
            a1[2] = 0;
            a1[3] = 1;
            a1[4] = 0;
        } else {
            a1[0] = 0;
            a1[1] = 0;
            a1[2] = 0;
            a1[3] = 0;
            a1[4] = 0;
        }

        return 0;
    }

    static int __cdecl lib_touchpanel_get_touch_direct(DWORD *a1) {
        return lib_touchpanel_get_touch(a1);
    }

    static int __cdecl lib_touchpanel_update() {
        return 1;
    }

    static bool __cdecl lib_touchpanel_term() {
        return 0;
    }

    static bool __cdecl lib_touchpanel_instance_term() {
        return 0;
    }

    static BOOL __cdecl SetEqualizer(char *a1) {
        return FALSE;
    }

    static LPSTR __stdcall GetCommandLineA_hook() {
        static std::string lp_args = "-win -noCamera -noWatchDog -noIOError -noIrda -notarget";

        return lp_args.data();
    }

    LovePlusGame::LovePlusGame() : Game("LovePlus") {
    }

    void LovePlusGame::attach() {
        Game::attach();

        // init stuff
        devicehook_init();

        // enable touch
        TOUCH_ENABLE = true;

        // touchpanel emulation
        HMODULE lib_touchpanel = libutils::try_library("lib_touchpanel.dll");
        detour::inline_hook((void *) lib_touchpanel_init, libutils::try_proc(
                  lib_touchpanel, "lib_touchpanel_init"));
        detour::inline_hook((void *) lib_touchpanel_calibrate, libutils::try_proc(
                  lib_touchpanel, "lib_touchpanel_calibrate"));
        detour::inline_hook((void *) lib_touchpanel_diagnostics, libutils::try_proc(
                  lib_touchpanel, "lib_touchpanel_diagnostics"));
        detour::inline_hook((void *) lib_touchpanel_get_touch, libutils::try_proc(
                  lib_touchpanel, "lib_touchpanel_get_touch_direct"));
        detour::inline_hook((void *) lib_touchpanel_get_touch_direct, libutils::try_proc(
                  lib_touchpanel, "lib_touchpanel_get_touch"));
        detour::inline_hook((void *) lib_touchpanel_update, libutils::try_proc(
                  lib_touchpanel, "lib_touchpanel_update"));
        detour::inline_hook((void *) lib_touchpanel_term, libutils::try_proc(
                  lib_touchpanel, "lib_touchpanel_term"));
        detour::inline_hook((void *) lib_touchpanel_instance_term, libutils::try_proc(
                  lib_touchpanel, "lib_touchpanel_instance_term"));

        // equalizer crash fix
        HMODULE seteq = libutils::try_library("ad_hd_seteq_dll.dll");
        detour::inline_hook((void *) SetEqualizer, libutils::try_proc(seteq, "SetEqualizer"));

        // get command line hook
        HMODULE lpac = libutils::try_library("lpac.dll");
        detour::iat("GetCommandLineA", GetCommandLineA_hook, lpac);
    }

    void LovePlusGame::detach() {
        Game::detach();

        // disable touch
        TOUCH_ENABLE = false;
    }
}
