#include "asset_asm_data.h"

#define ASSETS_START_LABEL "ASSETS_START"
#define ASSETS_END_LABEL   "ASSETS_END"
#define ASSETS_SIZE_LABEL "ASSETS_END - ASSETS_START"

AssetAsmIncludes::AssetAsmIncludes(std::string assetsDirectory, std::string buildDirectory, std::string dstPath) {
    std::stringstream out;

    write_comment(out, "This file is automatically generated. Any changes you make to this file will get overwritten.");
    out << std::endl;
    out << ".include \"macros.inc\"" << std::endl;
    out << std::endl;

    std::vector<std::string> sections = get_assets_order();
    write_main_table(out, sections);

    out << std::endl;
    write_header_comment(out, "Asset Sections");
    write_global_label(out, ASSETS_START_LABEL, true);
    out << std::endl;
    
    for(std::string &sectionName : sections) {
        write_section(out, assetsDirectory, buildDirectory, sectionName);
    }
    
    write_global_label(out, ASSETS_END_LABEL, true);
    
    write_text_file(out.str(), dstPath + "/assets.s");
}

AssetAsmIncludes::~AssetAsmIncludes() {
    
}

void AssetAsmIncludes::write_comment(std::stringstream &out, std::string text) {
    out << "# " << text << std::endl;
}

void AssetAsmIncludes::write_header_comment(std::stringstream &out, std::string text) {
    out << "/********** " << text << " **********/" << std::endl << std::endl;
}

void AssetAsmIncludes::write_word(std::stringstream &out, std::string value) {
    out << ".word " << value << std::endl;
}

void AssetAsmIncludes::write_two_words(std::stringstream &out, std::string value, std::string value2) {
    out << ".word " << value << ", " << value2 << std::endl;
}

void AssetAsmIncludes::write_word_with_comment(std::stringstream &out, std::string value, std::string comment) {
    out << ".word " << value << " # " << comment << std::endl;
}

void AssetAsmIncludes::write_binary_include(std::stringstream &out, std::string path) {
    out << ".incbin \"" << replace_extension(path, ".bin") << "\"" << std::endl;
}

void AssetAsmIncludes::write_global_label(std::stringstream &out, std::string label, bool align) {
    if(align) {
        out << ".balign 16" << std::endl;
    }
    out << "glabel " << label << std::endl;
}

void AssetAsmIncludes::write_main_table(std::stringstream &out, std::vector<std::string> &sectionNames) {
    write_header_comment(out, "Asset Sections Table");
    write_word_with_comment(out, std::to_string(get_asset_count()), "Number of entries in the table");
    for(std::string &sectionName : sectionNames) {
        write_word(out, sectionName + " - " + ASSETS_START_LABEL);
    }
    write_word(out, ASSETS_SIZE_LABEL);
}

std::string formatGameTextWordEntry(std::string sourceSectionFirst, std::string entryName, std::string gameTextJsonPath) {
    bool isDialog = get_string_from_json(gameTextJsonPath, "text-type") == "Dialog";

    if(isDialog) {
        return "(" + entryName + " - " + sourceSectionFirst + ") | 0x80000000";
    } else {
        return entryName + " - " + sourceSectionFirst;
    }   
}

void AssetAsmIncludes::write_ttghost_table_entry(std::stringstream &out, std::string entryWord, std::string ttGhostJsonPath) {
    int levelId = get_index_from_build_id("ASSET_LEVEL_HEADERS", get_string_from_json(ttGhostJsonPath, "header", "level"));
    std::string vehicle = get_string_from_json(ttGhostJsonPath, "header", "vehicle");
    int vehicleId = get_enum_value_from_string("Vehicle", vehicle);
    write_two_words(out, to_hex((levelId << 24) | (vehicleId << 16)), entryWord);
}

void AssetAsmIncludes::write_section(std::stringstream &out, std::string assetsDirectory, std::string buildDirectory, std::string sectionName) {
    write_header_comment(out, sectionName);
    write_global_label(out, sectionName, true);
    
    if(get_string_from_section(sectionName, "type") == "Table") {
        std::string sourceSectionName = get_source_section_for_table(sectionName);

        if(sectionName == "ASSET_MENU_TEXT_TABLE") {
            int numTextEntries = get_array_length_from_section(sourceSectionName, "menu-text-build-ids");
            write_word_with_comment(out, std::to_string(numTextEntries), "Number of text entries for the languages");
        }

        std::vector<std::string> sectionFilenames = get_section_order(sourceSectionName);
        
        bool first = true;
        for(std::string &entryName : sectionFilenames) {
            if(first) {
                if(sectionName == "ASSET_AUDIO_TABLE") {
                    // For some reason, the first entry in the audio table is skipped.
                    first = false;
                    continue;
                }
                first = false;
            }

            std::string entryWord = entryName + " - " + sourceSectionName;

            if(sectionName == "ASSET_GAME_TEXT_TABLE") {
                std::string filename = get_filename_from_section_entry(sourceSectionName, entryName);
                std::string folder = get_string_from_section(sourceSectionName, "folder");
                std::string gameTextJsonPath = assetsDirectory + "/" + folder + "/" + filename;
                entryWord = formatGameTextWordEntry(sourceSectionName, entryName, gameTextJsonPath);
            }
            
            if(sectionName == "ASSET_TTGHOSTS_TABLE") {
                std::string filename = get_filename_from_section_entry(sourceSectionName, entryName);
                std::string folder = get_string_from_section(sourceSectionName, "folder");
                std::string ttGhostJsonPath = assetsDirectory + "/" + folder + "/" + filename;
                write_ttghost_table_entry(out, entryWord, ttGhostJsonPath);
            } else {
                if(sectionName == "ASSET_MISC_TABLE") {
                    entryWord = "(" + entryWord + ") / 4";
                }
                write_word(out, entryWord);
            }
        }

        if(sectionName == "ASSET_TTGHOSTS_TABLE") {
            write_two_words(out, "0xFFFF0000", sourceSectionName + "_END - " + sourceSectionName);
            write_two_words(out, "0xFFFF0000", "0xFFFFFFFF");
        } else {
            if(sectionName == "ASSET_MISC_TABLE") {
                write_word(out, "(" + sourceSectionName + "_END - " + sourceSectionName + ") / 4");
            } else {
                write_word(out, sourceSectionName + "_END - " + sourceSectionName);
            }
            write_word(out, "0xFFFFFFFF");
        }
    } else {
        std::string folder = get_string_from_section(sectionName, "folder");
        if(section_has_key(sectionName, "files")) {
            std::vector<std::string> sectionFilenames = get_section_order(sectionName);
            for(std::string &subSectionName : sectionFilenames) {
                write_global_label(out, subSectionName);
                std::string filename = get_filename_from_section_entry(sectionName, subSectionName);
                write_binary_include(out, buildDirectory + "/" + folder + "/" + filename);
            }
        } else if(section_has_key(sectionName, "filename")) {
            std::string filename = get_string_from_section(sectionName, "filename");
            write_binary_include(out, buildDirectory + "/" + folder + "/" + filename);
        }
        write_global_label(out, sectionName + "_END");
    }
    
    out << std::endl;
}









