"use strict";

const fs = require('fs');

function getModFilepath(mod) {
    return "user/mods/" + mod.author + "-" + mod.name + "-" + mod.version + "/";
}

function scanRecursiveMod(filepath, baseNode, modNode) {
    if (typeof modNode === "string") {
        baseNode = filepath + modNode;
    }

    if (typeof modNode === "object") {
        for (let node in modNode) {
            if (!(node in baseNode)) {
                baseNode[node] = {};
            }

            baseNode[node] = scanRecursiveMod(filepath, baseNode[node], modNode[node]);
        }
    }

    return baseNode;
}

function loadMod(mod, filepath) {
    logger.logInfo("Loading mod " + mod.author + "-" + mod.name + "-" + mod.version);

    let src = json.parse(json.read("user/cache/loadorder.json"));

    if ("db" in mod) {
        db = scanRecursiveMod(filepath, db, mod.db);
    }

    if ("res" in mod) {
        res = scanRecursiveMod(filepath, res, mod.res);
    }

    if ("src" in mod) {
        src = scanRecursiveMod(filepath, src, mod.src);
        json.write("user/cache/loadorder.json", src);
    }
}

function detectChangedMods() {
    let changed = false;

    for (let mod of modsConfig) {
        if (!fs.existsSync(getModFilepath(mod) + "mod.config.json")) {
            changed = true;
            break;
        }

        let config = json.parse(json.read(getModFilepath(mod) + "mod.config.json"));

        if (mod.name !== config.name || mod.author !== config.author || mod.version !== config.version) {
            changed = true;
            break;
        }
    }

    if (changed) {
        modsConfig = [];
    }

    return changed;
}

function detectMissingMods() {
    if (!fs.existsSync("user/mods/")) {
        return;
    }

    let dir = "user/mods/";
    let mods = utility.getDirList(dir);

    for (let mod of mods) {
        /* check if config exists */
        if (!fs.existsSync(dir + mod + "/mod.config.json")) {
            logger.logError("Mod " + mod + " is missing mod.config.json");
            logger.logError("Forcing server shutdown...");
            process.exit(1);
        }

        let config = json.parse(json.read(dir + mod + "/mod.config.json"));
        let found = false;

        /* check if mod is already in the list */
        for (let installed of modsConfig) {
            if (installed.name === config.name) {
                logger.logInfo("Mod " + mod + " is installed");
                found = true;
                break;
            }
        }

        /* add mod to the list */
        if (!found) {
            if (!config.version || config.files || config.filepaths) {
                logger.logError(`Mod ${mod} is out of date and not compatible with this version of EmuTarkov`);
                logger.logError("Forcing server shutdown...");
                process.exit(1);
            }
            logger.logWarning("Mod " + mod + " not installed, adding it to the modlist");
            modsConfig.push({"name": config.name, "author": config.author, "version": config.version, "enabled": true});
            serverConfig.rebuildCache = true;
            json.write("user/configs/mods.json", modsConfig);
        }
    }
}

function isRebuildRequired() {
    if (!fs.existsSync("user/cache/mods.json")
    || !fs.existsSync("user/cache/db.json")
    || !fs.existsSync("user/cache/res.json")
    || !fs.existsSync("user/cache/loadorder.json")) {
        return true;
    }

    let cachedlist = json.parse(json.read("user/cache/mods.json"));

    if (modsConfig.length !== cachedlist.length) {
        return true;
    }

    for (let mod in modsConfig) {
        /* check against cached list */
        if (modsConfig[mod].name !== cachedlist[mod].name
        || modsConfig[mod].author !== cachedlist[mod].author
        || modsConfig[mod].version !== cachedlist[mod].version
        || modsConfig[mod].enabled !== cachedlist[mod].enabled) {
            return true;
        }
    }

    return false;
}

function loadAllMods() {
    for (let element of modsConfig) {
        if (!element.enabled) {
            logger.logWarning("Skipping mod " + element.author + "-" + element.name + "-" + element.version);
            continue;
        }

        let filepath = getModFilepath(element);
        let mod = json.parse(json.read(filepath + "mod.config.json"));
        loadMod(mod, filepath);
    }
}

function flush() {
    db = {};
    res = {};
}

function dump() {
    json.write("user/cache/db.json", db);
    json.write("user/cache/res.json", res);
}

function scanRecursiveRoute(filepath) {
    let baseNode = {};
    let directories = utility.getDirList(filepath);
    let files = fs.readdirSync(filepath);

    // remove all directories from files
    for (let directory of directories) {
        for (let file in files) {
            if (files[file] === directory) {
                files.splice(file, 1);
            }
        }
    }

    // make sure to remove the file extention
    for (let node in files) {
        let fileName = files[node].split('.').slice(0, -1).join('.');
        baseNode[fileName] = filepath + files[node];
    }

    // deep tree search
    for (let node of directories) {
        baseNode[node] = scanRecursiveRoute(filepath + node + "/");
    }

    return baseNode;
}

function routeAll() {
    db = scanRecursiveRoute("db/");
    res = scanRecursiveRoute("res/");
    json.write("user/cache/loadorder.json", json.parse(json.read("src/loadorder.json")))

    /* add important server paths */
    db.user = {
        "configs": {
            "server": "user/config/server.json"
        },
        "events": {
            "schedule": "user/events/schedule.json"
        }   
    }
}

function all() {
    detectMissingMods();

    /* check if loadorder is missing */
    if (!fs.existsSync("user/cache/loadorder.json")) {
        logger.logWarning("Loadorder mismatch");
        serverConfig.rebuildCache = true;
    }

    /* detect if existing mods changed */
    if (detectChangedMods()) {
        logger.logWarning("Modlist mismatch");
        serverConfig.rebuildCache = true;
    }

    /* check if db need rebuid */
    if (isRebuildRequired()) {
        logger.logWarning("Rebuild required");
        serverConfig.rebuildCache = true;
    }

    /* rebuild db */
    if (serverConfig.rebuildCache) {
        logger.logWarning("Rebuilding routes");
        
        flush();
        routeAll();
        detectMissingMods();
        loadAllMods();
        dump();

        return;
    }

    db = json.parse(json.read("user/cache/db.json"));
    res = json.parse(json.read("user/cache/res.json"));
}

module.exports.all = all;