#include <string.h>

#include "../core_command/cmd_private.h"
#include "../filesystem/fs_private.h"
#include "../core_info/info_private.h"
#include "core_runtime_private.h"

#define COM_SAFE_MODE_DEFAULT qfalse
#define COM_STARTUP_SET_COMMAND "set"
#define COM_STARTUP_SAFE_COMMAND "safe"
#define COM_STARTUP_CVAR_RESTART_COMMAND "cvar_restart"
#define COM_STARTUP_COMMAND_SEPARATOR "\n"
#define COM_STARTUP_JOURNAL_RECORD 1
#define COM_STARTUP_JOURNAL_REPLAY 2
#define COM_STARTUP_CVAR_SET_FLAGS CODUO_CVAR_USER_CREATED
#define COM_JOURNAL_CVAR_NAME "journal"
#define COM_JOURNAL_FAILURE_CVAR_NAME "com_journal"

int32_t com_argc;
char *com_argv[CODUO_COM_MAX_ARGS];
static qboolean com_safeModeRequested = COM_SAFE_MODE_DEFAULT;

int32_t com_journalFile;
int32_t com_journalDataFile;

void Com_ParseCommandLine(char *commandLine)
{
    com_argv[0] = commandLine;
    com_argc = 1;

    while (*commandLine != '\0') {
        if (*commandLine == '+' || *commandLine == '\n') {
            if (com_argc == CODUO_COM_MAX_ARGS) {
                return;
            }

            com_argv[com_argc] = commandLine + 1;
            com_argc++;
            *commandLine = '\0';
        }

        commandLine++;
    }
}

qboolean Com_SafeMode(void)
{
    for (int32_t index = 0; index < com_argc; ++index) {
        Cmd_TokenizeString(com_argv[index]);

        if (Q_stricmp(Cmd_Argv(0), COM_STARTUP_SAFE_COMMAND) == 0 ||
            Q_stricmp(Cmd_Argv(0), COM_STARTUP_CVAR_RESTART_COMMAND) == 0) {
            com_argv[index][0] = '\0';
            return qtrue;
        }
    }

    return com_safeModeRequested;
}

void Com_RequestSafeMode(void)
{
    com_safeModeRequested = qtrue;
}

void Com_StartupVariable(const char *name)
{
    for (int32_t index = 0; index < com_argc; ++index) {
        Cmd_TokenizeString(com_argv[index]);

        const char *command = Cmd_Argv(0);
        const char *cvarName = Cmd_Argv(1);
        if (strcmp(command, COM_STARTUP_SET_COMMAND) == 0 &&
            (name == NULL || strcmp(cvarName, name) == 0)) {
            Cvar_Set(cvarName, Cmd_Argv(2));
            Cvar_Get(cvarName, "", 0)->flags |= COM_STARTUP_CVAR_SET_FLAGS;
        }
    }
}

qboolean Com_AddStartupCommands(void)
{
    qboolean addedNonSetCommand = qfalse;

    for (int32_t index = 0; index < com_argc; ++index) {
        if (com_argv[index] == NULL || com_argv[index][0] == '\0') {
            continue;
        }

        if (Q_stricmpn(com_argv[index], COM_STARTUP_SET_COMMAND,
                       (int)strlen(COM_STARTUP_SET_COMMAND)) != 0) {
            addedNonSetCommand = qtrue;
        }

        Cbuf_AddText(com_argv[index]);
        Cbuf_AddText(COM_STARTUP_COMMAND_SEPARATOR);
    }

    return addedNonSetCommand;
}


void Com_InitJournaling(void)
{
    Com_StartupVariable(COM_JOURNAL_CVAR_NAME);
    com_journal = Cvar_Get(COM_JOURNAL_CVAR_NAME, "0", CODUO_CVAR_INIT);

    if (com_journal->integer == COM_STARTUP_JOURNAL_RECORD) {
        Com_Printf("Journaling events\n");
        com_journalFile = FS_FOpenFileWrite("journal.dat");
        com_journalDataFile = FS_FOpenFileWrite("journaldata.dat");
    } else if (com_journal->integer == COM_STARTUP_JOURNAL_REPLAY) {
        Com_Printf("Replaying journaled events\n");
        FS_FOpenFileRead("journal.dat", &com_journalFile, qtrue);
        FS_FOpenFileRead("journaldata.dat", &com_journalDataFile, qtrue);
    }

    if (com_journal->integer != 0 &&
        (com_journalFile == 0 || com_journalDataFile == 0)) {
        /* ORIGINAL_BINARY_BUG: stock clears a distinct cvar and discards any
         * successfully opened peer handle without closing it; see
         * original-bugs/coduomp-journal-open-failure-wrong-cvar.md. */
        Cvar_Set(COM_JOURNAL_FAILURE_CVAR_NAME, "0");
        com_journalFile = 0;
        com_journalDataFile = 0;
        Com_Printf("Couldn't open journal files\n");
    }
}
