"use strict";
this.name = "FuelTweaks_FuelEconomy";
this.author = "phkb";
this.copyright = "2017 phkb";
this.description = "Controls where fuel is available";
this.licence = "CC BY-NC-SA 4.0";

/* 
check: fuel available at corp state, but rations were shown instead. plus, rations up to 6 were shown, even though limit was 4
also, buying the 5 and 6 rations did nothing (ie no fuel)
*/
this._fuelNormal = true;                    // switch to true to enable normal fuel operations galaxywide
this._fuelUseRations = false;               // default setting to indicate whether fuel rationing is in place (true) or standard fuel purchasing (false)
                                            // even if false, availability of fuel will still be controlled by settings below
this._npcUseStations = false;
this._defaultMinTL = 0;
this._fuelGovs = 0;
this._fuelEcos = 0;
this._fuelAvailability = [{eco:[0,1,2,3,4,5,6,7],gov:[0,1,2,3,4,5,6,7],tl:0,rations:false,max:1}];
                                            // an array of dictionaries
                                            // dict:    eco         array of economies. empty array means no economy check
                                            //          gov         array of government types. empty array means no gov checks
                                            //          tl          min tech level. -1 means no tl check
                                            //          rations     boolean to indicate fuel rations are in use. default = this._fuelUseRations
                                            //          max         integer between 0 and 6 being the maximum ration that can be sold
this._maxFuelRation = 1;                    // default maximum amount of ration that can be sold (between 0 and 6)
this._fuelAllegiances = 1;
this._fuelAllegiance = ["galcop"];          // allegiance of stations where fuel rations will be available. empty means any station allegiance
                                            // can include galcop,neutral,pirate,chaotic,,military,private,restricted,hunter,thargoid

this._fuelSystemOverride = [];              // array of dictionaries
                                            // dict:    systemID    id of the system having the override
                                            //          available   boolean indicating whether any fuel is for sale
                                            //          rations     boolean indicating whether rations are in use
                                            //          max         integer between 0 and 4 indicating the maximum ration that can be sold
this._defaultRSMinTL = 0;
this._refuelGovs = 0;
this._refuelEcos = 0;
this._refuelStationAvailability = [{eco:[0,1,2,3,4,5,6,7],gov:[0,1,2,3,4,5,6,7],tl:0,factor:0.15}];
                                           // an array of dictionaries
                                            // dict:    eco         array of economies. empty array means no economy check
                                            //          gov         array of government types. empty array means no gov checks
                                            //          tl          min tech level. -1 means no tl check
                                            //          factor      price factor for these systems
this._refuelStationSystemOverride = [];     // array of dictionaries 
                                            // dict:    systemID    system id of system in question
                                            //          available   boolean indicating whether refuel station is available
                                            //          factor      price factor to apply to fuel here
this._refuelStationCostFactor = 0.15;       // default price factor applied to fuel sold through refuelling stations

this._emergencyFuel = [];                   // array of stations names where player has purchased a fuel ration
this._saveSettings = false;                 // set to true to save all settings in save game file (which means values set above will be overridden)
                                            // this would probably only enabled if the OXP reached release stage and was linked into other OXP's
                                            // eg Diplomacy

this._trueValues = ["yes", "1", 1, "true", true];
this._allegiances = ["galcop", "neutral", "pirate", "chaotic", "military", "private", "restricted", "hunter", "thargoid"];
this._ecos = [];
this._govs = [];

// configuration settings for use in Lib_Config
this._feConfig = {
	Name: this.name,
	Alias: "Fuel Tweaks",
	Display: "General Settings",
	Alive: "_feConfig",
	Notify: "$update",
	Bool: {
		B0: { Name: "_saveSettings", Def: false, Desc: "Apply changes" },
		B1: { Name: "_fuelNormal", Def: true, Desc: "Normal fuel operation" },
        B2: { Name: "_fuelUseRations", Def: false, Desc: "Fuel rationing" },
        B3: { Name: "_npcUseStations", Def: false, Desc: "NPCs use stations" },
		Info: "0: Apply any fuel availability changes when saving the game; 1: Apply normal (ie standard) fuel operations in all systems - all settings will be ignored; 2: Use fuel rationing when custom options are in operation; 3: NPC's will be allowed to use refuelling stations."
	},
	SInt: {
		S0: { Name: "_maxFuelRation", Def: 1, Min: 0, Max: 6, Desc: "Max fuel ration" },
		S1: { Name: "_defaultMinTL", Def: 0, Min: 0, Max: 15, Desc: "Default min TL" },
		S2: { Name: "_refuelStationCostFactor", Def: 0.15, Min: 0.01, Max: 50, Float:true, Desc: "Refuel stn cost factor" },
		S3: { Name: "_defaultRSMinTL", Def: 0, Min: 0, Max: 15, Desc: "Refuel stn min TL" },
		Info: "0: Maximum fuel ration available (when rationing in place); 1: Minimum TL for purchasing fuel, where 0 means everywhere; 2: Default cost factor applied to any fuel stations; 3: Minimum TL where Refuel Stations are available, where 0 means everywhere."
	},
};

this._faAllegianceConfig = {
    Name: this.name,
    Display: "Fuel Availability: Allegiance",
    Alias: "Fuel Tweaks",
    Alive: "_faAllegianceConfig",
    Notify: "$update",
    EInt: {
        E0: {
            Name: "_fuelAllegiances",
            Def: 0,
            Min: 0,
            Max: 511,
            Desc: this._allegiances
        },
        Info: "Station allegiances where fuel will be available."
    },
};

this._faGovernmentConfig = {
    Name: this.name,
    Display: "Fuel Availability: Governments",
    Alias: "Fuel Tweaks",
    Alive: "_faGovernmentConfig",
    Notify: "$update",
    EInt: {
        E0: {
            Name: "_fuelGovs",
            Def: 0,
            Min: 0,
            Max: 255,
            Desc: this._govs
        },
        Info: "Default Governments where fuel will be available."
    },
};

this._faEconomyConfig = {
    Name: this.name,
    Display: "Fuel Availability: Economies",
    Alias: "Fuel Tweaks",
    Alive: "_faEconomyConfig",
    Notify: "$update",
    EInt: {
        E0: {
            Name: "_fuelEcos",
            Def: 0,
            Min: 0,
            Max: 255,
            Desc: this._ecos
        },
        Info: "Default Economic types where fuel will be available."
    },
};

this._rsGovernmentConfig = {
    Name: this.name,
    Display: "Fuel Stations: Governments",
    Alias: "Fuel Tweaks",
    Alive: "_rsGovernmentConfig",
    Notify: "$update",
    EInt: {
        E0: {
            Name: "_refuelGovs",
            Def: 0,
            Min: 0,
            Max: 255,
            Desc: this._govs
        },
        Info: "Default Governments where fuel stations will be available."
    },
};

this._rsEconomyConfig = {
    Name: this.name,
    Display: "Fuel Stations: Economies",
    Alias: "Fuel Tweaks",
    Alive: "_rsEconomyConfig",
    Notify: "$update",
    EInt: {
        E0: {
            Name: "_refuelEcos",
            Def: 0,
            Min: 0,
            Max: 255,
            Desc: this._ecos
        },
        Info: "Default Economic types where fuel stations will be available."
    },
};

//-------------------------------------------------------------------------------------------------------------
this.startUp = function() {
	for (var i = 0; i < 8 ; i++) this._govs.push(String.fromCharCode(i) + " " + this.$governmentDescription(i));
	for (i = 0; i < 8 ; i++) this._ecos.push(String.fromCharCode(23 - i) + " " + this.$economyDescription(i));

    /*
    // sample settings
    this._fuelNormal = false; // turn off normal fuel operations
    this._fuelUseRations = true; // turn on rationing
    // add some controls over fuel availability
    this.$addFuelAvailability([4,5,6,7], [0], 5);
    this.$addFuelAvailability([4,5,6,7], [1], 3);
    this.$addFuelAvailability([3,4,5,6,7], [2,3,4,5,6,7], 0, false);
    
    // add some controls over where refulling stations are present
    this.$addRefuelStationAvailability([4,5], [0], 8);
    this.$addRefuelStationAvailability([4,5], [1], 6);
    this.$addRefuelStationAvailability([4,5,6], [2], 3);
    this.$addRefuelStationAvailability([3,4,5,6], [2,3,4,5,6,7], 2);
    */

    if (missionVariables.FuelTweaks_EmergencyFuel) {
        this._emergencyFuel = JSON.parse(missionVariables.FuelTweaks_EmergencyFuel);
        delete missionVariables.FuelTweaks_EmergencyFuel;
    }
    // only try to load settings if we're actually saving them
    if (missionVariables.FuelTweaks_SaveSettings) {
        this._saveSettings = (this._trueValues.indexOf(missionVariables.FuelTweaks_SaveSettings) >= 0 ? true : false);
    }
    if (this._saveSettings === true) {
        if (missionVariables.FuelTweaks_FuelAvailability) {
            this._fuelAvailability = JSON.parse(missionVariables.FuelTweaks_FuelAvailability);
            //delete missionVariables.FuelTweaks_FuelAvailability;
        }
        if (missionVariables.FuelTweaks_FuelMinTL) {
            this._defaultMinTL = parseInt(missionVariables.FuelTweaks_FuelMinTL);
        }
        if (missionVariables.FuelTweaks_MaxFuelRation) {
            this._maxFuelRation = parseInt(missionVariables.FuelTweaks_MaxFuelRation);
        }
        if (missionVariables.FuelTweaks_Allegiance) {
            this._fuelAllegiance = JSON.parse(missionVariables.FuelTweaks_Allegiance);
            //delete missionVariables.FuelTweaks_Allegiance;
        }
        if (missionVariables.FuelTweaks_FuelSystemOverride) {
            this._fuelSystemOverride = JSON.parse(missionVariables.FuelTweaks_FuelSystemOverride);
            //delete missionVariables.FuelTweaks_FuelSystemOverride;
        }
        if (missionVariables.FuelTweaks_RefuelStationAvailability) {
            this._refuelStationAvailability = JSON.parse(missionVariables.FuelTweaks_RefuelStationAvailability);
            //delete missionVariables.FuelTweaks_RefuelEconomy;
        }
        if (missionVariables.FuelTweaks_RefuelStationSystemOverride) {
            this._refuelStationSystemOverride = JSON.parse(missionVariables.FuelTweaks_RefuelStationSystemOverride);
            //delete missionVariables.FuelTweaks_RefuelStationSystemOverride;
        }
        if (missionVariables.FuelTweaks_RefuelStationCostFactor) {
            this._refuelStationCostFactor = parseFloat(missionVariables.FuelTweaks_RefuelStationCostFactor);
        }
        if (missionVariables.FuelTweaks_RefuelStationMinTL) {
            this._defaultRSMinTL = parseInt(missionVariables.FuelTweaks_RefuelStationMinTL);
        }
        if (missionVariables.FuelTweaks_Normal) {
            this._fuelNormal = (this._trueValues.indexOf(missionVariables.FuelTweaks_Normal) >= 0 ? true : false);
        }
        if (missionVariables.FuelTweaks_UseRations) {
            this._fuelUseRations = (this._trueValues.indexOf(missionVariables.FuelTweaks_UseRations) >= 0 ? true : false);
        }
        if (missionVariables.FuelTweaks_NPCUseStations) {
            this._npcUseStations = (this._trueValues.indexOf(missionVariables.FuelTweaks_NPCUseStations) >= 0 ? true : false);
        }
    }
    this.$updateIsisEquipment();
    delete this.startUp;
}

//-------------------------------------------------------------------------------------------------------------
this.startUpComplete = function() {
    this.$updateBitwiseValues();

    // register our settings, if Lib_Config is present
    if (worldScripts.Lib_Config) {
        worldScripts.Lib_Config._registerSet(this._feConfig);
        worldScripts.Lib_Config._registerSet(this._faAllegianceConfig);
        worldScripts.Lib_Config._registerSet(this._faGovernmentConfig);
        worldScripts.Lib_Config._registerSet(this._faEconomyConfig);
        worldScripts.Lib_Config._registerSet(this._rsGovernmentConfig);
        worldScripts.Lib_Config._registerSet(this._rsEconomyConfig);
    }

    if (worldScripts["oolite-system-data-config"] && worldScripts["CommpressedF7Layout"]) {
        worldScripts["oolite-system-data-config"].addChangeCallback(this.name, "$addInfoToSystemDataScreen");
        delete this.guiScreenWillChange;
        delete this.infoSystemWillChange;
    }
    if (worldScripts.GalCopAdminServices) {
        var ga = worldScripts.GalCopAdminServices;
        ga._purchase_ignore_equip.push("EQ_PIRATE_FUEL");
        ga._purchase_ignore_equip.push("EQ_FUEL_ALT1");
        ga._purchase_ignore_equip.push("EQ_FUEL_ALT2");
        ga._purchase_ignore_equip.push("EQ_FUEL_ALT3");
        ga._purchase_ignore_equip.push("EQ_FUEL_ALT4");
        ga._purchase_ignore_equip.push("EQ_FUEL_ALT5");
        ga._purchase_ignore_equip.push("EQ_FUEL_ALT6");
    }
}

//-------------------------------------------------------------------------------------------------------------
this.playerWillSaveGame = function() {
    if (this._emergencyFuel.length > 0) {
        missionVariables.FuelTweaks_EmergencyFuel = JSON.stringify(this._emergencyFuel);
    } else {
        delete missionVariables.FuelTweaks_EmergencyFuel;
    }
    // only save settings if we're allowed
    missionVariables.FuelTweaks_SaveSettings = this._saveSettings;
    if (this._saveSettings === true) {
        missionVariables.FuelTweaks_FuelAvailability = JSON.stringify(this._fuelAvailability);
        missionVariables.FuelTweaks_FuelMinTL = this._defaultMinTL;
        missionVariables.FuelTweaks_MaxFuelRation = this._maxFuelRation;
        missionVariables.FuelTweaks_Allegiance = JSON.stringify(this._fuelAllegiance);
        missionVariables.FuelTweaks_FuelSystemOverride = JSON.stringify(this._fuelSystemOverride);
        missionVariables.FuelTweaks_RefuelStationAvailability = JSON.stringify(this._refuelStationAvailability);
        missionVariables.FuelTweaks_RefuelStationSystemOverride = JSON.stringify(this._refuelStationSystemOverride);
        missionVariables.FuelTweaks_RefuelStationCostFactor = this._refuelStationCostFactor;
        missionVariables.FuelTweaks_RefuelStationMinTL = this._defaultRSMinTL;
        missionVariables.FuelTweaks_Normal = this._fuelNormal;
        missionVariables.FuelTweaks_UseRations = this._fuelUseRations;
        missionVariables.FuelTweaks_NPCUseStations = this._npcUseStations;
    }
}

//-------------------------------------------------------------------------------------------------------------
this.playerBoughtEquipment = function(equipmentKey) {
    var list = ["EQ_FUEL_ALT1","EQ_FUEL_ALT2","EQ_FUEL_ALT3","EQ_FUEL_ALT4"]    
	if (list.indexOf(equipmentKey) >= 0) {
        var info = EquipmentInfo.infoForKey(equipmentKey);
        var amt = parseInt(info.scriptInfo["fuel_amount"]);
		player.ship.fuel += amt;
        player.ship.removeEquipment(equipmentKey);
        this._emergencyFuel.push(player.ship.dockedStation.displayName); 
    }
    if (equipmentKey === "EQ_PIRATE_FUEL") {
        player.ship.fuel = 7;
        player.ship.removeEquipment(equipmentKey);
    }
}

//-------------------------------------------------------------------------------------------------------------
this.shipWillEnterWitchspace = function(cause, destination) {
    this._emergencyFuel = [];
    if (cause === "galactic jump") {
        // reset fuel system parameters
        if (this._fuelAvailability.length > 1) {
            for (var i = 1; i < this._fuelAvailability.length; i++) this._fuelAvailability.splice(1, 1);
        }
        this._fuelSystemOverride = [];
        if (this._refuelStationAvailability.length > 1) {
            for (var i = 1; i < this._refuelStationAvailability.length; i++) this._refuelStationAvailability.splice(1, 1);
        }
        this._refuelStationSystemOverride = [];
    }
}

//-------------------------------------------------------------------------------------------------------------
this.shipExitedWitchspace = function() {
    if (this._fuelNormal === false && system.ID >= 0) {
        var msg = "";
        var avail = this.$isFuelAvailable(system.ID);
        var stn = (avail === true ? this.$isRefuelStationAvailable(system.ID) : false);
        var rations = (avail === true ? this.$fuelRationsInUse(system.ID) : false);
        if (avail === false) msg = expandDescription("[fuelTweaks_notAvailable]");
        if (avail === true) {
            if (rations === true && stn === false) msg = expandDescription("[fuelTweaks_fuelRationsAvailable]");
        }
        if (msg !== "") {
            var targets = system.shipsWithRole("buoy-witchpoint");
            if (targets.length > 0) {
                for (var i = 0; i < targets.length; i++) {
                    targets[i].commsMessage(msg, player.ship);
                }
            }
        }
    }
}

//-------------------------------------------------------------------------------------------------------------
this.guiScreenWillChange = function(to, from) {
    if (to === "GUI_SCREEN_SYSTEM_DATA") {
		this.$addInfoToSystemDataScreen();
    }
}

//-------------------------------------------------------------------------------------------------------------
this.infoSystemWillChange = function(to, from) {
	if (guiScreen === "GUI_SCREEN_SYSTEM_DATA") {
		this.$addInfoToSystemDataScreen();
	}
}

//-------------------------------------------------------------------------------------------------------------
// adds an availability register to the list
this.$addFuelAvailability = function(ecoList, govList, tl, useRations, maxRation) {
    if (ecoList && Array.isArray(ecoList) === false) {
        throw "Invalid settings - ecoList must be an array";
    }
    if (!ecoList) {
        ecoList = [0, 1, 2, 3, 4, 5, 6, 7];
    } else {
        for (var i = 0; i < ecoList.length; i++) {
            if (ecoList[i] < 0 || ecoList[i] > 7) {
                throw "Invalid settings - ecoList data elements must be between 0 and 7";
            }
        }
    }
    if (govList && Array.isArray(govList) === false) {
        throw "Invalid settings - govList must be an array";
    }
    if (!govList) {
        govList = [0, 1, 2, 3, 4, 5, 6, 7];
    } else {
        for (var i = 0; i < govList.length; i++) {
            if (govList[i] < 0 || govList[i] > 7) {
                throw "Invalid settings - govList data elements must be between 0 and 7";
            }
        }
    }
    if (tl < -1 || tl > 15) {
        throw "Invalid settings - tl must be between -1 and 15";
    }
    // default if not passed
    if (useRations == undefined) useRations = this._fuelUseRations;
    if (maxRation == undefined) {
        maxRation = this._maxFuelRation;
    } else {
        if (isNaN(maxRation) || maxRation < 0 || maxRation > 6) {
            throw "Invalid settings - maxRation must be an integer between 0 and 6";
        }
    }

    var test = JSON.stringify({eco:ecoList, gov:govList, tl:tl, rations:useRations, max:maxRation});
    var found = false;
    for (var i = 1; i < this._fuelAvailability.length; i++) {
        if (JSON.stringify(this._fuelAvailability[i]) === test) {found = true; break;}
    }
    if (found === false) this._fuelAvailability.push({eco:ecoList, gov:govList, tl:tl, rations:useRations, max:maxRation});
}

//-------------------------------------------------------------------------------------------------------------
// removes a fuel availability register from the list
this.$removeFuelAvailability = function(ecoList, govList, tl, useRations, maxRation) {
    if (ecoList && Array.isArray(ecoList) === false) {
        throw "Invalid settings - ecoList must be an array";
    }
    if (!ecoList) {
        ecoList = [0, 1, 2, 3, 4, 5, 6, 7];
    } else {
        for (var i = 0; i < ecoList.length; i++) {
            if (ecoList[i] < 0 || ecoList[i] > 7) {
                throw "Invalid settings - ecoList data elements must be between 0 and 7";
            }
        }
    }
    if (govList && Array.isArray(govList) === false) {
        throw "Invalid settings - govList must be an array";
    }
    if (!govList) {
        govList = [0, 1, 2, 3, 4, 5, 6, 7];
    } else {
        for (var i = 0; i < govList.length; i++) {
            if (govList[i] < 0 || govList[i] > 7) {
                throw "Invalid settings - govList data elements must be between 0 and 7";
            }
        }
    }
    if (tl < -1 || tl > 15) {
        throw "Invalid settings - tl must be between -1 and 15";
    }
    // default if not passed
    if (useRations == undefined) useRations = this._fuelUseRations;
    if (maxRation == undefined) {
        maxRation = this._maxFuelRation;
    } else {
        if (isNaN(maxRation) || maxRation < 0 || maxRation > 6) {
            throw "Invalid settings - maxRation must be an integer between 0 and 6";
        }
    }

    // don't remove the default settings
    if (this._fuelAvailability.length > 1) {
        var test = JSON.stringify({eco:ecoList, gov:govList, tl:tl, rations:useRations, max:maxRation});
        for (var i = 1; i < this._fuelAvailability.length; i++) {
            if (JSON.stringify(this._fuelAvailability[i]) === test) {
                this._fuelAvailability.splice(i, 1);
                break;
            }
        }
    }
}

//-------------------------------------------------------------------------------------------------------------
this.$addInfoToSystemDataScreen = function() {
	var sysID = player.ship.targetSystem;
	if (player.ship.hasOwnProperty("infoSystem")) sysID = player.ship.infoSystem;
    // build message
    var msg = "";
    var avail = this.$isFuelAvailable(sysID);
    var stn = (avail === true ? this.$isRefuelStationAvailable(sysID) : false);
    var rations = (avail === true ? this.$fuelRationsInUse(sysID) : false);
    if (avail === false) msg = expandDescription("[fuelTweaks_notAvailable]");
    if (avail === true) {
        if (rations === true && stn === false) msg = expandDescription("[fuelTweaks_fuelRationsAvailable]");
        if (stn === true) msg = expandDescription("[fuelTweaks_fuelStationAvailable]");
    }
    // add message to top part of screen, if possible
    var added = false;
    if (worldScripts["oolite-system-data-config"] && worldScripts["CompressedF7Layout"]) {
        var ln = 0;
        var dc = worldScripts["oolite-system-data-config"];
        for (var i = 16; i >= 1; i--) {
            if (ln === 0 && dc.systemDataLineText(i) != "") {
                ln = i + 1;
            }
            if (dc.systemDataLineOwner(i) === this.name) {
                ln = i;
                break;
            }
        }
        if (ln <= 16 && ln > 0) {
            added = true;
            dc.setSystemDataLine(ln, msg, (msg != "" ? this.name : ""));
        }
    } 
    // if none of the above worked, then just add the message to the bottom part of the screen
    if (added === false) mission.addMessageText(msg);
}

//-------------------------------------------------------------------------------------------------------------
// adds a fuel override to a specific system
this.$addFuelSystemOverride = function(sysID, avail, useRations, maxRation) {
    // default if not passed
    if (!useRations) useRations = this._fuelUseRations;
    if (!maxRation) {
        maxRation = this._maxFuelRation;
    } else {
        if (isNaN(maxRation) || maxRation < 0 || maxRation > 6) {
            throw "Invalid settings - maxRation must be an integer between 0 and 6";
        }
    }
    
    var found = false;
    for (var i = 0; i < this._fuelSystemOverride.length; i++) {
        if (this._fuelSystemOverride[i].systemID === sysID) {
            this._fuelSystemOverride[i] = {systemID:sysID, available:avail, rations:useRations, max:maxRation};
            found = true;
            break;
        }
    }
    if (found === false) {
        this._fuelSystemOverride.push({systemID:sysID, available:avail, rations:useRations, max:maxRation});
    }
}

//-------------------------------------------------------------------------------------------------------------
// removes a system specific fuel override
this.$removeFuelSystemOverride = function(sysID) {
    if (isNaN(sysID) || sysID < 0 || sysID > 255) {
        throw "Invalid setting - system ID must be an integer between 0 and 255";
    }
    for (var i = 0; i < this._fuelSystemOverride.length; i++) {
        if (this._fuelSystemOverride[i].systemID === sysID) {
            this._fuelSystemOverride.splice(i, 1)
            break;
        }
    }
}

//-------------------------------------------------------------------------------------------------------------
// Sets the default maximum amount of fuel that can be purchased through a non-refuelling stations
// Zero (0) means no ration available.
this.$setMaxFuelRation = function(maxFuel) {
	if (isNaN(maxFuel) === true || maxFuel < 0 || maxFuel > 6) {
		throw "Invalid setting - max fuel ration must be an integer between 0 and 6";
	}
    this._maxFuelRation = maxFuel;
}

//-------------------------------------------------------------------------------------------------------------
// Sets an array of allegiance types to control what stations can sell fuel.
// empty array means all stations.
this.$setFuelAllegiance = function(alleg) {
	if (Array.isArray(alleg) === false) {
		throw "Invalid setting - parameter must be an array of strings";
	}
    var poss = this._allegiances;
	for (var i = 0; i < alleg.length; i++) {
		if (poss.indexOf(alleg[i]) === -1) {
			throw "Invalid allegiance value - " + alleg[i] + " must be one of these: " + this._allegiances.join();
		}
	}
	this._fuelAllegiance = alleg;
}

//-------------------------------------------------------------------------------------------------------------
// adds a refuel station availability register to the list
this.$addRefuelStationAvailability = function(ecoList, govList, tl, factor) {
    if (ecoList && Array.isArray(ecoList) === false) {
        throw "Invalid settings - ecoList must be an array";
    }
    if (!ecoList) {
        ecoList = [0, 1, 2, 3, 4, 5, 6, 7];
    } else {
        for (var i = 0; i < ecoList.length; i++) {
            if (ecoList[i] < 0 || ecoList[i] > 7) {
                throw "Invalid settings - ecoList data elements must be between 0 and 7";
            }
        }
    }
    if (govList && Array.isArray(govList) === false) {
        throw "Invalid settings - govList must be an array";
    }
    if (!govList) {
        govList = [0, 1, 2, 3, 4, 5, 6, 7];
    } else {
        for (var i = 0; i < govList.length; i++) {
            if (govList[i] < 0 || govList[i] > 7) {
                throw "Invalid settings - govList data elements must be between 0 and 7";
            }
        }
    }
    if (tl < -1 || tl > 15) {
        throw "Invalid settings - tl must be between -1 and 15";
    }
    if (factor && isNaN(factor) === true) {
        throw "Invalid settings - factor must be a valid number greater than 0";
    }
    var test = JSON.stringify({eco:ecoList, gov:govList, tl:tl});
    var found = false;
    for (var i = 0; i < this._refuelStationAvailability.length; i++) {
        if (JSON.stringify(this._refuelStationAvailability[i]) === test) {found = true; break;}
    }
    if (!factor) factor = this._refuelStationCostFactor;
    if (found === false) this._refuelStationAvailability.push({eco:ecoList, gov:govList, tl:tl, factor:factor});
}

//-------------------------------------------------------------------------------------------------------------
// removes a refuel station availability register from the list
this.$removeRefuelStationAvailability = function(ecoList, govList, tl) {
        if (Array.isArray(ecoList) === false) {
        throw "Invalid settings - ecoList must be an array";
    }
    for (var i = 0; i < ecoList.length; i++) {
        if (ecoList[i] < 0 || ecoList[i] > 7) {
            throw "Invalid settings - ecoList data elements must be between 0 and 7";
        }
    }
    if (Array.isArray(govList) === false) {
        throw "Invalid settigns - govList must be an array";
    }
    for (var i = 0; i < govList.length; i++) {
        if (govList[i] < 0 || govList[i] > 7) {
            throw "Invalid settings - govList data elements must be between 0 and 7";
        }
    }
    if (tl < -1 || tl > 15) {
        throw "Invalid settings - tl must be between -1 and 15";
    }

    if (this._refuelStationAvailability.length > 1) {
        var test = JSON.stringify({eco:ecoList, gov:govList, tl:tl});
        for (var i = 0; i < this._refuelStationAvailability.length; i++) {
            if (JSON.stringify(this._refuelStationAvailability[i]) === test) {
                this._refuelStationAvailability.splice(i, 1); 
                break;
            }
        }
    }
}

//-------------------------------------------------------------------------------------------------------------
// Adds a system ID to a list of systems that will be forced to have a refuelling station.
this.$addRefuelStationSystemOverride = function(sysID, avail, costFactor) {
    if (isNaN(sysID) || sysID < 0 || sysID > 255) {
        throw "Invalid setting - system ID must be an integer between 0 and 255";
    }
    if (isNaN(costFactor) || costFactor <= 0) {
        throw "Invalid setting - cost factor must be greater than 0"
    }
    var found = false;
    for (var i = 0; i < this._refuelStationSystemOverride.length; i++) {
        if (this._refuelStationSystemOverride[i].systemID === sysID) {
            found = true;
            this._refuelStationSystemOverride[i] = {systemID:sysID, available:avail, factor:costFactor};
            break;
        }
    }
    if (found === false) {
        this._refuelStationSystemOverride.push({systemID:sysID, available:avail, factor:costFactor});
    }
}

//-------------------------------------------------------------------------------------------------------------
// Removes a systemID from the list of systems that will always have a refuelling station.
this.$removeRefuelStationSystemOverride = function(sysID) {
    if (isNaN(sysID) || sysID < 0 || sysID > 255) {
        throw "Invalid setting - system ID must be an integer between 0 and 255";
    }
    for (var i = 0; i < this._refuelStationSystemOverride.length; i++) {
        if (this._refuelStationSystemOverride[i].systemID === sysID) {
            this._refuelStationSystemOverride.splice(i, 1);
            break;
        }
    }
}

//-------------------------------------------------------------------------------------------------------------
// sets the default refuelling station cost factor 
this.$setRefuelStationCostFactor = function(cf) {
    if (isNaN(cf) || cf <= 0) {
        throw "Invalid setting - cost factor must be greater than 0";
    }
    this._refuelStationCostFactor = cf;
}

//-------------------------------------------------------------------------------------------------------------
// returns true if fuel rations will be available in a particular system. otherwise false.
this.$isFuelAvailable = function(sysID) {
    if (this._fuelNormal === true) return true;
	if (!sysID) sysID = system.ID;
	var sys = System.infoForSystem(galaxyNumber, sysID);
    // check if we have an override
    var ovr = this.$checkFuelSystemOverride(sysID);
    if (ovr) return ovr.available;
    return this.$checkFuelAvailability(sys.economy, sys.government, sys.techlevel);
}

//-------------------------------------------------------------------------------------------------------------
// returns true if fuel rationing is in use in a system, otherwise false
this.$fuelRationsInUse = function(sysID) {
    if (this._fuelNormal === true) return false;
	if (!sysID) sysID = system.ID;
	var sys = System.infoForSystem(galaxyNumber, sysID);
    // look for an override first
    var ovr = this.$checkFuelSystemOverride(sysID);
    if (ovr) return ovr.rations;
    // check if a refuel station is available here
    if (this.$isRefuelStationAvailable(sysID) === true) return false;
    var inuse = this.$checkFuelRationsInUse(sys.economy, sys.government, sys.techlevel);
    // make sure players who can't scoop fuel can still get a fuel ration
    if (player.ship.hasEquipmentProviding("EQ_FUEL_SCOOPS") === false) {
        if (inuse === false && this.$isFuelAvailable(sysID) === false && this.$isRefuelStationAvailable(sysID) === false) {
            inuse = true;
        }
    }
    return inuse;
}

//-------------------------------------------------------------------------------------------------------------
// checks all fuel availability settings to see if we hit a true condition
this.$checkFuelAvailability = function(ecoID, govID, tl) {
    for (var i = 0; i < this._fuelAvailability.length; i++) {
        var checkCount = 0;
        var itm = this._fuelAvailability[i];
        if (itm.eco.indexOf(ecoID) >= 0) checkCount += 1; // itm.eco.length === 0 || 
        if (itm.gov.indexOf(govID) >= 0) checkCount += 1; // itm.gov.length === 0 || 
        if (itm.tl === -1 || tl >= itm.tl) checkCount += 1;
 
        if (checkCount === 3) {
            return true;
        }
    }
    return false;
}

//-------------------------------------------------------------------------------------------------------------
// checks all fuel availability settings to see whether fuel rations are in use
this.$checkFuelRationsInUse = function(ecoID, govID, tl) {
    var inuse = this._fuelUseRations;
    var itm = this.$getAvailabilityItem(ecoID, govID, tl);
    if (itm != null) inuse = itm.useRation;
    return inuse;
}

//-------------------------------------------------------------------------------------------------------------
// returns the maximum possible fuel ration available for this system
this.$getMaxFuelRation = function(sysID) {
    var max = this._maxFuelRation;
	if (!sysID) sysID = system.ID;
	var sys = System.infoForSystem(galaxyNumber, sysID);
    // look for an override
    var ovr = this.$checkFuelSystemOverride(sysID);
    if (ovr) return ovr.max;
    // look for a general item
    var itm = this.$getAvailabilityItem(sys.economy, sys.government, sys.techlevel);
    if (itm != null) max = itm.maxRation;
    // make sure players who can't scoop fuel can still get a decent fuel ration
    if (player.ship.hasEquipmentProviding("EQ_FUEL_SCOOPS") === false) {
        if (this.$isFuelAvailable(sysID) === false && this.$isRefuelStationAvailable(sysID) === false) {
            max = 4;
        }
    }
    return max;
}

//-------------------------------------------------------------------------------------------------------------
// checks the availability register for a match on eco, gov and tl
this.$getAvailabilityItem = function(ecoID, govID, tl) {
    var item = null;
    for (var i = 0; i < this._fuelAvailability.length; i++) {
        var itm = this._fuelAvailability[i];
        if (((itm.eco.length > 0 && itm.eco.indexOf(ecoID) >= 0)) && //  || itm.eco.length === 0
            ((itm.gov.length > 0 && itm.gov.indexOf(govID) >= 0)) && //  || itm.gov.length === 0
            (itm.tl === -1 || tl >= itm.tl)) {item = itm; break;}
    }
    return item;
}

//-------------------------------------------------------------------------------------------------------------
// checks for a fuel ration system override, returns item or null
this.$checkFuelSystemOverride = function(sysID) {
    if (this._fuelSystemOverride.length > 0) {
        for (var i = 0; i < this._fuelSystemOverride.length; i++) {
            if (this._fuelSystemOverride[i].systemID === sysID) return this._fuelSystemOverride[i];
        }
    }
    return null;
}

//-------------------------------------------------------------------------------------------------------------
// returns true if fuel rations will be available from stations in a particular system. otherwise false
this.$isRefuelStationAvailable = function(sysID) {
    // if the fuel is set to normal operations, don't spawn any refuelling stations - but maybe some discussion is worthwhile here.
    if (this._fuelNormal === true) return false;
	if (!sysID) sysID = system.ID;
	var sys = System.infoForSystem(galaxyNumber, sysID);
    // is fuel allowed to be sold in this system? run relevant fuel availability checks first
    var fuelAvail = true;
    var ovr = this.$checkFuelSystemOverride(sysID);
    // check if we have a "deny" override
    if (ovr) {
        fuelAvail = ovr.available;
    } else {
        // otherwise check for general availability
        fuelAvail = this.$checkFuelAvailability(sys.economy, sys.government, sys.techlevel);
    }
    // check the economy of the current system
    var avail = true;
    // if fuel is available we can now check for refuel stations
    if (fuelAvail === true) {
        // check if we have an override for this system
        ovr = this.$checkRefuelStationSystemOverride(sysID);
        if (ovr) return ovr.available;
        avail = this.$checkRefuelStationAvailability(sys.economy, sys.government, sys.techlevel);
    } else {
        // if no fuel is available, no refuelling stations are available either
        avail = false;
    }
    return avail;
}

//-------------------------------------------------------------------------------------------------------------
// checks all refuel station availability settings to see if we hit a true condition
this.$checkRefuelStationAvailability = function(ecoID, govID, tl) {
    for (var i = 0; i < this._refuelStationAvailability.length; i++) {
        var checkCount = 0;
        var itm = this._refuelStationAvailability[i];
        if (itm.eco.indexOf(ecoID) >= 0) checkCount += 1; // itm.eco.length === 0 || 
        if (itm.gov.indexOf(govID) >= 0) checkCount += 1; // itm.gov.length === 0 || 
        if (itm.tl === -1 || tl >= itm.tl) checkCount += 1;

        // if we completely matched on this item, we're good to go
        if (checkCount === 3) {
            return true;
        }
    }
    return false;
}

//-------------------------------------------------------------------------------------------------------------
// checks the fuel station availability register for a match on eco, gov and tl
this.$getRefuelStationAvailabilityItem = function(ecoID, govID, tl) {
    var item = null;
    for (var i = 0; i < this._refuelStationAvailability.length; i++) {
        var itm = this._refuelStationAvailability[i];
        if (((itm.eco.length > 0 && itm.eco.indexOf(ecoID) >= 0)) && // || itm.eco.length === 0
            ((itm.gov.length > 0 && itm.gov.indexOf(govID) >= 0)) && // || itm.gov.length === 0
            ((itm.tl >= 0 && tl >= itm.tl) || itm.tl === -1)) {item = itm; break;}
    }
    return item;
}

//-------------------------------------------------------------------------------------------------------------
// checks for a fuel ration system override, returns item or null
this.$checkRefuelStationSystemOverride = function(sysID) {
    if (this._refuelStationSystemOverride.length > 0) {
        for (var i = 0; i < this._refuelStationSystemOverride.length; i++) {
            if (this._refuelStationSystemOverride[i].systemID === sysID) return this._refuelStationSystemOverride[i];
        }
    }
    return null;
}

//-------------------------------------------------------------------------------------------------------------
// returns the refuel station cost factor for this system
this.$getRefuelStationCostFactor = function(sysID) {
	if (!sysID) sysID = system.ID;
	var sys = System.infoForSystem(galaxyNumber, sysID);
    // check for an override
    var ovr = this.$checkRefuelStationSystemOverride(sysID);
    if (ovr) return ovr.factor;
    // start with the default
    var result = this._refuelStationCostFactor;
    // see if there's something in the register
    var item = this.$getRefuelStationAvailabilityItem(sys.economy, sys.government, sys.techlevel);
    if (item != null) {
        result = parseFloat(item.factor);
    }
    return result;
}

//-------------------------------------------------------------------------------------------------------------
this.$update = function() {
    this._fuelAllegiance = [];
    var set = 1;
    for (var i = 0; i < this._allegiances.length; i++) {
        if (this._fuelAllegiances & set) {
            this._fuelAllegiance.push(this._allegiances[i])
        }
        set *= 2;
    }

    // fuel availability
    var fa = {
        eco:[],
        gov:[],
        tl:(this._defaultMinTL - 1),
        useRation:this._fuelUseRations,
        maxRation:this._maxFuelRation
    };
    var set = 1;
    for (var i = 0; i < this._govs.length; i++) {
        if (this._fuelGovs & set) {
            fa.gov.push(i)
        }
        set *= 2;
    }
    var set = 1;
    for (var i = 0; i < this._ecos.length; i++) {
        if (this._fuelEcos & set) {
            fa.eco.push(i)
        }
        set *= 2;
    }
    
    if (this._fuelAvailability.length >= 1) {
        this._fuelAvailability[0] = fa;
    } else {
        this._fuelAvailability.push(fa);
    }

    // refuel stations
    var rs = {
        eco:[],
        gov:[],
        tl:(this._defaultRSMinTL - 1),
        factor:this._refuelStationCostFactor
    }
    var set = 1;
    for (var i = 0; i < this._govs.length; i++) {
        if (this._refuelGovs & set) {
            rs.gov.push(i)
        }
        set *= 2;
    }
    var set = 1;
    for (var i = 0; i < this._ecos.length; i++) {
        if (this._refuelEcos & set) {
            rs.eco.push(i)
        }
        set *= 2;
    }
    if (this._refuelStationAvailability.length >= 1) {
        this._refuelStationAvailability[0] = rs;
    } else {
        this._refuelStationAvailability.push(rs);
    }
}

//-------------------------------------------------------------------------------------------------------------
this.$updateBitwiseValues = function() {
    var bitVals = [1, 2, 4, 8, 16, 32, 64, 128, 256];
    this._fuelAllegiances = 0;
    for (var i = 0; i < this._fuelAllegiance.length; i++) {
        var idx = this._allegiances.indexOf(this._fuelAllegiance[i]);
        this._fuelAllegiances += bitVals[idx];
    }
    this._fuelGovs = 0;
    this._fuelEcos = 0;
    if (this._fuelAvailability.length > 0) {
        for (var i = 0; i < this._fuelAvailability[0].gov.length; i++) {
            this._fuelGovs += bitVals[(this._fuelAvailability[0].gov[i])];
        }
        for (var i = 0; i < this._fuelAvailability[0].eco.length; i++) {
            this._fuelEcos += bitVals[(this._fuelAvailability[0].eco[i])];
        }
    }
    this._refuelGovs = 0;
    this._refuelEcos = 0;
    if (this._refuelStationAvailability.length > 0) {
        for (var i = 0; i < this._refuelStationAvailability[0].gov.length; i++) {
            this._refuelGovs += bitVals[(this._refuelStationAvailability[0].gov[i])];
        }
        for (var i = 0; i < this._refuelStationAvailability[0].eco.length; i++) {
            this._refuelEcos += bitVals[(this._refuelStationAvailability[0].eco[i])];
        }
    }
}

//-------------------------------------------------------------------------------------------------------------
// resets all fuel-related data
this.$reset = function() {
    this._saveSettings = false;    
    this._fuelNormal = true;
    this._npcUseStations = false;
    this._fuelUseRations = false;
    this._fuelAvailability = [{eco:[0,1,2,3,4,5,6,7],gov:[0,1,2,3,4,5,6,7],tl:0,rations:false,max:1}];
    this._maxFuelRation = 1;
    this._fuelAllegiance = ["galcop"];
    this._fuelSystemOverride = [];
    this._refuelStationAvailability = [{eco:[0,1,2,3,4,5,6,7],gov:[0,1,2,3,4,5,6,7],tl:0,factor:0.15}];
    this._refuelStationSystemOverride = [];
    this._refuelStationCostFactor = 0.15
    this._defaultRSMinTL = 0;
    this._emergencyFuel = [];
    delete missionVariables.FuelTweaks_FuelAvailability;
    delete missionVariables.Fuel
    delete missionVariables.FuelTweaks_Allegiance;
    delete missionVariables.FuelTweaks_FuelSystemOverride;;
    delete missionVariables.FuelTweaks_RefuelStationAvailability;
    delete missionVariables.FuelTweaks_RefuelStationSystemOverride;
    delete missionVariables.FuelTweaks_RefuelStationCostFactor;
    delete missionVariables.FuelTweaks_RefuelStationMinTL;
    delete missionVariables.FuelTweaks_Normal;
    this.$updateBitwiseValues();
}

//-------------------------------------------------------------------------------------------------------------
this.$governmentDescription = function $governmentDescription(gov) {
	switch (gov) {
		case 0: return "Anarchy";
		case 1: return "Feudal";
		case 2: return "Multi-Government";
		case 3: return "Dictatorship";
		case 4: return "Communist";
		case 5: return "Confederacy";
		case 6: return "Democracy";
		case 7: return "Corporate State";
	}
}

//-------------------------------------------------------------------------------------------------------------
this.$economyDescription = function $economyDescription(eco) {
	if (worldScripts["market_tweak.js"]) {
		switch (eco) {
			case 0: return "Extreme Industrial";
			case 1: return "Strong Industrial";
			case 2: return "Common Industrial";
			case 3: return "Mainly Industrial";
			case 4: return "Mainly Agricultural";
			case 5: return "Common Agricultural";
			case 6: return "Strong Agricultural";
			case 7: return "Extreme Agricultural";
		}
	} else {
		switch (eco) {
			case 0: return "Rich Industrial";
			case 1: return "Average Industrial";
			case 2: return "Poor Industrial";
			case 3: return "Mainly Industrial";
			case 4: return "Mainly Agricultural";
			case 5: return "Rich Agricultural";
			case 6: return "Average Agricultural";
			case 7: return "Poor Agricultural";
		}
	}
}

//-------------------------------------------------------------------------------------------------------------
// this ensures our overrides to EQ_FUEL will get included if Isis Interstellar wins the install race
// if, instead, we win, the Isis script wouldn't be called for EQ_FUEL anyway.
this.$updateIsisEquipment = function() {
    if (worldScripts["oolite-II"]) {
        var ii = worldScripts["oolite-II"];
        ii.$hold_allowAwardEquipment = ii.II_allowAwardEquipment;
        ii.II_allowAwardEquipment = this.$II_allowAwardEquipment;
    }
}

//-------------------------------------------------------------------------------------------------------------
this.$II_allowAwardEquipment = function(equipment, ship, context) {
	if (context == "scripted") return true;
    if (equipment === "EQ_FUEL") {
        if (system.ID === -1) return true;
        var fe = worldScripts.FuelTweaks_FuelEconomy;
        if (fe._fuelNormal === true) return true;
        if (fe.$isFuelAvailable() === true && fe.$fuelRationsInUse() === false && fe.$isRefuelStationAvailable() === false) return true;
        return false;
    }
    return this.$hold_allowAwardEquipment(equipment, ship, context);
}
