#include "src/build_tool/library.cpp"

enum {
    PROFILE_DEBUG,
    PROFILE_RELEASE,
};
int Profile = PROFILE_DEBUG;

void AddCommonFlags(Array<S8_String> *cmd) {
    cmd->add("/MP /Zi /FC /nologo");
    cmd->add("/WX /W3 /wd4200 /diagnostics:column");
    cmd->add("/Oi");
    cmd->add("-D_CRT_SECURE_NO_WARNINGS");
    if (Profile == PROFILE_DEBUG) {
        cmd->add("-DDEBUG_BUILD=1");
        cmd->add("-DRELEASE_BUILD=0");
        // cmd->add("-D_DEBUG /MDd");
    } else {
        cmd->add("-DDEBUG_BUILD=0");
        cmd->add("-DRELEASE_BUILD=1");
        cmd->add("/O2 /MT /DNDEBUG /GL");
    }
}

struct Library {
    Array<S8_String> sources;
    Array<S8_String> objects;
    Array<S8_String> include_paths;
    Array<S8_String> defines;
};

Library PreparePdfio(S8_String zlib_include_path) {
    Library l = {};
    l.include_paths.add("../src/external/pdfio");

    l.sources.add("../src/external/pdfio/pdfio-aes.c");
    l.sources.add("../src/external/pdfio/pdfio-array.c");
    l.sources.add("../src/external/pdfio/pdfio-common.c");
    l.sources.add("../src/external/pdfio/pdfio-content.c");
    l.sources.add("../src/external/pdfio/pdfio-crypto.c");
    l.sources.add("../src/external/pdfio/pdfio-dict.c");
    l.sources.add("../src/external/pdfio/pdfio-file.c");
    l.sources.add("../src/external/pdfio/pdfio-md5.c");
    l.sources.add("../src/external/pdfio/pdfio-object.c");
    l.sources.add("../src/external/pdfio/pdfio-page.c");
    l.sources.add("../src/external/pdfio/pdfio-rc4.c");
    l.sources.add("../src/external/pdfio/pdfio-sha256.c");
    l.sources.add("../src/external/pdfio/pdfio-stream.c");
    l.sources.add("../src/external/pdfio/pdfio-string.c");
    l.sources.add("../src/external/pdfio/pdfio-token.c");
    l.sources.add("../src/external/pdfio/pdfio-value.c");
    l.sources.add("../src/external/pdfio/ttf.c");

    l.objects.add("pdfio-aes.obj");
    l.objects.add("pdfio-array.obj");
    l.objects.add("pdfio-common.obj");
    l.objects.add("pdfio-content.obj");
    l.objects.add("pdfio-crypto.obj");
    l.objects.add("pdfio-dict.obj");
    l.objects.add("pdfio-file.obj");
    l.objects.add("pdfio-md5.obj");
    l.objects.add("pdfio-object.obj");
    l.objects.add("pdfio-page.obj");
    l.objects.add("pdfio-rc4.obj");
    l.objects.add("pdfio-sha256.obj");
    l.objects.add("pdfio-stream.obj");
    l.objects.add("pdfio-string.obj");
    l.objects.add("pdfio-token.obj");
    l.objects.add("pdfio-value.obj");
    l.objects.add("ttf.obj");

    if (!OS_FileExists(l.objects[0])) {
        Array<S8_String> cmd = {};
        cmd.add("cl.exe -c ");
        AddCommonFlags(&cmd);
        For(l.include_paths) cmd.add(S8_Format(Perm, "-I %.*s ", S8_Expand(it)));
        cmd.add(S8_Format(Perm, "-I %.*s ", S8_Expand(zlib_include_path)));
        cmd += l.sources;
        Run(cmd);
    }

    return l;
}

Library PrepareZLib() {
    Library l = {};

    l.sources.add("../src/external/zlib-1.3.1/adler32.c");
    l.sources.add("../src/external/zlib-1.3.1/compress.c");
    l.sources.add("../src/external/zlib-1.3.1/crc32.c");
    l.sources.add("../src/external/zlib-1.3.1/deflate.c");
    l.sources.add("../src/external/zlib-1.3.1/gzclose.c");
    l.sources.add("../src/external/zlib-1.3.1/gzlib.c");
    l.sources.add("../src/external/zlib-1.3.1/gzread.c");
    l.sources.add("../src/external/zlib-1.3.1/gzwrite.c");
    l.sources.add("../src/external/zlib-1.3.1/inflate.c");
    l.sources.add("../src/external/zlib-1.3.1/infback.c");
    l.sources.add("../src/external/zlib-1.3.1/inftrees.c");
    l.sources.add("../src/external/zlib-1.3.1/inffast.c");
    l.sources.add("../src/external/zlib-1.3.1/trees.c");
    l.sources.add("../src/external/zlib-1.3.1/uncompr.c");
    l.sources.add("../src/external/zlib-1.3.1/zutil.c");

    l.include_paths.add("../src/external/zlib-1.3.1/");
    l.objects.add("adler32.obj");
    l.objects.add("compress.obj");
    l.objects.add("crc32.obj");
    l.objects.add("deflate.obj");
    l.objects.add("gzclose.obj");
    l.objects.add("gzlib.obj");
    l.objects.add("gzread.obj");
    l.objects.add("gzwrite.obj");
    l.objects.add("inflate.obj");
    l.objects.add("infback.obj");
    l.objects.add("inftrees.obj");
    l.objects.add("inffast.obj");
    l.objects.add("trees.obj");
    l.objects.add("uncompr.obj");
    l.objects.add("zutil.obj");

    l.objects.add("Advapi32.lib");

    if (!OS_FileExists(l.objects[0])) {
        Array<S8_String> cmd = {};
        cmd.add("cl.exe -c");
        AddCommonFlags(&cmd);
        For(l.include_paths) cmd.add(S8_Format(Perm, "-I %.*s ", S8_Expand(it)));
        cmd += l.sources;
        Run(cmd);
    }

    return l;
}

Library PrepareIMGUI(S8_String sdl_inc_path) {
    Library l = {};
    l.include_paths.add("../src/external/imgui/");
    l.include_paths.add("../src/external/imgui/backends");
    l.sources.add("../src/external/imgui/backends/imgui_impl_sdl3.cpp");
    l.sources.add("../src/external/imgui/backends/imgui_impl_opengl3.cpp");
    l.sources.add("../src/external/imgui/imgui.cpp");
    l.sources.add("../src/external/imgui/imgui_demo.cpp");
    l.sources.add("../src/external/imgui/imgui_draw.cpp");
    l.sources.add("../src/external/imgui/imgui_tables.cpp");
    l.sources.add("../src/external/imgui/imgui_widgets.cpp");

    l.objects.add("imgui_impl_sdl3.obj");
    l.objects.add("imgui_impl_opengl3.obj");
    l.objects.add("imgui.obj");
    l.objects.add("imgui_demo.obj");
    l.objects.add("imgui_draw.obj");
    l.objects.add("imgui_tables.obj");
    l.objects.add("imgui_widgets.obj");

    if (!OS_FileExists(l.objects[0])) {
        Array<S8_String> cmd = {};
        cmd.add("cl.exe -c");
        AddCommonFlags(&cmd);
        cmd.add(Fmt("-I %.*s", S8_Expand(sdl_inc_path)));
        For(l.include_paths) cmd.add(S8_Format(Perm, "-I %.*s ", S8_Expand(it)));
        cmd += l.sources;
        Run(cmd);
    }

    return l;
}

void ListDirRecursive(Array<S8_String> *arr, S8_String dir) {
    for (OS_FileIter it = OS_IterateFiles(Perm, dir); OS_IsValid(it); OS_Advance(&it)) {
        if (it.is_directory) {
            ListDirRecursive(arr, it.absolute_path);
        } else {
            arr->add(S8_Copy(Perm, it.absolute_path));
        }
    }
}

Library PrepareSDL() {
    Library l = {};
    l.include_paths.add("../src/external/SDL/include");
    l.objects.add("../src/external/SDL/VisualC/x64/Release/SDL3.lib");
    return l;
}

Library PrepareRaylib() {
    Library l = {};
    l.include_paths.add("../src/external/raylib/include");
    l.objects.add("../src/external/raylib/lib/raylib.lib");
    return l;
}

Library PrepareGlad() {
    Library l = {};
    l.sources.add("../src/external/glad/glad.c");
    l.include_paths.add("../src/external/glad");
    l.objects.add("glad.obj");
    if (!OS_FileExists(l.objects[0])) {
        Array<S8_String> cmd = {};
        cmd.add("cl.exe -c");
        AddCommonFlags(&cmd);
        For(l.include_paths) cmd.add(S8_Format(Perm, "-I %.*s ", S8_Expand(it)));
        cmd += l.sources;
        Run(cmd);
    }
    return l;
}

int CompileTranscriptBrowser() {
    int result = 0;

    Array<Library> libs = {};
    libs.add(PrepareSDL());
    libs.add(PrepareZLib());
    libs.add(PreparePdfio(libs[1].include_paths[0]));
    libs.add(PrepareGlad());
    libs.add(PrepareIMGUI(libs[0].include_paths[0]));

    Array<S8_String> cmd = {};
    cmd.add("cl.exe");
    cmd.add("-Fe:transcript_browser.exe");
    cmd.add("-Fd:transcript_browser.pdb");
    cmd.add("-std:c++20"); // semaphore
    AddCommonFlags(&cmd);
    For2(lib, libs) For(lib.defines) cmd.add(it);

    cmd.add("../src/transcript_browser/main.cpp");
    cmd.add("../src/basic/win32.cpp");

    For2(lib, libs) For(lib.include_paths) cmd.add(Fmt("-I %.*s", S8_Expand(it)));

    cmd.add("/link");
    cmd.add("/incremental:no");
    cmd.add("/SUBSYSTEM:WINDOWS");
    cmd.add("opengl32.lib gdi32.lib winmm.lib Setupapi.lib ole32.lib oleaut32.lib version.lib");
    For(libs) For2(o, it.objects) cmd.add(o);

    OS_DeleteFile("transcript_browser.pdb");
    // For(cmd) IO_Printf("%.*s\n", S8_Expand(it));

    result += Run(cmd);
    return result;
}

int CompileTextEditor() {
    int result = 0;

    Array<Library> libs = {};
    libs.add(PrepareRaylib());

    Array<S8_String> cmd = {};
    cmd.add("cl.exe");
    cmd.add("-Fe:te.exe");
    cmd.add("-Fd:te.pdb");
    AddCommonFlags(&cmd);
    For2(lib, libs) For(lib.defines) cmd.add(it);

    cmd.add("../src/text_editor/text_editor.cpp");
    cmd.add("../src/basic/win32.cpp");

    For2(lib, libs) For(lib.include_paths) cmd.add(Fmt("-I %.*s", S8_Expand(it)));

    cmd.add("/link");
    cmd.add("/incremental:no");
    // cmd.add("/SUBSYSTEM:WINDOWS");
    cmd.add("opengl32.lib gdi32.lib winmm.lib shell32.lib user32.lib msvcrt.lib /NODEFAULTLIB:LIBCMT /FORCE");
    For(libs) For2(o, it.objects) cmd.add(o);

    OS_DeleteFile("te.pdb");
    // For(cmd) IO_Printf("%.*s\n", S8_Expand(it));

    result += Run(cmd);
    return result;
}

int main() {
    MA_InitScratch();
    SRC_InitCache(Perm, "pdf_browser.cache");
    int result = CompileTranscriptBrowser();
    // int result = CompileTextEditor();

    if (result != 0) {
        OS_DeleteFile("pdf_browser.cache");
        return result;
    }

    SRC_SaveCache();
    return 0;
}