#include "assetEnums.h"

#include "helpers/assetsHelper.h"
#include "helpers/stringHelper.h"
#include "helpers/c/cEnumsHelper.h"

AssetEnums::AssetEnums(DkrAssetsSettings &settings) : _settings(settings) {
    _outFilepath = _settings.pathToInclude / "asset_enums.h";
    
    _cHeader.write_comment("This file is automatically generated. Any changes you make to this file will get overwritten.");
    _cHeader.write_newline();
    
    _cHeader.write_raw_text_line("#ifndef _ASSET_ENUMS_H");
    _cHeader.write_raw_text_line("#define _ASSET_ENUMS_H");
    _cHeader.write_newline();
    
    _cHeader.write_header_comment("Asset Sections");
    _write_asset_section_enums();
    _cHeader.write_newline();
    
    _cHeader.write_raw_text_line("#endif");
    
    _cHeader.save(_outFilepath);
}

AssetEnums::~AssetEnums() {
    
}

void AssetEnums::_write_asset_section_enums() {
    JsonFile *mainJson = AssetsHelper::get_main_json(_settings);
    
    std::vector<std::string> mainOrder;
    mainJson->get_array<std::string>("/assets/order", mainOrder);
    
    _write_main_asset_enum(mainOrder);
    
    for(std::string &sectionId : mainOrder) {
        JsonFile *sectionJson = AssetsHelper::get_asset_section_json(_settings, sectionId);
        
        if(sectionJson != nullptr) {
            if(sectionJson->has("/files")) {
                _cHeader.write_newline();
                _cHeader.write_header_comment(sectionId.c_str());
                _write_asset_section_enums(sectionId, sectionJson);
            } else if(sectionJson->has("/filename")) {
                _write_single_asset_section_enums(sectionId, sectionJson);
            }
            continue;
        }
        
        std::string sectionPtr = "/assets/sections/" + sectionId;
        
        bool isDeferred = mainJson->get_bool(sectionPtr + "/deferred");
        
        if(isDeferred) {
            std::string deferInfoPtr = sectionPtr + "/defer-info";
            _cHeader.write_newline();
            _cHeader.write_header_comment(sectionId.c_str());
            _write_deferred_asset_section_enums(sectionId, mainJson, deferInfoPtr);
        }
    }
}

void AssetEnums::_write_main_asset_enum(std::vector<std::string> &mainOrder) {
    WriteableCEnum mainEnum("AssetSectionsEnum");
    for(std::string &sectionId : mainOrder) {
        mainEnum.add_symbol(sectionId);
    }
    mainEnum.add_symbol("\n    ASSET_SECTIONS_COUNT");
    _cHeader.write_raw_text(mainEnum.to_string());
}

void AssetEnums::_write_single_asset_section_enums(const std::string &sectionId, JsonFile *sectionJson) {
    if((sectionId != "ASSET_FONTS") && (sectionId != "ASSET_LEVEL_OBJECT_TRANSLATION_TABLE")) {
        return;
    }
    _cHeader.write_newline();
    _cHeader.write_header_comment(sectionId.c_str());
                
    std::string sectionEnumName = StringHelper::upper_snake_case_to_pascal_case(sectionId) + "Enum";
    WriteableCEnum sectionEnum(sectionEnumName);
    
    fs::path subFolder = sectionJson->get_path("/folder");
    fs::path jsonPath = _settings.pathToAssets;
    if(!subFolder.empty()) {
        jsonPath /= subFolder;
    }
    
    if(sectionJson->is_value_null("/filename")) {
        _cHeader.write_comment("No file");
        return;
    }
    
    jsonPath /= sectionJson->get_string("/filename");
    JsonFile *assetJson;
    DebugHelper::assert(JsonHelper::get().get_file(jsonPath, &assetJson),
        "(AssetEnums::_write_single_asset_section_enums) Could not load fonts json!");
    
    if(sectionId == "ASSET_FONTS") {
        std::vector<std::string> fontIds;
        assetJson->get_array<std::string>("/fonts-order", fontIds);
        for(std::string &fontId : fontIds) {
            sectionEnum.add_symbol(fontId);
        }
    } else if(sectionId == "ASSET_LEVEL_OBJECT_TRANSLATION_TABLE") {
        size_t tableLen = assetJson->length_of_array("/table");
        for(size_t i = 0; i < tableLen; i++) {
            std::string ptr = "/table/" + std::to_string(i);
            if(assetJson->is_value_null(ptr)) {
                sectionEnum.add_symbol("NULL_OBJECT_" + std::to_string(i) + "_ID");
                continue;
            }
            std::string objectId = assetJson->get_string(ptr);
            
            if(StringHelper::starts_with(objectId, "ASSET_OBJECT_")) {
                objectId.insert(13, "ID_");
            } else {
                objectId += "_ID";
            }
            
            sectionEnum.add_symbol(objectId);
        }
    }
    
    sectionEnum.add_symbol("\n    " + sectionId + "_COUNT");
    _cHeader.write_raw_text(sectionEnum.to_string());
}

void AssetEnums::_write_asset_section_enums(const std::string &sectionId, JsonFile *sectionJson) {
    std::string sectionEnumName = StringHelper::upper_snake_case_to_pascal_case(sectionId) + "Enum";
    WriteableCEnum sectionEnum(sectionEnumName);
    
    std::vector<std::string> sectionOrder;
    sectionJson->get_array<std::string>("/files/order", sectionOrder);
    
    for(std::string &assetBuildId : sectionOrder) {
        sectionEnum.add_symbol(assetBuildId);
    }
    
    
    if(sectionId != "ASSET_MENU_TEXT") {
        sectionEnum.add_symbol("\n    " + sectionId + "_COUNT");
    }
    _cHeader.write_raw_text(sectionEnum.to_string());
    
    if(sectionId == "ASSET_MENU_TEXT") {
        std::string menuTextIdsEnumName = StringHelper::upper_snake_case_to_pascal_case(sectionId) + "IdsEnum";
        WriteableCEnum menuTextIdsEnum(menuTextIdsEnumName);
        
        std::vector<std::string> menuTextIds;
        sectionJson->get_array<std::string>("/menu-text-build-ids", menuTextIds);
        for(std::string &textId : menuTextIds) {
            menuTextIdsEnum.add_symbol(textId);
        }
        
        menuTextIdsEnum.add_symbol("\n    " + sectionId + "_COUNT");
        _cHeader.write_newline();
        _cHeader.write_raw_text(menuTextIdsEnum.to_string());
    }
}

void AssetEnums::_write_deferred_asset_section_enums(const std::string &sectionId, JsonFile *mainJson, const std::string &deferInfoPtr) {
    std::string sectionEnumName = StringHelper::upper_snake_case_to_pascal_case(sectionId) + "Enum";
    WriteableCEnum sectionEnum(sectionEnumName);
    
    std::string fromSectionId = mainJson->get_string(deferInfoPtr + "/from-section");
    std::string idPostfix = mainJson->get_string(deferInfoPtr + "/id-postfix");
    
    JsonFile *fromSectionJson = AssetsHelper::get_asset_section_json(_settings, fromSectionId);
    
    std::vector<std::string> sectionOrder;
    fromSectionJson->get_array<std::string>("/files/order", sectionOrder);
    
    std::string sectionPtr = "/files/sections/";
    
    for(std::string &assetBuildId : sectionOrder) {
        // Don't include files that have null filenames.
        if(fromSectionJson->is_value_null(sectionPtr + assetBuildId + "/filename")) {
            continue;
        }
        
        // TODO: This only works for LevelNames. 
        //       Will need to be modified later since Object Animations aren't one-to-one with Object Models.
        sectionEnum.add_symbol(assetBuildId + idPostfix);
    }
    
    sectionEnum.add_symbol("\n    " + sectionId + "_COUNT");
    _cHeader.write_raw_text(sectionEnum.to_string());
}
