package data.scripts.util;

import com.fs.starfarer.api.Global;
import com.fs.starfarer.api.campaign.CampaignFleetAPI;
import com.fs.starfarer.api.campaign.FactionAPI;
import com.fs.starfarer.api.campaign.FactionDoctrineAPI;
import com.fs.starfarer.api.characters.MutableCharacterStatsAPI;
import com.fs.starfarer.api.characters.OfficerDataAPI;
import com.fs.starfarer.api.characters.PersonAPI;
import com.fs.starfarer.api.combat.BattleObjectiveAPI;
import com.fs.starfarer.api.combat.CombatEntityAPI;
import com.fs.starfarer.api.combat.DamagingProjectileAPI;
import com.fs.starfarer.api.combat.MissileAPI;
import com.fs.starfarer.api.combat.ShipAPI;
import com.fs.starfarer.api.combat.ShipHullSpecAPI;
import com.fs.starfarer.api.combat.ShipVariantAPI;
import com.fs.starfarer.api.fleet.FleetMemberAPI;
import com.fs.starfarer.api.graphics.SpriteAPI;
import com.fs.starfarer.api.impl.campaign.fleets.FleetParamsV3;
import com.fs.starfarer.api.impl.campaign.ids.Factions;
import com.fs.starfarer.api.mission.FleetSide;
import com.fs.starfarer.api.plugins.OfficerLevelupPlugin;
import data.scripts.SWPModPlugin;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.Set;
import org.dark.shaders.util.ShaderLib;
import org.lazywizard.lazylib.MathUtils;
import org.lazywizard.lazylib.combat.CombatUtils;
import org.lwjgl.util.vector.Vector2f;

public class SWP_Util {

    public static final Set<String> SPECIAL_SHIPS = new HashSet<>(36);

    public static boolean OFFSCREEN = false;
    public static final float OFFSCREEN_GRACE_CONSTANT = 500f;
    public static final float OFFSCREEN_GRACE_FACTOR = 2f;

    static {
        SPECIAL_SHIPS.add("ii_boss_dominus");
        SPECIAL_SHIPS.add("ii_boss_titanx");
        SPECIAL_SHIPS.add("msp_boss_potniaBis");
        SPECIAL_SHIPS.add("ms_boss_charybdis");
        SPECIAL_SHIPS.add("ms_boss_mimir");
        SPECIAL_SHIPS.add("tem_boss_paladin");
        SPECIAL_SHIPS.add("tem_boss_archbishop");
        SPECIAL_SHIPS.add("swp_boss_phaeton");
        SPECIAL_SHIPS.add("swp_boss_hammerhead");
        SPECIAL_SHIPS.add("swp_boss_sunder");
        SPECIAL_SHIPS.add("swp_boss_tarsus");
        SPECIAL_SHIPS.add("swp_boss_medusa");
        SPECIAL_SHIPS.add("swp_boss_falcon");
        SPECIAL_SHIPS.add("swp_boss_hyperion");
        SPECIAL_SHIPS.add("swp_boss_paragon");
        SPECIAL_SHIPS.add("swp_boss_mule");
        SPECIAL_SHIPS.add("swp_boss_aurora");
        SPECIAL_SHIPS.add("swp_boss_odyssey");
        SPECIAL_SHIPS.add("swp_boss_atlas");
        SPECIAL_SHIPS.add("swp_boss_afflictor");
        SPECIAL_SHIPS.add("swp_boss_brawler");
        SPECIAL_SHIPS.add("swp_boss_cerberus");
        SPECIAL_SHIPS.add("swp_boss_dominator");
        SPECIAL_SHIPS.add("swp_boss_doom");
        SPECIAL_SHIPS.add("swp_boss_euryale");
        SPECIAL_SHIPS.add("swp_boss_lasher_b");
        SPECIAL_SHIPS.add("swp_boss_lasher_r");
        SPECIAL_SHIPS.add("swp_boss_onslaught");
        SPECIAL_SHIPS.add("swp_boss_shade");
        SPECIAL_SHIPS.add("swp_boss_eagle");
        SPECIAL_SHIPS.add("swp_boss_beholder");
        SPECIAL_SHIPS.add("swp_boss_dominator_luddic_path");
        SPECIAL_SHIPS.add("swp_boss_onslaught_luddic_path");
        SPECIAL_SHIPS.add("swp_boss_conquest");
        SPECIAL_SHIPS.add("swp_boss_frankenstein");
        SPECIAL_SHIPS.add("swp_boss_sporeship");
        SPECIAL_SHIPS.add("swp_boss_excelsior");
        SPECIAL_SHIPS.add("uw_boss_astral");
        SPECIAL_SHIPS.add("tiandong_boss_wuzhang");
        SPECIAL_SHIPS.add("pack_bulldog_bullseye");
        SPECIAL_SHIPS.add("pack_pitbull_bullseye");
        SPECIAL_SHIPS.add("pack_komondor_bullseye");
        SPECIAL_SHIPS.add("pack_schnauzer_bullseye");
        SPECIAL_SHIPS.add("diableavionics_IBBgulf");
        SPECIAL_SHIPS.add("sun_ice_ihs");
        SPECIAL_SHIPS.add("FOB_boss_rast");
        SPECIAL_SHIPS.add("tahlan_vestige");
        SPECIAL_SHIPS.add("loamtp_macnamara_boss");
    }

    public static String getNonDHullId(ShipHullSpecAPI spec) {
        if (spec == null) {
            return null;
        }
        if (spec.getDParentHullId() != null && !spec.getDParentHullId().isEmpty()) {
            return spec.getDParentHullId();
        } else {
            return spec.getHullId();
        }
    }

    /* LazyLib 2.4b revert */
    public static List<DamagingProjectileAPI> getProjectilesWithinRange(Vector2f location, float range) {
        List<DamagingProjectileAPI> projectiles = new ArrayList<>();

        for (DamagingProjectileAPI tmp : Global.getCombatEngine().getProjectiles()) {
            if (tmp instanceof MissileAPI) {
                continue;
            }

            if (MathUtils.isWithinRange(tmp.getLocation(), location, range)) {
                projectiles.add(tmp);
            }
        }

        return projectiles;
    }

    /* LazyLib 2.4b revert */
    public static List<MissileAPI> getMissilesWithinRange(Vector2f location, float range) {
        List<MissileAPI> missiles = new ArrayList<>();

        for (MissileAPI tmp : Global.getCombatEngine().getMissiles()) {
            if (MathUtils.isWithinRange(tmp.getLocation(), location, range)) {
                missiles.add(tmp);
            }
        }

        return missiles;
    }

    /* LazyLib 2.4b revert */
    public static List<ShipAPI> getShipsWithinRange(Vector2f location, float range) {
        List<ShipAPI> ships = new ArrayList<>();

        for (ShipAPI tmp : Global.getCombatEngine().getShips()) {
            if (tmp.isShuttlePod()) {
                continue;
            }

            if (MathUtils.isWithinRange(tmp, location, range)) {
                ships.add(tmp);
            }
        }

        return ships;
    }

    /* LazyLib 2.4b revert */
    public static List<CombatEntityAPI> getAsteroidsWithinRange(Vector2f location, float range) {
        List<CombatEntityAPI> asteroids = new ArrayList<>();

        for (CombatEntityAPI tmp : Global.getCombatEngine().getAsteroids()) {
            if (MathUtils.isWithinRange(tmp, location, range)) {
                asteroids.add(tmp);
            }
        }

        return asteroids;
    }

    /* LazyLib 2.4b revert */
    public static List<BattleObjectiveAPI> getObjectivesWithinRange(Vector2f location,
            float range) {
        List<BattleObjectiveAPI> objectives = new ArrayList<>();

        for (BattleObjectiveAPI tmp : Global.getCombatEngine().getObjectives()) {
            if (MathUtils.isWithinRange(tmp.getLocation(), location, range)) {
                objectives.add(tmp);
            }
        }

        return objectives;
    }

    /* LazyLib 2.4b revert */
    public static List<CombatEntityAPI> getEntitiesWithinRange(Vector2f location, float range) {
        List<CombatEntityAPI> entities = new ArrayList<>();

        for (CombatEntityAPI tmp : Global.getCombatEngine().getShips()) {
            if (MathUtils.isWithinRange(tmp, location, range)) {
                entities.add(tmp);
            }
        }

        // This also includes missiles
        for (CombatEntityAPI tmp : Global.getCombatEngine().getProjectiles()) {
            if (MathUtils.isWithinRange(tmp, location, range)) {
                entities.add(tmp);
            }
        }

        for (CombatEntityAPI tmp : Global.getCombatEngine().getAsteroids()) {
            if (MathUtils.isWithinRange(tmp, location, range)) {
                entities.add(tmp);
            }
        }

        return entities;
    }

    public static void applyForce(CombatEntityAPI target, Vector2f dir, float force) {
        if (target instanceof ShipAPI) {
            ShipAPI root = SWP_Multi.getRoot((ShipAPI) target);
            float forceRatio = root.getMass() / root.getMassWithModules();
            CombatUtils.applyForce(root, dir, force * forceRatio);
        } else {
            CombatUtils.applyForce(target, dir, force);
        }
    }

    public static void addMoreCommanderSkills(PersonAPI commander, CampaignFleetAPI fleet, FleetParamsV3 params, Random random) {
        if (random == null) {
            random = new Random();
        }

        FactionDoctrineAPI doctrine = fleet.getFaction().getDoctrine();
        if (params != null && params.doctrineOverride != null) {
            doctrine = params.doctrineOverride;
        }

        List<String> skills = new ArrayList<>(doctrine.getCommanderSkills());
        if (skills.isEmpty()) {
            return;
        }

        if (random.nextFloat() < doctrine.getCommanderSkillsShuffleProbability()) {
            Collections.shuffle(skills, random);
        }

        OfficerLevelupPlugin plugin = (OfficerLevelupPlugin) Global.getSettings().getPlugin("officerLevelUp");
        int min = 5;
        int max = plugin.getMaxLevel(null);

        int maxPoints = 6;

        int points = (int) ((float) maxPoints * ((float) commander.getStats().getLevel() - min) / (float) (max - min));
        if (points <= 0) {
            return;
        }

        MutableCharacterStatsAPI stats = commander.getStats();
        stats.setSkipRefresh(true);

        for (String skillId : skills) {
            int spend = Math.round(Math.min(points, 3 - stats.getSkillLevel(skillId)));
            stats.setSkillLevel(skillId, spend + stats.getSkillLevel(skillId));
            points -= spend;
            if (points <= 0) {
                break;
            }
        }

        stats.setSkipRefresh(false);
        stats.refreshCharacterStatsEffects();
    }

    public static int calculatePowerLevel(CampaignFleetAPI fleet) {
        float power = fleet.getFleetPoints();
        int offLvl = 0;
        int cdrLvl = 0;
        boolean commander = false;
        for (OfficerDataAPI officer : fleet.getFleetData().getOfficersCopy()) {
            if (officer.getPerson() == fleet.getCommander()) {
                commander = true;
                cdrLvl = officer.getPerson().getStats().getLevel();
            } else {
                offLvl += officer.getPerson().getStats().getLevel() + 1;
            }
        }
        if (!commander) {
            cdrLvl = fleet.getCommanderStats().getLevel();
        }
        power *= cdrLvl / 200f + 1f;
        power += Math.min(power * 2f, (float) Math.sqrt(power) * offLvl / 10f);
        return Math.round(power);
    }

    public static boolean isOnscreen(Vector2f point, float radius) {
        return OFFSCREEN || ShaderLib.isOnScreen(point, radius * OFFSCREEN_GRACE_FACTOR + OFFSCREEN_GRACE_CONSTANT);
    }

    public static int clamp255(int x) {
        return Math.max(0, Math.min(255, x));
    }

    public static float effectiveRadius(ShipAPI ship) {
        if (ship.getSpriteAPI() == null || ship.isPiece()) {
            return ship.getCollisionRadius();
        } else {
            float fudgeFactor = 1.5f;
            return ((ship.getSpriteAPI().getWidth() / 2f) + (ship.getSpriteAPI().getHeight() / 2f)) * 0.5f * fudgeFactor;
        }
    }

    public static Collection<String> getBuiltInHullMods(ShipAPI ship) {
        ShipVariantAPI tmp = ship.getVariant().clone();
        tmp.clearHullMods();
        return tmp.getHullMods();
    }

    public static float getMemberRadiusEstimate(FleetMemberAPI member) {
        float radius = 500f;
        if (member.getHullSpec().getShieldSpec() != null) {
            radius = member.getHullSpec().getShieldSpec().getRadius();
        } else if (!member.getHullSpec().getSpriteName().isEmpty()) {
            SpriteAPI sprite = Global.getSettings().getSprite(member.getHullSpec().getSpriteName());
            float fudgeFactor = 1.5f;
            radius = ((sprite.getWidth() / 2f) + (sprite.getHeight() / 2f)) * 0.5f * fudgeFactor;
        }
        return radius;
    }

    public static String getOrJoined(String... strings) {
        if (strings.length == 1) {
            return strings[0];
        }

        String result = "";
        for (int i = 0; i < strings.length - 1; i++) {
            result += strings[i] + ", ";
        }
        if (!result.isEmpty()) {
            result = result.substring(0, result.length() - 2);
        }
        if (strings.length >= 2) {
            result += " or " + strings[strings.length - 1];
        }
        return result;
    }

    public static Vector2f getSafeSpawn(float collisionRadius, FleetSide side, float mapX, float mapY, boolean pursuit) {
        Vector2f spawnLocation = new Vector2f();
        float searchRadius = collisionRadius * 1.25f;
        while (searchRadius > 0) {
            searchRadius -= collisionRadius * 0.05f;
            spawnLocation.x = MathUtils.getRandomNumberInRange(-mapX / 2f, mapX / 2f);
            spawnLocation.y = mapY / 2f;
            if (side == FleetSide.PLAYER) {
                spawnLocation.y *= -1f;
            }
            if (pursuit) {
                spawnLocation.y *= -1f;
            }

            boolean collision = false;
            List<ShipAPI> ships = SWP_Util.getShipsWithinRange(spawnLocation, searchRadius);
            for (ShipAPI ship : ships) {
                if (ship.isFighter() || ship.isDrone() || ship.isShuttlePod() || ship.isPiece()) {
                    continue;
                }
                collision = true;
            }

            if (!collision) {
                break;
            }
        }

        return spawnLocation;
    }

    // Algorithm by broofa @ stackoverflow.com
    // Returns position of where the projectile should head towards to hit the target
    // Returns null if the projectile can never hit the target
    public static Vector2f intercept(Vector2f point, float speed, Vector2f target, Vector2f targetVel) {
        final Vector2f difference = new Vector2f(target.x - point.x, target.y - point.y);

        final float a = targetVel.x * targetVel.x + targetVel.y * targetVel.y - speed * speed;
        final float b = 2 * (targetVel.x * difference.x + targetVel.y * difference.y);
        final float c = difference.x * difference.x + difference.y * difference.y;

        final Vector2f solutionSet = quad(a, b, c);

        Vector2f intercept = null;
        if (solutionSet != null) {
            float bestFit = Math.min(solutionSet.x, solutionSet.y);
            if (bestFit < 0) {
                bestFit = Math.max(solutionSet.x, solutionSet.y);
            }
            if (bestFit > 0) {
                intercept = new Vector2f(target.x + targetVel.x * bestFit, target.y + targetVel.y * bestFit);
            }
        }

        return intercept;
    }

    public static Vector2f intercept(Vector2f point, float speed, float acceleration, float maxspeed, Vector2f target,
            Vector2f targetVel) {
        final Vector2f difference = new Vector2f(target.x - point.x, target.y - point.y);

        float s = speed;
        float a = acceleration / 2f;
        float b = speed;
        float c = difference.length();
        Vector2f solutionSet = quad(a, b, c);
        if (solutionSet != null) {
            float t = Math.min(solutionSet.x, solutionSet.y);
            if (t < 0) {
                t = Math.max(solutionSet.x, solutionSet.y);
            }
            if (t > 0) {
                s = acceleration * t;
                s = s / 2f + speed;
                s = Math.min(s, maxspeed);
            }
        }

        a = targetVel.x * targetVel.x + targetVel.y * targetVel.y - s * s;
        b = 2 * (targetVel.x * difference.x + targetVel.y * difference.y);
        c = difference.x * difference.x + difference.y * difference.y;

        solutionSet = quad(a, b, c);

        Vector2f intercept = null;
        if (solutionSet != null) {
            float bestFit = Math.min(solutionSet.x, solutionSet.y);
            if (bestFit < 0) {
                bestFit = Math.max(solutionSet.x, solutionSet.y);
            }
            if (bestFit > 0) {
                intercept = new Vector2f(target.x + targetVel.x * bestFit, target.y + targetVel.y * bestFit);
            }
        }

        return intercept;
    }

    public static float lerp(float x, float y, float alpha) {
        return (1f - alpha) * x + alpha * y;
    }

    public static Vector2f quad(float a, float b, float c) {
        Vector2f solution = null;
        if (Float.compare(Math.abs(a), 0) == 0) {
            if (Float.compare(Math.abs(b), 0) == 0) {
                solution = (Float.compare(Math.abs(c), 0) == 0) ? new Vector2f(0, 0) : null;
            } else {
                solution = new Vector2f(-c / b, -c / b);
            }
        } else {
            float d = b * b - 4 * a * c;
            if (d >= 0) {
                d = (float) Math.sqrt(d);
                float e = 2 * a;
                solution = new Vector2f((-b - d) / e, (-b + d) / e);
            }
        }
        return solution;
    }

    public static void initExtraFactions() {
        if (Global.getSector() == null) {
            return;
        }

        Map<String, Float> domainFactions = new HashMap<>();
        domainFactions.put(Factions.HEGEMONY, 1f);
        domainFactions.put(Factions.INDEPENDENT, 0.5f);
        domainFactions.put(Factions.LUDDIC_CHURCH, 0.5f);
        domainFactions.put(Factions.PERSEAN, 1f);
        domainFactions.put(Factions.PIRATES, 1f);
        domainFactions.put(Factions.SCAVENGERS, 0.25f);
        domainFactions.put(Factions.DIKTAT, 0.5f);
        domainFactions.put(Factions.TRITACHYON, 1f);
        domainFactions.put("tiandong", 0.75f);
        domainFactions.put("mayasura", 0.5f);

        Map<String, Float> sectorFactions = new HashMap<>();
        sectorFactions.putAll(domainFactions);
        sectorFactions.put(Factions.LIONS_GUARD, 0.5f);
        sectorFactions.put(Factions.LUDDIC_PATH, 0.5f);
        sectorFactions.put("ORA", 0.75f);
        sectorFactions.put("interstellarimperium", 0.75f);
        sectorFactions.put("ii_imperial_guard", 0.25f);
        sectorFactions.put("blackrock_driveyards", 0.75f);
        sectorFactions.put("br_consortium", 0.25f);
        sectorFactions.put("syndicate_asp", 0.25f);
        sectorFactions.put("shadow_industry", 1f);
        sectorFactions.put("junk_pirates", 0.75f);
        sectorFactions.put("pack", 0.5f);
        sectorFactions.put("exipirated", 0.5f);
        sectorFactions.put("SCY", 1f);
        sectorFactions.put("diableavionics", 1f);
        sectorFactions.put("Coalition", 1f);
        sectorFactions.put("dassault_mikoyan", 0.75f);
        sectorFactions.put("sun_ice", 0.5f);
        sectorFactions.put("kadur_remnant", 0.75f);
        sectorFactions.put("al_ars", 1f);
        sectorFactions.put("mayasuran_guard", 0.25f);
        sectorFactions.put("metelson", 0.75f);
        sectorFactions.put("communist_clouds", 0.5f);
        sectorFactions.put("xhanempire", 0.75f);
        sectorFactions.put("tahlan_legioinfernalis", 0.5f);
        sectorFactions.put("scalartech", 0.5f);

        Map<String, Float> everythingFactions = new HashMap<>();
        everythingFactions.putAll(sectorFactions);
        everythingFactions.put(Factions.DERELICT, 0.25f);
        everythingFactions.put(Factions.REMNANTS, 1f);
        everythingFactions.put("templars", 0.5f);
        everythingFactions.put("cabal", 0.5f);
        everythingFactions.put("exigency", 0.75f);
        everythingFactions.put("approlight", 0.75f);
        everythingFactions.put("immortallight", 0.25f);
        everythingFactions.put("fob", 0.75f);
        everythingFactions.put("blade_breakers", 0.5f);
        everythingFactions.put("OCI", 0.5f);
        everythingFactions.put("kingdom_of_terra", 0.5f);
        everythingFactions.put("sylphon", 1f);
        everythingFactions.put("tahlan_greathouses", 0.75f);

        List<FactionAPI> extraFactionAPIs = new ArrayList<>();
        extraFactionAPIs.add(Global.getSector().getFaction("domain"));
        extraFactionAPIs.add(Global.getSector().getFaction("sector"));
        extraFactionAPIs.add(Global.getSector().getFaction("everything"));

        for (FactionAPI extraFactionAPI : extraFactionAPIs) {
            Map<String, Float> factions;
            switch (extraFactionAPI.getId()) {
                case "domain":
                    factions = domainFactions;
                    break;
                case "sector":
                    factions = sectorFactions;
                    break;
                case "everything":
                    factions = everythingFactions;
                    break;
                default:
                    continue;
            }

            Set<String> setCopy;
            setCopy = new LinkedHashSet<>(extraFactionAPI.getAlwaysKnownShips());
            for (String alwaysKnownShip : setCopy) {
                extraFactionAPI.removeUseWhenImportingShip(alwaysKnownShip);
            }
            setCopy = new LinkedHashSet<>(extraFactionAPI.getKnownShips());
            for (String knownShip : setCopy) {
                extraFactionAPI.removeKnownShip(knownShip);
            }
            setCopy = new LinkedHashSet<>(extraFactionAPI.getKnownFighters());
            for (String knownFighter : setCopy) {
                extraFactionAPI.removeKnownFighter(knownFighter);
            }
            setCopy = new LinkedHashSet<>(extraFactionAPI.getKnownWeapons());
            for (String knownWeapon : setCopy) {
                extraFactionAPI.removeKnownWeapon(knownWeapon);
            }
            setCopy = new LinkedHashSet<>(extraFactionAPI.getKnownHullMods());
            for (String knownHullMod : setCopy) {
                extraFactionAPI.removeKnownHullMod(knownHullMod);
            }
            setCopy = new LinkedHashSet<>(extraFactionAPI.getPriorityShips());
            for (String priorityShip : setCopy) {
                extraFactionAPI.removePriorityShip(priorityShip);
            }
            setCopy = new LinkedHashSet<>(extraFactionAPI.getPriorityFighters());
            for (String priorityFighter : setCopy) {
                extraFactionAPI.removePriorityFighter(priorityFighter);
            }
            setCopy = new LinkedHashSet<>(extraFactionAPI.getPriorityWeapons());
            for (String priorityWeapon : setCopy) {
                extraFactionAPI.removePriorityWeapon(priorityWeapon);
            }
            setCopy = new LinkedHashSet<>(extraFactionAPI.getPriorityHullMods());
            for (String priorityHullMod : setCopy) {
                extraFactionAPI.removePriorityHullMod(priorityHullMod);
            }

            extraFactionAPI.clearShipRoleCache();

            for (Entry<String, Float> faction : factions.entrySet()) {
                FactionAPI factionAPI;
                try {
                    factionAPI = Global.getSector().getFaction(faction.getKey());
                } catch (RuntimeException e) {
                    continue;
                }
                if (factionAPI == null) {
                    try {
                        factionAPI = Global.getSettings().createBaseFaction(faction.getKey());
                    } catch (RuntimeException e) {
                        continue;
                    }
                }
                if (factionAPI == null) {
                    continue;
                }

                for (String alwaysKnownShip : factionAPI.getAlwaysKnownShips()) {
                    if (!extraFactionAPI.getAlwaysKnownShips().contains(alwaysKnownShip)) {
                        extraFactionAPI.addUseWhenImportingShip(alwaysKnownShip);
                    }
                }
                for (String knownShip : factionAPI.getKnownShips()) {
                    if (!extraFactionAPI.getKnownShips().contains(knownShip)) {
                        extraFactionAPI.addKnownShip(knownShip, false);
                        extraFactionAPI.getHullFrequency().put(knownShip, faction.getValue());
                    } else {
                        Float freq = extraFactionAPI.getHullFrequency().get(knownShip);
                        if (freq == null) {
                            freq = faction.getValue();
                        } else {
                            freq += faction.getValue();
                        }
                        extraFactionAPI.getHullFrequency().put(knownShip, freq);
                    }
                }
                for (String knownFighter : factionAPI.getKnownFighters()) {
                    if (!extraFactionAPI.getKnownFighters().contains(knownFighter)) {
                        extraFactionAPI.addKnownFighter(knownFighter, false);
                    }
                }
                for (String knownWeapon : factionAPI.getKnownWeapons()) {
                    if (!extraFactionAPI.getKnownWeapons().contains(knownWeapon)) {
                        extraFactionAPI.addKnownWeapon(knownWeapon, false);
                    }
                }
                for (String knownHullMod : factionAPI.getKnownHullMods()) {
                    if (!extraFactionAPI.getKnownHullMods().contains(knownHullMod)) {
                        extraFactionAPI.addKnownHullMod(knownHullMod);
                    }
                }
            }

            extraFactionAPI.clearShipRoleCache();
        }
    }

    public static enum RequiredFaction {

        NONE, JUNK_PIRATES, TIANDONG, SHADOWYARDS, IMPERIUM, TEMPLARS, DIABLE, BLACKROCK, EXIGENCY, SCY, CABAL, ORA, TYRADOR, DME, ICE, BORKEN, SCALARTECH, ARKGNEISIS;

        public boolean isLoaded() {
            switch (this) {
                case JUNK_PIRATES:
                    return SWPModPlugin.junkPiratesExists;
                case TIANDONG:
                    return SWPModPlugin.tiandongExists;
                case SHADOWYARDS:
                    return SWPModPlugin.shadowyardsExists;
                case IMPERIUM:
                    return SWPModPlugin.imperiumExists;
                case TEMPLARS:
                    return SWPModPlugin.templarsExists;
                case DIABLE:
                    return SWPModPlugin.diableExists;
                case BLACKROCK:
                    return SWPModPlugin.blackrockExists;
                case EXIGENCY:
                    return SWPModPlugin.exigencyExists;
                case SCY:
                    return SWPModPlugin.scyExists;
                case CABAL:
                    return SWPModPlugin.hasUnderworld;
                case ORA:
                    return SWPModPlugin.oraExists;
                case TYRADOR:
                    return SWPModPlugin.tyradorExists;
                case DME:
                    return SWPModPlugin.dmeExists;
                case ICE:
                    return SWPModPlugin.iceExists;
                case BORKEN:
                    return SWPModPlugin.borkenExists;
                case SCALARTECH:
                    return SWPModPlugin.scalarTechExists;
                case ARKGNEISIS:
                    return SWPModPlugin.arkgneisisExists;
                case NONE:
                default:
                    return true;
            }
        }
    }
}
