// included early to avoid warning
#include <winsock2.h>

#include "shutdown.h"

#include "api/controller.h"
#include "easrv/easrv.h"
#include "rawinput/rawinput.h"
#include "misc/vrutil.h"
#include "hooks/audio/audio.h"
#include "util/logging.h"

#include "launcher.h"
#include "logger.h"
#include "nvapi/nvapi.h"

namespace launcher {

    void stop_subsystems() {
        // note that it is possible for stop_subsystems to be called multiple times
        // (e.g., crashing, and then closing the window)
        // therefore, subsystems need to be guarded against multiple unload attempts
        log_info("launcher", "stopping subsystems");

        // flush/stop logger
        logger::stop();

        // VR
        vrutil::shutdown();

        // stop ea server
        easrv_shutdown();

        // free api sockets
        if (API_CONTROLLER) {
            API_CONTROLLER->free_socket();
        }

        // notify audio hook
        hooks::audio::stop();

        // stop raw input
        if (RI_MGR) {
            RI_MGR->stop();
        }

        // unload nvapi and free library (if loaded)
        nvapi::unload();
    }

    void kill(UINT exit_code) {

        // terminate
        TerminateProcess(GetCurrentProcess(), exit_code);
    }

    void shutdown(UINT exit_code) {

        // force exit after 1s
        std::thread force_thread([exit_code] {
            Sleep(1000);
            log_info("launcher", "force shutdown");
            kill(exit_code);
            return nullptr;
        });
        force_thread.detach();

        // stop all subsystems
        stop_subsystems();

        // terminate
        kill(exit_code);
    }

    static void restart_spawn() {

        // never do this twice
        static bool already_done = false;
        if (already_done) {
            return;
        } else {
            already_done = true;
        }

        // start the process using the same args
        if (LAUNCHER_ARGC > 0) {

            // build cmd line
            std::stringstream cmd_line;
            cmd_line << "START \"\" ";
            for (int i = 0; i < LAUNCHER_ARGC; i++)
                cmd_line << " \"" << LAUNCHER_ARGV[i] << "\"";

            // run command
            system(cmd_line.str().c_str());
        }
    }

    void restart() {

        // force restart after 1s
        std::thread force_thread([] {
            Sleep(1000);
            log_info("launcher", "force restart");
            restart_spawn();
            launcher::kill(0);
            return nullptr;
        });
        force_thread.detach();

        // clean up before restart so resources can be reclaimed
        stop_subsystems();

        // spawn new and terminate this process
        restart_spawn();
        launcher::kill(0);
    }
}
