#include <stdint.h>
#include <string.h>

#include "../core_runtime/core_runtime_private.h"
#include "../filesystem/fs_private.h"
#include "sound_alias_private.h"

enum {
    SOUND_ALIAS_SUBTITLE_REFERENCE_PREFIX_CHARS = 9,
    SOUND_ALIAS_STRINGED_ENDMARKER_SKIP_CHARS = 11
};

#define SOUND_ALIAS_SUBTITLE_FILE "soundaliases/subtitle.st"
#define SOUND_ALIAS_SUBTITLE_TEMP_FILE "soundaliases/temp.st"
#define SOUND_ALIAS_STRINGED_REFERENCE_KEY "REFERENCE"
#define SOUND_ALIAS_STRINGED_REFERENCE_PREFIX "REFERENCE           "
#define SOUND_ALIAS_STRINGED_ENGLISH_PREFIX "\r\nLANG_ENGLISH        \""
#define SOUND_ALIAS_STRINGED_ENTRY_SUFFIX "\"\r\n\r\n"
#define SOUND_ALIAS_STRINGED_ENDMARKER_KEY "ENDMARKER"
#define SOUND_ALIAS_STRINGED_ENDMARKER_SUFFIX "\r\nENDMARKER\r\n\r\n\r\n"

void Com_WriteSoundAliasSubtitleEntry(const char *reference,
                                      const char *englishText,
                                      int32_t handle)
{
    const char *text;
    size_t length;

    text = SOUND_ALIAS_STRINGED_REFERENCE_PREFIX;
    length = strlen(text);
    FS_Write(text, (int32_t)length, handle);

    length = strlen(reference);
    FS_Write(reference, (int32_t)length, handle);

    text = SOUND_ALIAS_STRINGED_ENGLISH_PREFIX;
    length = strlen(text);
    FS_Write(text, (int32_t)length, handle);

    length = strlen(englishText);
    FS_Write(englishText, (int32_t)length, handle);

    text = SOUND_ALIAS_STRINGED_ENTRY_SUFFIX;
    length = strlen(text);
    FS_Write(text, (int32_t)length, handle);
}

void Com_UpdateSoundAliasSubtitleFile(const char *subtitle,
                                      const char *englishText)
{
    qboolean found;
    const char *reference;
    int32_t tempHandle;
    void *subtitleFile;
    char *parseCursor;
    char *writeStart;
    char *token;
    size_t length;
    int32_t writeLength;
    char subtitleOSPath[FS_OSPATH_SIZE];
    char tempOSPath[FS_OSPATH_SIZE];

    found = qfalse;
    reference = subtitle + SOUND_ALIAS_SUBTITLE_REFERENCE_PREFIX_CHARS;
    tempHandle = FS_FOpenFileWrite(SOUND_ALIAS_SUBTITLE_TEMP_FILE);
    if (tempHandle == 0) {
        Com_Printf("WARNING: Could not open output file %s for writing\n",
                   SOUND_ALIAS_SUBTITLE_TEMP_FILE);
        return;
    }

    if (FS_ReadFile(SOUND_ALIAS_SUBTITLE_FILE, &subtitleFile) < 0) {
        Com_Printf("WARNING: Could not read local copy of StringEd file %s\n",
                   SOUND_ALIAS_SUBTITLE_FILE);
        FS_FCloseFile(tempHandle);
        return;
    }

    Com_BeginParseSession(SOUND_ALIAS_SUBTITLE_FILE);
    parseCursor = subtitleFile;
    writeStart = subtitleFile;

    while (1) {
        token = Com_Parse(&parseCursor);
        if (parseCursor == NULL) {
            break;
        }

        if (strcmp(token, SOUND_ALIAS_STRINGED_ENDMARKER_KEY) == 0) {
            if (writeStart < parseCursor) {
                /* ORIGINAL_BINARY_BUG: an ENDMARKER without the assumed
                 * preceding CRLF makes this signed length negative and the
                 * FS_Write sink treats it as a huge unsigned transfer; see
                 * original-bugs/engine-sound-alias-endmarker-negative-write.md. */
                writeLength =
                    (int32_t)(parseCursor - writeStart) -
                    SOUND_ALIAS_STRINGED_ENDMARKER_SKIP_CHARS;
                FS_Write(writeStart, writeLength, tempHandle);
            }
            break;
        }

        if (strcmp(token, SOUND_ALIAS_STRINGED_REFERENCE_KEY) == 0) {
            token = Com_ParseOnLine(&parseCursor);
            if (strcmp(token, reference) == 0) {
                if (writeStart < parseCursor) {
                    writeLength =
                        (int32_t)(parseCursor - writeStart);
                    FS_Write(writeStart, writeLength, tempHandle);
                }

                Com_WriteSoundAliasSubtitleEntry(reference, englishText,
                                                 tempHandle);
                found = qtrue;

                do {
                    writeStart = parseCursor;
                    token = Com_Parse(&parseCursor);
                    if (parseCursor == NULL) {
                        writeStart = NULL;
                        break;
                    }
                } while (strcmp(token, SOUND_ALIAS_STRINGED_REFERENCE_KEY) !=
                             0 &&
                         strcmp(token, SOUND_ALIAS_STRINGED_ENDMARKER_KEY) !=
                             0);

                if (parseCursor != NULL) {
                    Com_UngetToken();
                }
            }
        }

        Com_SkipRestOfLine(&parseCursor);
    }

    if (found == qfalse) {
        Com_WriteSoundAliasSubtitleEntry(reference, englishText, tempHandle);
    }

    Com_EndParseSession();
    FS_FreeFile(subtitleFile);

    token = SOUND_ALIAS_STRINGED_ENDMARKER_SUFFIX;
    length = strlen(token);
    FS_Write(token, (int32_t)length, tempHandle);
    FS_FCloseFile(tempHandle);

    FS_BuildOSPath(fs_basepath->string, fs_currentGameDir,
                   SOUND_ALIAS_SUBTITLE_TEMP_FILE, tempOSPath);
    FS_BuildOSPath(fs_basepath->string, fs_currentGameDir,
                   SOUND_ALIAS_SUBTITLE_FILE, subtitleOSPath);
    FS_Copyfiles(tempOSPath, subtitleOSPath);
    FS_Remove(tempOSPath);
}
