"use strict";
this.name = "Diplomacy_WarZones";
this.author = "phkb, m4r35n357";
this.copyright = "© 2011, 2012 m4r35n357 (Ian Smith)";
this.licence = "Creative Commons: attribution, non-commercial, sharealike. 3.0";
this.description = "Space battles for Diplomacy Warzones";

this._aiDebug = false; // AI debug
this._always = false; // Battles at all systems
this._start = 0.3; // Each faction starts this fraction of w->p away from the site
this._spread = 5000; // Cluster radius for spawning factions
this._battleProbability = 0.75; // Battle probability
this._aftermathProbability = 0.5; // aftermath of a battle
this._destroyerChance = 0.25; // chance of there being destroyers involved
this._totalShips = 48; // All factions combined, but not including escorts
this._battleGap = 10; // minimum number of days between battles for a system

this._priceBoost = 0.20; // 20% boost to prices
this._quantityDrop = 0.20; // 20% reduction in quantities

// The cast
this._factions = [
    { title: "System1", systemID: -1, role: "warzone1-generic" },
    { title: "System2", systemID: -1, role: "warzone2-generic" },
    { title: "Feudal1", systemID: -1, role: "warzone1-feudal" },
    { title: "Feudal2", systemID: -1, role: "warzone2-feudal" },
    { title: "Commies1", systemID: -1, role: "warzone1-commies", names: "warzones_fleet_commies" },
    { title: "Commies2", systemID: -1, role: "warzone2-commies", names: "warzones_fleet_commies" },
    { title: "Dictators1", systemID: -1, role: "warzone1-dictators", names: "warzones_fleet_imperials" }, // imp
    { title: "Dictators2", systemID: -1, role: "warzone2-dictators", names: "warzones_fleet_imperials" }, // imp
    { title: "Dictators3", systemID: -1, role: "warzone1-dictators", names: "warzones_fleet_juntas" }, // junta
    { title: "Dictators4", systemID: -1, role: "warzone2-dictators", names: "warzones_fleet_juntas" }, // junta
];

this._fightingFactions = [];
this._enemyVar = "warzone_enemy_";
this._battles = {};

this.libSettings = {
    Name: this.name,
    Display: expandDescription("[warzones_config_display]"),
    Alias: expandDescription("[warzones_config_alias]"),
    Alive: "libSettings",
    Bool: {
        B0: { Name: "_aiDebug", Def: false, Desc: expandDescription("[warzones_aidebug]") },
        Info: expandDescription("[warzones_config_bool_info]")
    },
    SInt: {
        S0: { Name: "_battleProbability", Def: 0.75, Min: 0.01, Max: 1, Float: true, Desc: expandDescription("[warzones_battlechance]") },
        S1: { Name: "_aftermathProbability", Def: 0.5, Min: 0.01, Max: 1, Float: true, Desc: expandDescription("[warzones_aftermathchance]") },
        S2: { Name: "_spread", Def: 5000, Min: 100, Max: 30000, Desc: expandDescription("[warzones_spread]") },
        S3: { Name: "_totalShips", Def: 48, Min: 10, Max: 100, Desc: expandDescription("[warzones_totalships]") },
        S4: { Name: "_destroyerChance", Def: 0.25, Min: 0.00, Max: 1, Float: true, Desc: expandDescription("[warzones_destroyerchance]") },
        S5: { Name: "_battleGap", Def: 10, Min: 1, Max: 1000, Desc: expandDescription("[warzones_battlegap]") },
        S6: { Name: "_priceBoost", Def: 0.2, Min: 0, Max: 10, Float: true, Desc: expandDescription("[warzones_priceboost]", { percent: "%%" }) },
        S7: { Name: "_quantityDrop", Def: 0.2, Min: 0, Max: 10, Float: true, Desc: expandDescription("[warzones_quantitydrop]", { percent: "%%" }) },
        Info: expandDescription("[warzones_config_sint_info]")
    }
};

//-------------------------------------------------------------------------------------------------------------
this.startUp = function () {
    if (missionVariables.Warzones_PriceBoost) this._priceBoost = parseFloat(missionVariables.Warzones_PriceBoost);
    if (missionVariables.Warzones_QuantityDrop) this._quantityDrop = parseFloat(missionVariables.Warzones_QuantityDrop);

    var msi = worldScripts.MarketScriptInterface_Main;
    msi.$addMarketInterface("system_local", "$updateLocalCommodityDefinition", this.name, "lowest");

    if (worldScripts.Factions) {
        log(this.name, "Monkey-patching Factions so warzones can have priority");
        worldScripts.Factions._hold_shipWillExitWitchspace = worldScripts.Factions.shipWillExitWitchspace;
    }
}

//-------------------------------------------------------------------------------------------------------------
this.startUpComplete = function () {
    if (worldScripts.Lib_Config) {
        worldScripts.Lib_Config._registerSet(this.libSettings);
    }
    if (missionVariables.Warzones_Battles) this._battles = JSON.parse(missionVariables.Warzones_Battles);
    if (missionVariables.Warzones_BattleProbability) this._battleProbability = parseInt(parseFloat(missionVariables.Warzones_BattleProbability) * 100) / 100;
    if (missionVariables.Warzones_AftermathProbability) this._aftermathProbability = parseInt(parseFloat(missionVariables.Warzones_AftermathProbability) * 100) / 100;
    if (missionVariables.Warzones_DestroyerChance) this._destroyerChance = parseInt(parseFloat(missionVariables.Warzones_DestroyerChance) * 100) / 100;
    if (missionVariables.Warzones_Spread) this._spread = parseInt(missionVariables.Warzones_Spread);
    if (missionVariables.Warzones_TotalShips) this._totalShips = parseInt(missionVariables.Warzones_TotalShips);
    if (missionVariables.Warzones_BattleGap) this._battleGap = parseInt(missionVariables.Warzones_BattleGap);

    var wsi = worldScripts.WireframeShipImages;
    if (wsi) {
        wsi._lookupByDataKey["warzone1-sidewinder"] = "sidewinder";
        wsi._lookupByDataKey["warzone2-sidewinder"] = "sidewinder";
        wsi._lookupByDataKey["warzone1-sidewinder_derelict"] = "sidewinder";
        wsi._lookupByDataKey["warzone2-sidewinder_derelict"] = "sidewinder";
    }
}

//-------------------------------------------------------------------------------------------------------------
this.playerWillSaveGame = function () {
    missionVariables.Warzones_Battles = JSON.stringify(this._battles);
    missionVariables.Warzones_BattleProbability = this._battleProbability;
    missionVariables.Warzones_AftermathProbability = this._aftermathProbability;
    missionVariables.Warzones_DestroyerChance = this._destroyerChance;
    missionVariables.Warzones_Spread = this._spread;
    missionVariables.Warzones_TotalShips = this._totalShips;
    missionVariables.Warzones_BattleGap = this._battleGap;
    missionVariables.Warzones_PriceBoost = this._priceBoost;
    missionVariables.Warzones_QuantityDrop = this._quantityDrop;
}

//-------------------------------------------------------------------------------------------------------------
this.$updateLocalCommodityDefinition = function (goodDefinition, station, systemID) {
    var we = worldScripts.DayDiplomacy_040_WarEngine;
    var wz = worldScripts.Diplomacy_WarZones;
    if (we && system.ID >= 0) {
        if (we.$isSystemWarring(system.ID)) {
            // apply price boost
            // calculate the new price, based on the price limits in 
            var newprice = Math.round(goodDefinition.price * (1 + wz._priceBoost), 2);
            var newqty = Math.round(goodDefinition.quantity * (1 - wz._quantityDrop), 2);
            // adjust the price property
            log(this.name + ".debug", "changing price on " + goodDefinition.key + " from " + goodDefinition.price + " to " + newprice);
            goodDefinition.price = newprice;
            goodDefinition.quantity = newqty;
        }
    }
    return goodDefinition;
}

//-------------------------------------------------------------------------------------------------------------
// Helpers
this.$setDisplayName = function (ship) {
    return ship._fighterName + " " + ship.name;
}

//-------------------------------------------------------------------------------------------------------------
this.$addFaction = function (faction, shipList, pos) {
    for (var i = 0; i < shipList.length; i++) {
        var f_ship = shipList[i];
        var pilot = this.$createPilot(faction.systemID, 0, 20);
        this.$setShipCrew(f_ship, pilot);
        f_ship._faction = faction;
        f_ship._fighterName = faction.title;
        f_ship.displayName = this.$setDisplayName(f_ship);
        //f_ship.bounty = 100.0; not sure about this
        f_ship.savedCoordinates = pos;
        f_ship.homeSystem = faction.systemID;
        f_ship.switchAI("warzoneAI.plist");
        f_ship.setScript("warzone_ship_script.js");
        f_ship.script._logging = this._logging;
        log(this.name + ".debug", faction.title + " [" + i + "] " + f_ship.name + ", " + f_ship.AI + " " + f_ship.script);
        this._aiDebug && (f_ship.reportAIMessages = true);
        if (f_ship.escorts) {
            for (var j = 0; j < f_ship.escorts.length; j++) {
                var escort = f_ship.escorts[j];
                var e_pilot = this.$createPilot(faction.systemID, 0, 20);
                this.$setShipCrew(escort, e_pilot);
                escort._faction = faction;
                escort._isEscort = true;
                escort._fighterName = faction.title; // + " " + i + ":" + j;
                escort.displayName = escort._fighterName + " " + escort.name;
                //escort.bounty = 50.0;
                escort.savedCoordinates = pos;
                escort.switchAI("warzoneEscortAI.plist");
                log(this.name + ".debug", "  [" + i + ":" + j + "] " + escort.name + ", " + escort.AI + " " + escort.script);
                this._aiDebug && (escort.reportAIMessages = true);
            }
        }
    }
}

//-------------------------------------------------------------------------------------------------------------
this.$writeMissionVariables = function () {
    for (var i = 0; i < this._fightingFactions.length; i++) {
        if (this._fightingFactions[i].playerHasKilled) {
            missionVariables[this._enemyVar + this._fightingFactions[i].systemID] = 1;
        } else {
            missionVariables[this._enemyVar + this._fightingFactions[i].systemID] = 0;
        }
    }
}

//-------------------------------------------------------------------------------------------------------------
// Event handlers
this.shipWillExitWitchspace = function () {
    // Activation is based on whether two systems are at war with each other
    if (system.isInterstellarSpace) return;

    var wz = worldScripts.DayDiplomacy_040_WarEngine;
    var ds = worldScripts.DayDiplomacy_010_Systems;
    var dc = worldScripts.DayDiplomacy_060_Citizenships;

    var act1 = ds.$retrieveActorFromSystem(galaxyNumber, system.ID);

    var slist = system.info.systemsInRange(7);
    var i = slist.length;
    var target = -1;
    var temptarget = -1
    while (i--) {
        temptarget = slist[i].systemID;
        var act2 = ds.$retrieveActorFromSystem(galaxyNumber, slist[i].systemID);
        if (wz.$areActorsWarring(act1.id, act2.id)) { target = slist[i].systemID; break; }
    }
    var skip = false;

    // don't do battles with the same system if last one was less that 10 days prior.
    if (!this._always) {
        var curr = this._battles[system.ID];
        var tgt = this._battles[target];
        if (!curr) curr = clock.days - 50; // if we haven't had a battle yet, make sure it's possible straight away
        if (!tgt) tgt = clock.days - 50;
        if (clock.days - curr < this._battleGap) skip = true;
        if (clock.days - tgt < this._battleGap) skip = true;
    }

    var allowFactions = true;
    
    if (!skip) {
        if (this._always || (target >= 0 && Math.random() <= this._battleProbability)) {
            allowFactions = false;

            log(this.name + ".debug", "initiating war zone");

            this._battles[system.ID] = clock.days;
            this._battles[target] = clock.days;

            // make sure there is always a target system, even when testing
            if (target == -1) target = temptarget;

            this._fightingFactions = this.$buildFactions(System.infoForSystem(galaxyNumber, target), true);

            var flagActive = false;
            if (dc._flag) {
                // add in player assignment based on their current flag
                if (dc._flag.galaxyID == galaxyNumber && dc._flag.systemID == system.ID) {
                    player.ship._faction = this._fightingFactions[0];
                    this._fightingFactions[1].playerIsEnemy = true;
                    this._fightingFactions[1].playerHasKilled = true;
                    this._fightingFactions[0].playerIsEnemy = false;
                    this._fightingFactions[0].playerHasKilled = false;
                    flagActive = true;
                }
                if (dc._flag.galaxyID == galaxyNumber && dc._flag.systemID == target) {
                    player.ship._faction = this._fightingFactions[1];
                    this._fightingFactions[0].playerIsEnemy = true;
                    this._fightingFactions[0].playerHasKilled = true;
                    this._fightingFactions[1].playerIsEnemy = false;
                    this._fightingFactions[1].playerHasKilled = false;
                    flagActive = true;
                }
            }

            // Factions
            this._factionsize = this._totalShips / this._fightingFactions.length;
            log(this.name + ".debug", "Battle: between " + system.name + " (" + system.ID + ") and " + System.systemNameForID(target) + " (" + target + "), " + this._factionsize + " ships per side.");
            for (var i = 0; i < this._fightingFactions.length; i++) {
                var pos = this._fightingFactions[i].unit;
                this.$addFaction(
                    this._fightingFactions[i],
                    system.addShips(this._fightingFactions[i].role, (this._fightingFactions[i].hasDestroyer ? this._factionsize - 1 : this._factionsize), pos, this._spread),
                    pos
                );
                if (this._fightingFactions[i].hasDestroyer) {
                    this.$addFaction(
                        this._fightingFactions[i],
                        system.addShips(this._fightingFactions[i].role + "-destroyer", 1, pos, this._spread),
                        pos
                    );
                }
                if (!flagActive) {
                    // this covers the situation where the player has joined in the battle on one side or the other, 
                    // even though they aren't flying a flag for either side
                    if (missionVariables[this._enemyVar + this._fightingFactions[i].systemID] && (missionVariables[this._enemyVar + this._fightingFactions[i].systemID] > 0)) {
                        this._fightingFactions[i].playerIsEnemy = true;
                    } else {
                        this._fightingFactions[i].playerIsEnemy = false;
                    }
                    this._fightingFactions[i].playerHasKilled = false;
                }
            }
        } else {
            if (target >= 0 && Math.random() <= this._aftermathProbability) {
                allowFactions = false;
                log(this.name + ".debug", "setting up aftermath");
                var factions = this.$buildFactions(System.infoForSystem(galaxyNumber, target), false);

                // spawn up to 6 each of the different sides
                var pos = Vector3D(0.0, 0.0, this._start).fromCoordinateSystem("wpu");
                var side1 = system.addShips(factions[0].role + "_derelict", Math.floor(Math.random() * 6) + 1, pos, 25600);
                this.$aftermath(factions[0], side1);
                var side2 = system.addShips(factions[1].role + "_derelict", Math.floor(Math.random() * 6) + 1, pos, 25600);
                this.$aftermath(factions[1], side2);
                // add some escape pods (between 0 and 2 for both sides)
                var num = Math.floor(Math.random() * 3);
                if (num > 0) {
                    var escape1 = system.addShips("escape-capsule", num, pos, 25600);
                    for (var i = 0; i < escape1.length; i++) {
                        var pilot = this.$createPilot(system.ID, 0, Math.floor(Math.random() * 100) + 10);
                        this.$setShipCrew(escape1[i], pilot);
                    }
                }
                num = Math.floor(Math.random() * 3);
                if (num > 0) {
                    var escape2 = system.addShips("escape-capsule", num, pos, 25600);
                    for (var i = 0; i < escape2.length; i++) {
                        var pilot = this.$createPilot(target, Math.floor(Math.random() * 100) + 10, 0);
                        this.$setShipCrew(escape2[i], pilot);
                    }
                }
                // add some alloys
                system.addShips("alloys", Math.floor(Math.random() * 10) + 10, pos, 25600);
                // and some cargo (but not a lot)
                system.addShips("barrel", Math.floor(Math.random() * 4) + 1, pos, 25600);
            }
        }
    }
    
    if (allowFactions && worldScripts.Factions) {
        worldScripts.Factions._hold_shipWillExitWitchspace();
    }
}

//-------------------------------------------------------------------------------------------------------------
this.$buildFactions = function (targetInfo, withPosition) {
    var factions = [];

    if (system.government == 1 && worldScripts["feudal-mission.js"]) factions.push(JSON.parse(JSON.stringify(this._factions[2])));
    else if (system.government == 3 && worldScripts["dictators.js"] && system.economy <= 3) factions.push(JSON.parse(JSON.stringify(this._factions[6])));
    else if (system.government == 3 && worldScripts["dictators.js"] && system.economy > 3) factions.push(JSON.parse(JSON.stringify(this._factions[8])));
    else if (system.government == 4 && worldScripts["communist_population"]) factions.push(JSON.parse(JSON.stringify(this._factions[4])));
    else factions.push(JSON.parse(JSON.stringify(this._factions[0])));

    var groupName1;
    if (!factions[0].hasOwnProperty("names")) groupName1 = system.name + " " + expandDescription("[warzones_fleet]");
    else groupName1 = expandDescription("[" + factions[0].names + "]", {system: system.name});

    factions[0].title = groupName1;
    factions[0].systemID = system.ID;
    if (withPosition) factions[0].unit = Vector3D(0.0, 0.01, this._start).fromCoordinateSystem("wpu");

    if (targetInfo.government == 1 && worldScripts["feudal-mission.js"]) factions.push(JSON.parse(JSON.stringify(this._factions[3])));
    else if (targetInfo.government == 3 && worldScripts["dictators.js"] && targetInfo.economy <= 3) factions.push(JSON.parse(JSON.stringify(this._factions[7])));
    else if (targetInfo.government == 3 && worldScripts["dictators.js"] && targetInfo.economy > 3) factions.push(JSON.parse(JSON.stringify(this._factions[9])));
    else if (targetInfo.government == 4 && worldScripts["communist_population"]) factions.push(JSON.parse(JSON.stringify(this._factions[5])));
    else factions.push(JSON.parse(JSON.stringify(this._factions[1])));
    var groupName2 = groupName1
    do {
        if (!factions[1].hasOwnProperty("names")) groupName2 = targetInfo.name + " " + expandDescription("[warzones_fleet]");
        else groupName2 = expandDescription("[" + factions[1].names + "]", {system: targetInfo.name});
    } while (groupName2 == groupName1);

    factions[1].title = groupName2;
    factions[1].systemID = targetInfo.systemID;
    if (withPosition) factions[1].unit = Vector3D(0.0, -0.01, this._start).fromCoordinateSystem("wpu");

    factions[0].oppositeRole = factions[1].role;
    factions[1].oppositeRole = factions[0].role;

    if (Math.random() < this._destroyerChance) factions[0].hasDestroyer = true;
    if (factions[0].hasDestroyer && Math.random() < 0.90) factions[1].hasDestroyer = true;

    return factions;
}

//-------------------------------------------------------------------------------------------------------------
// returns a pilot object
this.$createPilot = function $createPilot(home, legal, insurance) {
    var pilot = {};
    // create the pilot
    pilot["name"] = expandDescription("%N [nom]");
    pilot["description"] = expandDescription("[warzones_pilot_description]", { species: System.infoForSystem(galaxyNumber, home).inhabitant.toLowerCase(), system: System.systemNameForID(home) });
    pilot["homeSystem"] = home;
    pilot["legalStatus"] = legal;
    pilot["insurance"] = (insurance ? insurance : 0);
    pilot["species"] = System.infoForSystem(galaxyNumber, home).inhabitant.toLowerCase();
    pilot["seed"] = "0 0 0 0 " + home + " 2";
    return pilot;
}

//-------------------------------------------------------------------------------------------------------------
this.$setShipCrew = function (ship, pilot) {
    ship.setCrew({
        name: pilot.mame,
        origin: pilot.homeSystem,
        insurance: pilot.insurance,
        bounty: pilot.legalStatus,
        short_description: pilot.description,
        random_seed: pilot.seed
    });
}

//-------------------------------------------------------------------------------------------------------------
this.$aftermath = function (faction, shipList) {
    for (var i = 0; i < shipList.length; i++) {
        var f_ship = shipList[i];
        f_ship._faction = faction;
        f_ship._fighterName = faction.title;
        f_ship.displayName = this.$setDisplayName(f_ship);
        this.$makeShipDerelict(shipList[i]);
    }
}

//-------------------------------------------------------------------------------------------------------------
this.$makeShipDerelict = function (shp) {
    shp.bounty = 0;
    // set script to default, to avoid a special script for the trader doing stuff. (like setting a new AI)
    shp.setScript("oolite-default-ship-script.js");
    shp.switchAI("oolite-nullAI.js");

    // remove any escorts that came with the ship
    if (shp.escorts) {
        var k = shp.escorts.length;
        while (k--) shp.escorts[k].remove(true);
    }

    shp.script.shipLaunchedEscapePod = this.$warzones_derelict_shipLaunchedEscapePod; // function to remove the escapepod after launch.
    if (shp.equipmentStatus("EQ_ESCAPE_POD") === "EQUIPMENT_UNAVAILABLE") shp.awardEquipment("EQ_ESCAPE_POD");
    shp.abandonShip(); // make sure no pilot is left behind and this command turns the ship into cargo.
    shp.displayName = shp.displayName + " (derelict)";
    shp.maxEnergy = 50;
    shp.lightsActive = false;
}

//-------------------------------------------------------------------------------------------------------------
this.$warzones_derelict_shipLaunchedEscapePod = function $warzones_derelict_shipLaunchedEscapePod(pod, passengers) {
    pod.remove(true); // we don't want floating escapepods around but need them initially to create the derelict.
}

//-------------------------------------------------------------------------------------------------------------
this.shipDockedWithStation = function (station) {
    this.$writeMissionVariables();
}

//-------------------------------------------------------------------------------------------------------------
this.shipWillEnterWitchspace = function (cause) {
    this.$writeMissionVariables();
}
