
const dateFormat = require('/usr/local/lib/node_modules/dateformat');

module.exports = {
    defaultConfig: {
        enabled: true,
        debug: true,
        runs: true,
        apiKey: ''
    },
    defaultConfigDetails: {
        debug: { label: 'Print debug messages' },
        runs: { label: 'Also log runs' },
        apiKey: { label: 'SWDB instance API key', type: 'textarea' }
    },
    pluginName: 'SWDB',
    pluginDescription: 'Uploads data to SWDB.',
    target_host: 'localhost',
    target_port: '80',
    target_url_run: '/API/v1/log-run',
    target_url_profile: '/API/v1/log-profile',
    temp: {},
    proxy: null,
    config: null,

    /**
     * Initializes he plugin.
     * 
     * @param proxy The proxy.
     * @param Global configurations.
     */
    init(proxy, config) {
        this.proxy = proxy;
        this.config = config;

        proxy.on('apiCommand', (req, resp) => {
            try {
                wizardID = '';
                command = '';
                apiKey = config.Config.Plugins[this.pluginName].apiKey;
                if (config.Config.Plugins[this.pluginName].enabled) {
                    command = req["command"];
                    wizardID = req["wizard_id"];
                }
                if (!this.temp[wizardID]) {
                    this.temp[wizardID] = {};
                }

                // USER LOGIN: Upload profile
                if (command === 'HubUserLogin') {
                    // Check that API key is filled in
                    if (!apiKey) {
                        this.log('error', `Profile upload is enabled, but missing API key. Check ${this.pluginName} settings.`);
                        return;
                    }
                    this.log('info', 'Uploading profile to SWDB...')
                    var request = require('/usr/local/lib/node_modules/request')

                    var querystring = require('/usr/local/lib/node_modules/querystring');
                    //var http = require('/usr/local/lib/node_modules/http');
                    var http = require('http');
                    //var fs = require('/usr/local/lib/node_modules/fs');
                    var post_data = querystring.stringify({
                        'data' : JSON.stringify(resp),
                        'key' : apiKey
                    });

                    // An object of options to indicate where to post to
                    var post_options = {
                        host: this.target_host,
                        port: this.target_port,
                        path: this.target_url_profile,
                        method: 'POST',
                        headers: {
                            'Content-Type': 'application/x-www-form-urlencoded',
                            'Content-Length': Buffer.byteLength(post_data)
                        }
                    };

                    // Set up the request
                    var resp = ""
                    var post_req = http.request(post_options, function(res) {
                        res.setEncoding('utf8');
                        res.on('data', function (chunk) {
                            resp = chunk;
                        });
                    });
                    

                    // post the data
                    post_req.write(post_data);
                    post_req.end();

                }

                if (command === 'BattleScenarioStart') {
                    //for (var key in req) {
                    //    this.log('DEBUG', `IVV SCE START: ${key}: ${req[key]}`);
                    //}
                    if (req["helper_list"] != null || req["mentor_helper_list"] != null || req["npc_friend_helper_list"] != null ){
                        this.temp[wizardID].helper = 1;
                    }
                    else{
                        this.temp[wizardID].helper = 0;
                    }
                    this.temp[wizardID].area = req.region_id;
                    this.temp[wizardID].stage = req.stage_no;
                    this.temp[wizardID].difficulty = req.difficulty;
                }

                if (command === 'BattleScenarioResult' || command === 'BattleDungeonResult' || command === 'BattleDimensionHoleDungeonResult') {
                    //proxy.log({ type: 'info', source: 'plugin', name: this.pluginName, message: `IVV RUN RESULT: ${resp}` })
                    //for (var key in req) {
                    //    this.log("info", `IVV REQ: ${key}: ${req[key]}`);
                    //}
                    //for (var key in resp) {
                    //    this.log("info", `IVV RES: ${key}: ${resp[key]}`);
                    //}
                    this.parse_normal(req, resp);
                }

                if (command === 'BattleRiftOfWorldsRaidResult') {
                    this.parse_raid_rift(req, resp);
                }

                if (command === 'BattleRiftDungeonResult') {
                    this.parse_elemental_rift(req, resp);
                }
            }
            catch (e) {
                proxy.log({ type: 'error', source: 'plugin', name: this.pluginName, message: `An unexpected error occured: ${e.message}` });
            }
        });
    },

    /**
     * Parses a reward item (no runes!).
     * 
     * @param crate The crate object.
     * @return array of {item, unit, pieces, quantity, shapeshifting}
     */
    getItem(crate) {
        item = 0;
        unit = 0;
        pieces = 0;
        quantity = 0;
        shapeshifting = 0;
        if (crate.random_scroll && (crate.random_scroll.item_master_id === 1 || crate.random_scroll.item_master_id === 8 || crate.random_scroll.item_master_id === 2)){
            // 1: Unknown scroll
            // 2: Mystical Scroll
            // 8: Summoning Stones
            item = crate.random_scroll.item_master_id;
            quantity = crate.random_scroll.item_quantity;
        }
        if (crate.costume_point) {
            shapeshifting = crate.costume_point;
        }
        if (crate.rune_upgrade_stone) {
            item = 'Power Stone'; // TODO: Do they exist?
            quantity = crate.rune_upgrade_stone.item_quantity;
        }
        if (crate.unit_info) {
            unit = crate.unit_info.unit_master_id;
        }
        if (crate.material) {
            item = crate.material.item_master_id;
            quantity = crate.material.item_quantity;
        }
        if (crate.summon_pieces) {
            pieces = crate.summon_pieces.item_master_id;
            quantity = crate.summon_pieces.item_quantity;
        }
        if (crate.craft_stuff) {
            item = crate.craft_stuff.item_master_id;
            quantity = crate.craft_stuff.item_quantity;
        }
        drop = {item, unit, pieces, quantity, shapeshifting}
        return drop;
    },

    /**
     * TODO! Unaddapted.
     * Parses a rift reward item (no runes!).
     * 
     * @param crate The crate object.
     * @return array of {item, unit, pieces, quantity}
     */
    getItemRift(item, entry) {
        if (item.type === 8) {
            const rune = item.info;
            entry.drop = 'Rune';
            entry.grade = `${rune.class}*`;
            entry.sell_value = rune.sell_value;
            entry.set = gMapping.rune.sets[rune.set_id];
            entry.slot = rune.slot_no;
            entry.efficiency = gMapping.getRuneEfficiency(rune).current;
            entry.rarity = gMapping.rune.class[rune.sec_eff.length];
            entry.main_stat = gMapping.getRuneEffect(rune.pri_eff);
            entry.prefix_stat = gMapping.getRuneEffect(rune.prefix_eff);

            rune.sec_eff.forEach((substat, i) => {
                entry[`sub${i + 1}`] = gMapping.getRuneEffect(substat);
            });
        }
        if (item.info.craft_type_id) {
            enhancement = this.getEnchantVals(item.info.craft_type_id, item.info.craft_type);
            entry.drop = enhancement.drop;
            entry.sell_value = item.sell_value;
            entry.set = enhancement.set;
            entry.main_stat = enhancement.type;
            entry.sub1 = enhancement.min;
            entry.sub2 = enhancement.max;
        }
        return entry;
    },

    /**
     * TODO! Will I need this.
     * Saves run data to a file in CSV format.
     * 
     */
    saveToFile(entry, filename, headers, proxy) {
        const csvData = [];
        const self = this;
        fs.ensureFile(path.join(config.Config.App.filesPath, filename), err => {
        if (err) {
            return;
        }
        csv
            .fromPath(path.join(config.Config.App.filesPath, filename), { ignoreEmpty: true, headers, renameHeaders: true })
            .on('data', data => {
            csvData.push(data);
            })
            .on('end', () => {
            csvData.push(entry);
            csv.writeToPath(path.join(config.Config.App.filesPath, filename), csvData, { headers }).on('finish', () => {
                proxy.log({ type: 'success', source: 'plugin', name: self.pluginName, message: `Saved run data to ${filename}` });
            });
            });
        });
    },

    /**
     * Parses a battle data.
     * Valid for scenario, dungeons and Dimensional Hole.
     *
     * @param req The request
     * @param resp The response.
     */
    parse_normal(req, resp) {
        const { command } = req;
        const { wizard_id: wizardID, wizard_name: wizardName } = resp.wizard_info;

        const run = {};
        const run_party = {};
        const run_k_party = {};
        log = true;

        if (command === 'BattleDungeonResult') {
            //for (var key in req) {
            //    this.log("DEBUG", `IVV DUNGEON REQ: ${key}: ${req[key]}`);
            //}
            if (req.dungeon_id > 10000){
                // Hall of Heroes or unknown
                log = false;
            }
            run.area = req.dungeon_id;
            run.stage = req.stage_id;
            run.difficulty = 0
            // TODO: Read request
            run.helper = 0;
        }

        if (command === 'BattleScenarioResult') {
            //for (var key in this.temp[wizardID]) {
            //    this.log("DEBUG", `IVV TEMP: ${key}: ${this.temp[wizardID][key]}`);
            //}
            //for (var key in req) {
            //    this.log("DEBUG", `IVV REQ: ${key}: ${req[key]}`);
            //}
            run.helper = this.temp[wizardID].helper;
            run.area = this.temp[wizardID].area;
            run.stage = this.temp[wizardID].stage;
            run.difficulty = this.temp[wizardID].difficulty;
        }

        if (command === 'BattleDimensionHoleDungeonResult') {
            //if (gMapping.dungeon[resp.dungeon_id]) {
            //    run.dungeon = `${gMapping.dungeon[resp.dungeon_id]} Level ${resp.difficulty}`;
            //}
            //for (var key in req) {
            //    this.log("DEBUG", `IVV DIMENSONALE REQ: ${key}: ${req[key]}`);
            //}
            run.area = resp.dungeon_id;
            run.stage = resp.difficulty;
            run.difficulty = 0
            run.helper = 0;
        }
        run.uid = wizardID;
        run.win = resp.win_lose; // 1/0 2 is lost in scenario

        run.dtime = dateFormat(new Date(), 'yyyy-mm-dd HH:MM:ss');

        const reward = resp.reward ? resp.reward : {};
        run.time = req.clear_time ? req.clear_time : 0;

        run.mana = reward.mana ? reward.mana : 0;
        run.energy = reward.energy ? reward.energy : 0;
        run.crystal = reward.crystal ? reward.crystal : 0;
        
        // TODO: GET
        //run.helper = 0;

        run.rune = {};
        run.unit = 0;
        run.unit_pieces = {};
        run.sd = 0;
        run.item = {};
        run.shapeshifting = 0;

        if (reward.crate) {
            run.mana = reward.crate.mana ? run.mana + reward.crate.mana : run.mana;
            run.energy = reward.crate.energy ? run.energy + reward.crate.energy : run.energy;
            run.crystal = reward.crate.crystal ? run.crystal + reward.crate.crystal : run.crystal;

            if (reward.crate.rune) {
                const rune = reward.crate.rune;
                //for (var key in rune) {
                //    this.proxy.log({ type: 'info', source: 'plugin', name: this.pluginName, message: `IVV RUNE: ${key}: ${rune[key]}` })
                //}
                run.rune.id = rune.rune_id;
                run.rune.grade = rune.class;
                run.rune.value = rune.sell_value;
                run.rune.set = rune.set_id;
                run.rune.slot = rune.slot_no;
                run.rune.efficiency = gMapping.getRuneEfficiency(rune).current;
                run.rune.quality = rune.rank;
                run.rune.main = rune.pri_eff[0];
                run.rune.main_value = rune.pri_eff[1];
                if (rune.prefix_eff){
                    run.rune.innate = rune.prefix_eff[0];
                    run.rune.innate_value = rune.prefix_eff[1];
                }
                run.rune['substat_1'] = 0;
                run.rune['substat_1_value'] = 0;
                run.rune['substat_2'] = 0;
                run.rune['substat_2_value'] = 0;
                run.rune['substat_3'] = 0;
                run.rune['substat_3_value'] = 0;
                run.rune['substat_4'] = 0;
                run.rune['substat_4_value'] = 0;
                rune.sec_eff.forEach((substat, i) => {
                    run.rune[`substat_${i + 1}`] = substat[0];
                    run.rune[`substat_${i + 1}_value`] = substat[1];
                });
            }
            else {
                for (var key in reward.crate) {
                    this.proxy.log({ type: 'info', source: 'plugin', name: this.pluginName, message: `IVV ITEM: ${key}: ${reward.crate[key]}` })
                }
                drop = this.getItem(reward.crate);
                if (drop.unit){
                    run.unit.id = drop.unit;
                }
                if (drop.pieces){
                    run.unit_pieces.id = drop.pieces;
                    run.unit_pieces.quantity = drop.quantity;
                }
                if (drop.item){
                    run.item.id = drop.item;
                    run.item.quantity = drop.quantity;
                }
                run.shapeshifting = drop.shapeshifting;
            }
        }
        if (resp.instance_info) {
            run.sd = resp.instance_info;
        }

        if (resp.unit_list && resp.unit_list.length > 0) {
            resp.unit_list.forEach((unit, i) => {
                run_party[i] = unit.unit_master_id;
                run_k_party[i] = unit.unit_id;
            });
        }

        json_rune = '[';
        if (run.rune.id != undefined){
            json_rune += '{';
            json_rune += `"id": "${run.rune.id}", `;
            json_rune += `"stars": "${run.rune.grade}", `;
            json_rune += `"type": "${run.rune.set}", `;
            json_rune += `"slot": "${run.rune.slot}", `;
            json_rune += `"efficiency": "${run.rune.efficiency}", `;
            json_rune += `"quality": "${run.rune.quality}", `;
            // TODO: Get
            json_rune += `"ancient": 0, `;
            json_rune += `"value": "${run.rune.value}", `;
            json_rune += `"main_stat": "${run.rune.main}", `;
            json_rune += `"main_stat_value": "${run.rune.main_value}", `;
            json_rune += `"innate_stat": "${run.rune.innate}", `;
            json_rune += `"innate_stat_value": "${run.rune.innate_value}", `;
            json_rune += `"substat_1": "${run.rune.substat_1}", `;
            json_rune += `"substat_1_value": "${run.rune.substat_1_value}", `;
            json_rune += `"substat_2": "${run.rune.substat_2}", `;
            json_rune += `"substat_2_value": "${run.rune.substat_2_value}", `;
            json_rune += `"substat_3": "${run.rune.substat_3}", `;
            json_rune += `"substat_3_value": "${run.rune.substat_3_value}", `;
            json_rune += `"substat_4": "${run.rune.substat_4}", `;
            json_rune += `"substat_4_value": "${run.rune.substat_4_value}"`;
            json_rune += '}';
        }
        json_rune = json_rune + "]";

        json_item = '[';
        if (run.item.id != undefined){
            json_item += '{';
            json_item += `"id": "${run.item.id}", `;
            json_item += `"quantity": "${run.item.quantity}"`;
            json_item += '}';
        }
        json_item = json_item + "]";

        json_unit_pieces = '[';
        if (run.unit_pieces.length > 0){
            json_unit_pieces += '{';
            json_unit_pieces += `"id": "${run.unit_pieces.id}", `;
            json_unit_pieces += `"quantity": "${run.unit_pieces.quantity}"`;
            json_unit_pieces += '}';
        }
        json_unit_pieces = json_unit_pieces + "]";

        json_party = '[';
        resp.unit_list.forEach((unit, i) => {
            json_party = json_party + '{"unit_id": ' + run_party[i] + ', "unit_master_id": ' + run_k_party[i] + '},';
        });
        json_party = json_party.slice(0, -1) + "]";

        json = `{`;
        json += `"uid": "${run.uid}", `;
        json += `"dtime": "${run.dtime}", `;
        json += `"area": "${run.area}", `;
        json += `"stage": "${run.stage}", `;
        json += `"difficulty": "${run.difficulty}", `;
        json += `"win": "${run.win}", `;
        json += `"time": "${run.time}", `;
        json += `"mana": "${run.mana}", `;
        json += `"energy": "${run.energy}", `;
        json += `"crystal": "${run.crystal}", `;
        json += `"rune": ${json_rune}, `;
        json += `"sd": ${run.sd}, `;
        json += `"item": ${json_item}, `;
        json += `"unit": ${run.unit}, `;
        json += `"unit_pieces": ${json_unit_pieces}, `;
        json += `"shapeshifting": ${run.shapeshifting}, `;
        json += `"party": ${json_party}, `;
        json += `"helper": "${run.helper}"`;
        json += `}`;
        this.log('DEBUG', `json: ${json}`)
        //const filename = sanitize(`${wizardName}-${wizardID}-runs.csv`);
        //const filename = sanitize(`IVV-${wizardName}-${wizardID}-runs.csv`);
        //this.saveToFile(entry, filename, headers, proxy);
        
        // Make the request
        //var inspect = require('/usr/local/lib/node_modules/eyespect').inspector();
        var request = require('/usr/local/lib/node_modules/request')


        var querystring = require('/usr/local/lib/node_modules/querystring');
        //var http = require('/usr/local/lib/node_modules/http');
        var http = require('http');
        //var fs = require('/usr/local/lib/node_modules/fs');
        var post_data = querystring.stringify({
            'data' : json,
            'key' : apiKey
        });

        // An object of options to indicate where to post to
        var post_options = {
            host: this.target_host,
            port: this.target_port,
            path: this.target_url_run,
            method: 'POST',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded',
                'Content-Length': Buffer.byteLength(post_data)
            }
        };

        // Set up the request
        var resp = ""
        var post_req = http.request(post_options, function(res) {
            res.setEncoding('utf8');
            res.on('data', function (chunk) {
                resp = chunk;
            });
        });
        

        // post the data
        post_req.write(post_data);
        post_req.end();
        
        
    },

    /**
     * TODO Unaddapted.
     * Parses a battle data.
     * Valid for Rift Raids.
     *
     * @param req The request
     * @param resp The response.
     */
    parse_raid_rift(req, resp) {
        const { wizard_id: wizardID, wizard_name: wizardName } = resp.wizard_info;

        let run = {};
        if (gMapping.dungeon[req.dungeon_id]) {
            run.dungeon = `${gMapping.elemental_rift_dungeon[req.dungeon_id]}`;
            isElemental = true;
        }

        //const winLost = resp.win_lose === 1 ? 'Win' : 'Did not kill';

        run.dtime = dateFormat(new Date(), 'yyyy-mm-dd HH:MM:ss');
        //run.result = winLost;

        run.win  = resp.win_lose;
        run.rune = {};
        run.unit = 0;
        run.unit_pieces = {};
        run.sd = 0;
        run.item = {};
        run.shapeshifting = 0;
        run.time = req.clear_time ? req.clear_time : 0;

        const reward = resp.reward ? resp.reward : {};

        if (resp.win_lose === 1) {
            // No crate: Mana of shapeshifting stones
            if (!reward.crate) {
                const reward = resp.battle_reward_list.find(value => value.wizard_id === resp.wizard_info.wizard_id).reward_list[0];
                if (reward.item_master_id === 6) {
                    run.shapeshifting = reward.item_quantity;
                }
                else {
                    run.mana = reward.item_quantity;
                }
            }
            // Rune craft item
            else if (reward.crate.changestones) {
                const item = {
                    info: {
                        craft_type: reward.crate.changestones[0].craft_type,
                        craft_type_id: reward.crate.changestones[0].craft_type_id
                    },
                    sell_value: reward.crate.changestones[0].sell_value
                };
                run.drop= this.getItemRift(item, run);
            }
            // Rune
            else if (reward.crate.rune) {
                const rune = reward.crate.rune;
                //for (var key in rune) {
                //    this.proxy.log({ type: 'info', source: 'plugin', name: this.pluginName, message: `IVV RUNE: ${key}: ${rune[key]}` })
                //}
                run.rune.id = rune.rune_id;
                run.rune.grade = rune.class;
                run.rune.value = rune.sell_value;
                run.rune.set = rune.set_id;
                run.rune.slot = rune.slot_no;
                run.rune.efficiency = gMapping.getRuneEfficiency(rune).current;
                run.rune.quality = rune.rank;
                run.rune.main = rune.pri_eff[0];
                run.rune.main_value = rune.pri_eff[1];
                if (rune.prefix_eff){
                    run.rune.innate = rune.prefix_eff[0];
                    run.rune.innate_value = rune.prefix_eff[1];
                }
                rune.sec_eff.forEach((substat, i) => {
                    run.rune[`substat_${i + 1}`] = substat[0];
                    run.rune[`substat_${i + 1}_value`] = substat[1];
                });
            }
            // Unit (Rainbowmon)
            else if (reward.crate.unit_info && reward.crate.unit_info.unit_master_id > 0) {
                run.unit.id = reward.crate.unit_info.unit_master_id;
            }
            // Item
            else if (!run.drop && resp.reward.crate) {
                //run.drop = this.getItem(reward.resp.reward.crate);
                if (resp.reward.crate.random_scroll && (resp.reward.crate.random_scroll.item_master_id === 1 || resp.reward.crate.random_scroll.item_master_id === 8 || resp.reward.crate.random_scroll.item_master_id === 2)){
                    // 1: Unknown scroll
                    // 2: Mystical Scroll
                    // 8: Summoning Stones
                    run.item.id = resp.reward.crate.random_scroll.item_master_id;
                    run.item.quantity = resp.reward.crate.random_scroll.item_quantity;
                }
                //if (resp.reward.crate.costume_point) {
                //    shapeshifting = resp.reward.crate.costume_point;
                //}
                //if (resp.reward.crate.rune_upgrade_stone) {
                //    item = 'Power Stone'; // TODO: Do they exist?
                //    quantity = resp.reward.crate.rune_upgrade_stone.item_quantity;
                //}
                //if (resp.reward.crate.unit_info) {
                //    unit = resp.reward.crate.unit_info.unit_master_id;
                //}
                if (resp.reward.crate.material) {
                    item = resp.reward.crate.material.item_master_id;
                    quantity = resp.reward.crate.material.item_quantity;
                }
                //if (resp.reward.crate.summon_pieces) {
                //    pieces = resp.reward.crate.summon_pieces.item_master_id;
                //    quantity = resp.reward.crate.summon_pieces.item_quantity;
                //}
                if (resp.reward.crate.craft_stuff) {
                    item = resp.reward.crate.craft_stuff.item_master_id;
                    quantity = resp.reward.crate.craft_stuff.item_quantity;
                }
            }
            else {
                run.drop = 'unknown';
            }
        }

        if (resp.unit_list && resp.unit_list.length > 0) {
            resp.unit_list.forEach((unit, i) => {
                run_party[i] = unit.unit_master_id;
                run_k_party[i] = unit.unit_id;
            });
        }


        //const filename = sanitize(`${wizardName}-${wizardID}-raid-runs.csv`);
        //this.saveToFile(run. filename, headers, proxy);
    },

    /**
     * TODO Unaddapted.
     * Parses a battle data.
     * Valid for Rift Dungeons.
     *
     * @param req The request
     * @param resp The response.
     */
    parse_elemental_rift(req, resp) {
        const { wizard_id: wizardID, wizard_name: wizardName } = resp.wizard_info;

        let entry = {};
        if (gMapping.dungeon[req.dungeon_id]) {
            entry.dungeon = `${gMapping.elemental_rift_dungeon[req.dungeon_id]}`;
            isElemental = true;
        }

        const winLost = req.battle_result === 1 ? 'Win' : 'Did not kill';

        entry.date = dateFormat(new Date(), 'yyyy-mm-dd HH:MM');
        entry.result = winLost;

        if (resp.item_list && resp.item_list.length > 0) {
            resp.item_list.forEach((item, i) => {
                if (item.is_boxing !== 1 || item.id === 2001) {
                entry[`item${i + 1}`] = `${gMapping.craftMaterial[item.id]} x${item.quantity}`;
                } else {
                if (item.id === 2) {
                    entry.drop = 'Mystical Scroll';
                }
                if (item.id === 8) {
                    entry.drop = `Summoning Stones x${item.item_quantity}`;
                }
                if (item.info && item.info.unit_master_id > 0) {
                    entry.drop = `${gMapping.getMonsterName(item.info.unit_master_id)} ${item.class}`;
                }
                if ((item.info && item.info.craft_type_id) || item.type === 8) {
                    entry = this.getItemRift(item, entry);
                }
                }
            });
        }

        if (resp.unit_list && resp.unit_list.length > 0) {
            resp.unit_list.forEach((unit, i) => {
                entry[`team${i + 1}`] = gMapping.getMonsterName(unit.unit_master_id);
            });
        }
        json_rune = '[';
        if (run.rune.length() > 0){
            json_rune += `"id": "${run.rune.id}", `;
            json_rune += `"grade": "${run.rune.grade}", `;
            json_rune += `"set": "${run.rune.set}", `;
            json_rune += `"slot": "${run.rune.slot}", `;
            json_rune += `"efficiency": "${run.rune.efficiency}", `;
            json_rune += `"quality": "${run.rune.quality}", `;
            json_rune += `"value": "${run.rune.value}", `;
            json_rune += `"main_stat": "${run.rune.main}", `;
            json_rune += `"main_stat_value": "${run.rune.main_value}", `;
            json_rune += `"innate_stat": "${run.rune.innate}", `;
            json_rune += `"innate_stat_value": "${run.rune.innate_value}", `;
            json_rune += `"substat_1": "${run.rune.substat_1}", `;
            json_rune += `"substat_1_value": "${run.rune.substat_1_value}", `;
            json_rune += `"substat_2": "${run.rune.substat_2}", `;
            json_rune += `"substat_2_value": "${run.rune.substat_2_value}", `;
            json_rune += `"substat_3": "${run.rune.substat_3}", `;
            json_rune += `"substat_4_value": "${run.rune.substat_3_value}", `;
            json_rune += `"substat_4": "${run.rune.substat_4}", `;
            json_rune += `"substat_4_value": "${run.rune.substat_4_value}", `;
        }
        json_rune = json_party.slice(0, -1) + "]";

        json_item = '[';
        if (run.item.length() > 0){
            json_item += `"id": "${run.item.id}", `;
            json_item += `"quantity": "${run.item.quantity}", `;
        }
        json_item = json_party.slice(0, -1) + "]";

        json_unit_pieces = '[';
        if (run.unit_pieces.length() > 0){
            json_unit_pieces += `"id": "${run.unit_pieces.id}", `;
            json_unit_pieces += `"quantity": "${run.unit_pieces.quantity}", `;
        }
        json_unit_pieces = json_party.slice(0, -1) + "]";

        json_party = '[';
        resp.unit_list.forEach((unit, i) => {
            json_party = json_party + '{"unit_id": ' + run_party[i] + ', "unit_master_id": ' + run_k_party[i] + '},';
        });
        json_party = json_party.slice(0, -1) + "]";

        //this.log('DEBUG', `IVV json_party: ${json_party}`)

        json = `{`;
        json += `"dtime": "${run.dtime}", `;
        json += `"area": "${run.dungeon}", `;
        json += `"stage": "${run.stage}", `;
        json += `"difficulty": "${run.difficulty}", `;
        json += `"win": "${run.win}", `;
        json += `"time": "${run.time}", `;
        json += `"mana": "${run.mana}", `;
        json += `"energy": "${run.energy}", `;
        json += `"crystal": "${run.crystal}", `;
        json += `"rune": ${json_rune}, `;
        json += `"sd": ${run.sd}, `;
        json += `"item": ${json_item}, `;
        json += `"unit": ${run.unit}, `;
        json += `"unit_pieces": ${json_unit_pieces}, `;
        json += `"sapeshifting": ${run.sapeshifting}, `;
        json += `"party": ${json_party}`;
        json += `}`;
        this.log('DEBUG', `json: ${json}`)
    },

    /**
     * TODO ???
     */
    getEnchantVals(craftID, craftType) {
        const map = {};
        const typeNumber = Number(craftID.toString().slice(-4, -2));
        map.set = gMapping.rune.sets[Number(craftID.toString().slice(0, -4))];
        map.grade = gMapping.rune.quality[Number(craftID.toString().slice(-1))];
        map.type = gMapping.rune.effectTypes[typeNumber];

        if (craftType === 2) {
            map.min = gMapping.grindstone[typeNumber].range[Number(craftID.toString().slice(-1))].min;
            map.max = gMapping.grindstone[typeNumber].range[Number(craftID.toString().slice(-1))].max;
            map.drop = 'Grindstone';
        } else {
            map.min = gMapping.enchanted_gem[typeNumber].range[Number(craftID.toString().slice(-1))].min;
            map.max = gMapping.enchanted_gem[typeNumber].range[Number(craftID.toString().slice(-1))].max;
            map.drop = 'Enchanted Gem';
        }
        return map;
    },

    /**
     * Prints a messageto the SWEX output.
     * 
     * @param type Log type. Debug type will be printed depending on config.
     * @param msg The message to log.
     */
    log(type, msg){
        //if (type != "debug" || this.config.Config.Plugins[this.pluginName].debug == true){
            this.proxy.log({ type: type, source: 'plugin', name: this.pluginName, message: msg });
        //}
    }
};
