"use strict";
this.name = "GalCopBB_CargoMonitor";
this.author = "phkb";
this.copyright = "2017 phkb";
this.description = "Monitors collected cargo to make sure mission-related items aren't lost/misplaced";
this.license = "CC BY-NC-SA 4.0";

this._smugglersInstalled = false; // indicates whether Smugglers is installed
this._igtInstalled = false; // indicates whether Illegal Goods Tweak is installed
this._monitor = []; // array of cargo types to monitor
this._commodityIDList = []; // list of all commodity ID's (use for type 8/9 missions)
this._commodityType = {};
this._docking = false; // flag to indicate when the player is about to dock with the station
this._slavesBefore = 0; // number of slaves player had before docking (based on IGT values)
this._holdCargo = []; // holding array of firearms or narcotics cargo (type 14/15 missions)
// pulls cargo out of the way of IGT/smugglers so it can be handled here

//-------------------------------------------------------------------------------------------------------------
this.$addMonitor = function $addMonitor(missID, commodity, sysID, thargoid, pods) {
    this._monitor.push({
        ID: missID,
        commodity: commodity,
        thargoid: thargoid,
        system: sysID,
        containers: [],
        specific_pods: pods
    });
}

//-------------------------------------------------------------------------------------------------------------
this.$removeMonitor = function $removeMonitor(missID) {
    for (var i = this._monitor.length - 1; i >= 0; i--) {
        if (this._monitor[i].ID === missID) this._monitor.splice(i, 1);
    }
}

//-------------------------------------------------------------------------------------------------------------
this.startUpComplete = function () {
    if (missionVariables.GalCopBBMissions_CargoMonitor) this._monitor = JSON.parse(missionVariables.GalCopBBMissions_CargoMonitor);

    var types = ["t", "kg", "g"];

    // build a list of available commodities
    var m = null;
    if (system.mainStation) {
        m = system.mainStation.market;
    } else if (player.ship.dockedStation) {
        m = player.ship.dockedStation.market;
    } else if (system.stations.length > 0) {
        m = system.stations[0].market;
    }
    if (m) {
        var c = Object.keys(m);
        for (var i = 0; i < c.length; i++) {
            this._commodityIDList.push(c[i]);
            this._commodityType[c[i]] = types[m[c[i]].quantity_unit];
        }
    }

    // set a flag if the illegal goods tweak is installed
    if (worldScripts["illegal_goods_tweak"]) this._igtInstalled = true;

    // monkey patch smugglers so we can do things in the right order (ie remove illegal cargo to hide it before Smugglers sees it)
    if (worldScripts.Smugglers_Equipment) {
        this._smugglersInstalled = true;
        log(this.name, "monkey patching 'Smugglers' to ensure correct order of events");
        worldScripts.Smugglers_Equipment.$gcm_hold_shipWillDockWithStation = worldScripts.Smugglers_Equipment.shipWillDockWithStation;
        delete worldScripts.Smugglers_Equipment.shipWillDockWithStation;
    }
}

//-------------------------------------------------------------------------------------------------------------
this.playerWillSaveGame = function () {
    missionVariables.GalCopBBMissions_CargoMonitor = JSON.stringify(this._monitor);
}

//-------------------------------------------------------------------------------------------------------------
this.missionScreenOpportunity = function () {
    // put back any held cargo, but only if the IGT and Smugglers processes have finished
    if (this._holdCargo.length > 0) {
        if ((this._smugglersInstalled === true && worldScripts.Smugglers_Equipment._doCargoCheck === false) ||
            (this._igtInstalled === true && (!worldScripts.illegal_goods_tweak._runContrabandCheck || worldScripts.illegal_goods_tweak._runContrabandCheck === false))) {
            for (var i = 0; i < this._holdCargo.length; i++) {
                player.ship.manifest[this._holdCargo[i].commodity] += this._holdCargo[i].amount;
            }
            this._holdCargo.length = 0;
        }
    }
}

//-------------------------------------------------------------------------------------------------------------
this.shipScoopedOther = function (whom) {
    /*log(this.name, "got here - shipScoopedOther - " + whom);
    log(this.name, "_delivery = " + whom.script._delivery);
    log(this.name, "_missionID = " + whom.script._missionID);
    log(this.name, "_checkMissionID = " + whom.script._checkMissionID);
    log(this.name, "has shipWasDumped = " + whom.script.shipWasDumped);*/

    // ignore escape pods
    if (whom.hasRole("escape-capsule")) return;

    // delete any scooped cargo that is being monitored, so that we can catch the dumping process correctly
    // this may lead to (sometimes) an issue where the look of an ejected cargo pod doesn't match what was scooped
    var text = expandDescription("[gcm_cargo_collected]");
    for (var i = 0; i < this._monitor.length; i++) {
        var process = false;
        var idx = -1;
        // looking for any cargo of a particular type
        if (!this._monitor[i].specific_pods && this._monitor[i].commodity === whom.commodity && (this._monitor[i].system === 256 || this._monitor[i].system === system.ID)) process = true;
        // looking for a specific group of cargo pods
        if (process == false && this._monitor[i].specific_pods && this._monitor[i].specific_pods.indexOf(whom) >= 0) {
            process = true;
            idx = this._monitor[i].specific_pods.indexOf(whom);
        }

        if (process) {
            var reprocessed = false;
            if (this._monitor[i].thargoid === true) {
                // if this is a thargoid wreckage mission, but the cargo hasn't got the flag, just continue
                if (whom.script.hasOwnProperty("_fromThargoid") === false || whom.script._fromThargoid === false) continue;
                // otherwise...
                // to explain why it won't look like wreckage when ejected
                text += " " + expandDescription("[gcm_cargo_reprocessed]");
                reprocessed = true;
            }
            if (this._commodityType[whom.commodity] != "t") {
                // to explain why gold/platinum/gem_stones can't be ejected again
                text += " " + expandDescription("[gcm_cargo_transferred]");
                reprocessed = true;
            }
            var amt = whom.commodityAmount;
            var cmdty = whom.commodity;

            var bb = worldScripts.BulletinBoardSystem;
            if (whom.script && whom.script._missionID && whom.script._missionID > 0) {
                var item = bb.$getItem(whom.script._missionID);
                if (item) {
                    // clear out the mission ID on the pod, so we don't end up back here again.
                    // if the player tries ejecting and re-scooping the pod, nothing should now happen
                    whom.script._missionID = 0;
                    // remove this pod from the monitoring list
                    //this.$removeSpecialCargo(whom, item.ID);
                    // increment the collected quantity
                    // but only if we haven't reached the target
                    if (item.data.quantity < (item.data.targetQuantity - item.data.destroyedQuantity)) {
                        item.data.quantity += 1;
                        // update the bulletin board
                        bb.$updateBBMissionPercentage(item.ID, (item.data.quantity / (item.data.targetQuantity - item.data.destroyedQuantity)));

                        worldScripts.GalCopBB_Missions.$logMissionData(item.ID);
                        // tell the player something happened on our mission
                        player.consoleMessage(expandDescription("[goal_updated]"));

                        // special case for type 46
                        if (item.data.missionType === 46) {
                            // attach our dump script, in case this actual cargo pod is dumped at the site
                            // would only happen if the player got to the site without docking (which is possible)
                            if (whom.script.shipWasDumped && !whom.script.$gcm_hold_shipWasDumped) {
                                whom.script.$gcm_hold_shipWasDumped = whom.script.shipWasDumped;
                            }
                            whom.script.shipWasDumped = worldScripts.GalCopBB_Delivery.$gcd_cargo_shipWasDumped2;

                            if (Math.round(item.percentComplete * 10) === 10 && item.data.stage === 0) {
                                bb.$removeChartMarker(item.ID);
                                //this._cargoMonitor.length = 0;
                                // reset mission for stage 2
                                item.destination = item.data.destinationA;
                                item.destinationName = System.systemNameForID(item.destination);
                                item.additionalMarkers = [];

                                item.data.stage = 1;
                                item.data.quantity = 0;
                                bb.$updateBBMissionPercentage(item.ID, (item.data.quantity / (item.data.targetQuantity - item.data.destroyedQuantity)));
                                bb.$addManifestEntry(item.ID);
                            }
                        }
                    }
                }
            }
            // only remove/re-add cargo that needs reprocessing
            if (reprocessed == true) {
                whom.remove(true);
                player.ship.manifest[cmdty] += amt;
                // remove specific pod from array
                if (idx >= 0) this._monitor[i].specific_pods.splice(idx, 1);
            }
            this._monitor[i].containers.push(amt);
            player.consoleMessage(text, 4);
            break;
        }
    }
}

//-------------------------------------------------------------------------------------------------------------
this.shipDumpedCargo = function (cargo) {
    /*log(this.name, "got here - shipDumpedCargo - " + cargo);
    log(this.name, "_delivery = " + cargo.script._delivery);
    log(this.name, "_missionID = " + cargo.script._missionID);
    log(this.name, "_checkMissionID = " + cargo.script._checkMissionID);*/

    // we don't need to worry about kg/g here, because it's being transferred to the safe straight away
    // so we can always just remove the last item
    for (var i = 0; i < this._monitor.length; i++) {
        if (this._monitor[i].commodity === cargo.commodity && this._monitor[i].containers.length > 0) {
            this._monitor[i].containers.pop();
            // put the thargoid flag variables on the cargo entity so it can be rescooped correctly
            if (this._monitor[i].thargoid === true) cargo.script._fromThargoid = true;
            break;
        }
    }
}

//-------------------------------------------------------------------------------------------------------------
this.playerSoldCargo = function (commodity, units, price) {
    var remain = units;
    for (var i = 0; i < this._monitor.length; i++) {
        if (this._monitor[i].commodity === commodity && this._monitor[i].containers.length > 0) {
            // reduce amount recorded
            var c = this._monitor[i].containers;
            for (var j = c.length - 1; j >= 0; j--) {
                if (c[j] > 0) {
                    c[j] -= remain;
                    if (c[j] < 0) {
                        remain = Math.abs(c[j]);
                        c[j] = 0;
                    } else {
                        remain = 0;
                    }
                }
                if (remain === 0) break;
            }
            var finished = false;
            do {
                finished = false;
                if (c[c.length - 1] === 0) {
                    c.pop();
                } else {
                    finished = true;
                }
            } while (finished === false);

        }
    }
}

//-------------------------------------------------------------------------------------------------------------
this.playerBoughtNewShip = function (ship, price) {
    // check our monitoring array for any shortfalls
    for (var i = 0; i < this._monitor.length; i++) {
        if (this._monitor[i].currentAmount > 0) {
            this._monitor[i].currentAmount = 0;
            this._monitor[i].containers = [];
        }
    }
}

//-------------------------------------------------------------------------------------------------------------
this.shipWillLaunchFromStation = function (station) {
    // grab the value of the ig rescued slaves prior to IGT running
    if (missionVariables.ig_rescued_slaves && missionVariables.ig_rescued_slaves > 0) this._slavesBefore = missionVariables.ig_rescued_slaves
}

//-------------------------------------------------------------------------------------------------------------
this.shipWillDockWithStation = function (station) {
    this._docking = true;
    var gcm = worldScripts.GalCopBB_Missions;
    // check for other mission types being completed
    var list = gcm.$getListOfMissions(true, [14, 15]);
    for (var i = 0; i < list.length; i++) {
        // special cases for 14/15: because they are authorised contracts, they aren't illegal to bring in (possibly, depending on station)
        if (system.ID === list[i].source) {
            // only move cargo if the stationKeys match
            var stnkey = expandDescription("[missionType" + list[i].data.missionType + "_stationKeys]");
            if (worldScripts.BulletinBoardSystem.$checkMissionStationKey(this.name, station, stnkey) === true) {
                // hide the authorised quantity from being picked up in any illegal commodity sweep
                if (gcm.$isCommodityIllegal(system.ID, station, list[i].data.commodity) === true) {
                    var amt = list[i].data.quantity;
                    if (player.ship.manifest[list[i].data.commodity] < amt) amt = player.ship.manifest[list[i].data.commodity];
                    this._holdCargo.push({
                        commodity: list[i].data.commodity,
                        amount: amt
                    });
                    player.ship.manifest[list[i].data.commodity] -= amt;
                    player.addMessageToArrivalReport(expandDescription("[gcm_customs_wavethrough]", {
                        amount: amt,
                        commodity: displayNameForCommodity(list[i].data.commodity).toLowerCase()
                    }));
                }
            }
        }
    }

    // call our monkey-patched shipWillDockWithStation from Smugglers
    if (this._smugglersInstalled) worldScripts.Smugglers_Equipment.$gcm_hold_shipWillDockWithStation(station);
}

//-------------------------------------------------------------------------------------------------------------
this.guiScreenChanged = function (to, from) {
    var ignorelist = ["GUI_SCREEN_STATUS", "GUI_SCREEN_REPORT", "GUI_SCREEN_MISSION"];
    // run a check after docking in case any cargo was removed by some OXP docking process (eg IGT, Smugglers)
    if (this._docking === true && ignorelist.indexOf(guiScreen) === -1) {
        this._docking = false;
        // special case for type 12 (slaves) mission
        if (this._igtInstalled === true) {
            var slavesAfter = 0;
            // grab the value of the ig rescued slaves prior to IGT running
            if (missionVariables.ig_rescued_slaves && missionVariables.ig_rescued_slaves > 0) slavesAfter = missionVariables.ig_rescued_slaves;

            var gcm = worldScripts.GalCopBB_Missions;
            var bb = worldScripts.BulletinBoardSystem;
            var rescued = slavesAfter - this._slavesBefore;
            var onBoard = this.$countCargoByType("slaves");
            if (rescued > 0 && onBoard > 0) {
                // run this number through any active type 12 missions and see if it applies - but only if the slaves
                // were scooped in a destination system
                var list = gcm.$getListOfMissions(true, 12);
                if (list.length > 0) {
                    do {
                        // to get here we must have slaves that were rescued as part of a type 12 mission
                        // we have now handed over slaves to AI, so we need to update the mission
                        var pre = rescued;
                        for (var i = 0; i < list.length; i++) {
                            var rec = list[i].data;
                            var removed = 0;
                            if (rec.quantity < rec.targetQuantity) {
                                rec.quantity += rescued;
                                removed = rescued;
                                if (rec.quantity > rec.targetQuantity) {
                                    rescued = rec.targetQuantity - rec.quantity;
                                    removed = removed - rescued;
                                    rec.quantity = rec.targetQuantity;
                                } else {
                                    rescued = 0;
                                }
                                // update the mission percent complete
                                bb.$updateBBMissionPercentage(list[i].ID, (list[i].data.quantity / list[i].data.targetQuantity));
                                // however, we won't send a "Mission updated" message for this one - we'll just do it silently.

                                this.$removeCargoForMission(list[i].ID, removed);
                                onBoard -= removed;
                                break;
                            }
                        }
                        // check for something going wrong - no change to rescued means invalid/incorrect data
                        if (pre === rescued) break;
                    } while (rescued > 0 && onBoard > 0);
                }
            }
        }

        // make sure we haven't lost anything along the way (via damage or something else)
        for (var i = 0; i < this._commodityIDList.length; i++) {
            var pa = player.ship.manifest[this._commodityIDList[i]];
            var amt = this.$countCargoByType(this._commodityIDList[i]);
            var lost = pa - amt;
            if (lost < 0) {
                lost = Math.abs(lost);
                do {
                    // cycle through the monitoring list, deducting amounts until our difference is zero
                    for (var j = this._monitor.length - 1; j >= 0; j--) {
                        var item = this._monitor[j];
                        if (item.commodity === this._commodityIDList[i] && item.containers.length > 0) {
                            do {
                                item.containers[item.containers.length - 1] -= lost;
                                if (item.containers[item.containers.length - 1] < 0) {
                                    lost = Math.abs(item.containers[item.containers.length - 1])
                                    item.containers[item.containers.length - 1] = 0;
                                } else {
                                    lost = 0;
                                }
                                if (item.containers[item.containers.length - 1] === 0) {
                                    item.containers.pop();
                                }
                            } while (lost > 0 && item.containers.length > 0);
                        }
                    }
                } while (lost > 0);
            }
        }
    }
}

//-------------------------------------------------------------------------------------------------------------
// returns the number of containers for a mission
this.$countCargoForMission = function $countCargoForMission(missID) {
    var amount = 0;
    for (var i = 0; i < this._monitor.length; i++) {
        if (this._monitor[i].ID === missID) amount += this._monitor[i].containers.length;
    }
    return amount;
}

//-------------------------------------------------------------------------------------------------------------
// returns the total cargo of a particular type
this.$countCargoByType = function $countCargoByType(cmdty) {
    var amount = 0;
    for (var i = 0; i < this._monitor.length; i++) {
        if (this._monitor[i].commodity === cmdty) {
            amount += this.$sumContainers(this._monitor[i]);
        }
    }
    return amount;
}

//-------------------------------------------------------------------------------------------------------------
// remove cargo by containers, not by cargo quantity
this.$removeCargoForMission = function $removeCargoForMission(missID, amount) {
    //log(this.name, "ID " + missID + ", amount " + amount);
    var cargo = {};
    cargo.commodity = "";
    cargo.quantity = 0;
    for (var i = this._monitor.length - 1; i >= 0; i--) {
        var item = this._monitor[i];
        if (item.ID === missID && item.containers.length > 0) {
            cargo.commodity = item.commodity;
            do {
                cargo.quantity += item.containers[item.containers.length - 1];
                item.containers.pop();
                amount -= 1;
            } while (amount > 0);
        }
        if (amount == 0) break;
    }
    //log(this.name, "found " + cargo.quantity + " of " + cargo.commodity);
    return cargo;
}

//-------------------------------------------------------------------------------------------------------------
this.$sumContainers = function $sumContainers(item) {
    var amount = 0;
    for (var i = 0; i < item.containers.length; i++) {
        amount += item.containers[i];
    }
    return amount;
}