package data.scripts.shipsystems.ai;

import com.fs.starfarer.api.Global;
import com.fs.starfarer.api.combat.CombatEngineAPI;
import com.fs.starfarer.api.combat.CombatEntityAPI;
import com.fs.starfarer.api.combat.FighterWingAPI;
import com.fs.starfarer.api.combat.ShipAPI;
import com.fs.starfarer.api.combat.ShipSystemAIScript;
import com.fs.starfarer.api.combat.ShipSystemAPI;
import com.fs.starfarer.api.combat.ShipwideAIFlags;
import com.fs.starfarer.api.util.IntervalUtil;
import data.scripts.hullmods.II_BasePackage;
import data.scripts.shipsystems.II_CommandCenterStats;
import data.scripts.shipsystems.II_CommandCenterStats.CCMode;
import java.util.ArrayList;
import java.util.List;
import org.lazywizard.lazylib.MathUtils;
import org.lazywizard.lazylib.combat.AIUtils;
import org.lwjgl.util.vector.Vector2f;

public class II_CommandCenterAI implements ShipSystemAIScript {

    private static final float REGROUP_SPEED_THRESHOLD_STANDARD = 1f / 3f;
    private static final float ENGAGE_REFIT_LIMIT_STANDARD = 1f / 3f;
    private static final float ENGAGE_ATTACK_THRESHOLD_STANDARD = 0.5f;

    private static final float REGROUP_SPEED_THRESHOLD_ARMOR = 0.5f;
    private static final float ENGAGE_REFIT_LIMIT_ARMOR = 0.5f;
    private static final float ENGAGE_ATTACK_THRESHOLD_ARMOR = 2f / 3f;

    private static final float REGROUP_SPEED_THRESHOLD_TARGETING = 0.2f;
    private static final float ENGAGE_REFIT_LIMIT_TARGETING = 0.2f;
    private static final float ENGAGE_ATTACK_THRESHOLD_TARGETING = 2f / 3f;

    private static final float ENGAGE_REFIT_LIMIT_ELITE = 0.2f;
    private static final float ENGAGE_ATTACK_THRESHOLD_ELITE = 1f / 3f;

    private static List<ShipAPI> getFighters(ShipAPI carrier) {
        List<ShipAPI> result = new ArrayList<>(30);

        for (ShipAPI ship : Global.getCombatEngine().getShips()) {
            if (!ship.isFighter()) {
                continue;
            }
            if (ship.getWing() == null) {
                continue;
            }
            if (ship.getWing().getSourceShip() == carrier) {
                result.add(ship);
            }
        }

        return result;
    }

    private CombatEngineAPI engine;
    private ShipwideAIFlags flags;
    private ShipAPI ship;
    private ShipSystemAPI system;

    private final IntervalUtil tracker = new IntervalUtil(0.1f, 0.2f);

    @Override
    public void advance(float amount, Vector2f missileDangerDir, Vector2f collisionDangerDir, ShipAPI target) {
        if (engine == null) {
            return;
        }

        if (engine.isPaused()) {
            return;
        }

        tracker.advance(amount);

        if (tracker.intervalElapsed()) {
            if (ship.getFluxTracker().isOverloadedOrVenting()) {
                return;
            }

            float REGROUP_SPEED_THRESHOLD = REGROUP_SPEED_THRESHOLD_STANDARD;
            float ENGAGE_REFIT_LIMIT = ENGAGE_REFIT_LIMIT_STANDARD;
            float ENGAGE_ATTACK_THRESHOLD = ENGAGE_ATTACK_THRESHOLD_STANDARD;
            if (ship.getVariant().hasHullMod(II_BasePackage.ARMOR_PACKAGE)) {
                REGROUP_SPEED_THRESHOLD = REGROUP_SPEED_THRESHOLD_ARMOR;
                ENGAGE_REFIT_LIMIT = ENGAGE_REFIT_LIMIT_ARMOR;
                ENGAGE_ATTACK_THRESHOLD = ENGAGE_ATTACK_THRESHOLD_ARMOR;
            } else if (ship.getVariant().hasHullMod(II_BasePackage.TARGETING_PACKAGE)) {
                REGROUP_SPEED_THRESHOLD = REGROUP_SPEED_THRESHOLD_TARGETING;
                ENGAGE_REFIT_LIMIT = ENGAGE_REFIT_LIMIT_TARGETING;
                ENGAGE_ATTACK_THRESHOLD = ENGAGE_ATTACK_THRESHOLD_TARGETING;
            } else if (ship.getVariant().hasHullMod(II_BasePackage.ELITE_PACKAGE)) {
                ENGAGE_REFIT_LIMIT = ENGAGE_REFIT_LIMIT_ELITE;
                ENGAGE_ATTACK_THRESHOLD = ENGAGE_ATTACK_THRESHOLD_ELITE;
            }

            CombatEntityAPI immediateTarget;
            if (flags.getCustom(ShipwideAIFlags.AIFlags.CARRIER_FIGHTER_TARGET) instanceof CombatEntityAPI) {
                immediateTarget = (CombatEntityAPI) flags.getCustom(ShipwideAIFlags.AIFlags.CARRIER_FIGHTER_TARGET);
            } else if (flags.getCustom(ShipwideAIFlags.AIFlags.MANEUVER_TARGET) instanceof CombatEntityAPI) {
                immediateTarget = (CombatEntityAPI) flags.getCustom(ShipwideAIFlags.AIFlags.MANEUVER_TARGET);
            } else {
                immediateTarget = ship.getShipTarget();
            }

            float fightersActive = 0f;
            float fightersNearCarrier = 0f;
            float fightersNearTarget = 0f;
            float fightersNotNearTarget = 0f;
            for (ShipAPI fighter : getFighters(ship)) {
                float targetNearnessThreshold = fighter.getWing().getSpec().getAttackRunRange() + 500f;
                float carrierNearnessThreshold = 750f;

                if (fighter.isAlive() && engine.isEntityInPlay(fighter) && !fighter.isLanding()) {
                    fightersActive += (fighter.getWing().getSpec().getOpCost(null) + 20f) / (float) fighter.getWing().getSpec().getNumFighters();
                }
                if (MathUtils.getDistance(fighter, ship) <= carrierNearnessThreshold) {
                    fightersNearCarrier += (fighter.getWing().getSpec().getOpCost(null) + 20f) / (float) fighter.getWing().getSpec().getNumFighters();
                }
                if ((immediateTarget != null) && (MathUtils.getDistance(fighter, immediateTarget) <= targetNearnessThreshold)) {
                    fightersNearTarget += (fighter.getWing().getSpec().getOpCost(null) + 20f) / (float) fighter.getWing().getSpec().getNumFighters();
                } else if ((immediateTarget != null) && (MathUtils.getDistance(fighter, immediateTarget) > targetNearnessThreshold)
                        && (MathUtils.getDistance(fighter, immediateTarget) <= fighter.getWing().getRange())) {
                    fightersNotNearTarget += (fighter.getWing().getSpec().getOpCost(null) + 20f) / (float) fighter.getWing().getSpec().getNumFighters();
                }
            }

            float totalFighters = 0f;
            for (FighterWingAPI wing : ship.getAllWings()) {
                totalFighters += wing.getSpec().getOpCost(null) + 20f;
            }

            CCMode desiredMode;
            if (totalFighters > 0) {
                if (ship.isPullBackFighters()) {
                    float fightersNotNearCarrier = fightersActive - fightersNearCarrier;
                    if (fightersNotNearCarrier > (totalFighters * REGROUP_SPEED_THRESHOLD)) {
                        desiredMode = CCMode.SPEED;
                    } else {
                        desiredMode = CCMode.REFIT;
                    }
                } else {
                    float fightersThatCanReachTarget = fightersNotNearTarget + fightersNearTarget;
                    if (fightersActive < (totalFighters * ENGAGE_REFIT_LIMIT)) {
                        desiredMode = CCMode.REFIT;
                    } else if (fightersNearTarget > (fightersThatCanReachTarget * ENGAGE_ATTACK_THRESHOLD)) {
                        desiredMode = CCMode.ATTACK;
                    } else {
                        desiredMode = CCMode.SPEED;
                    }
                }
            } else {
                desiredMode = CCMode.REFIT;
            }

            if ((desiredMode != II_CommandCenterStats.getMode(ship)) && AIUtils.canUseSystemThisFrame(ship)) {
                ship.useSystem();
            }
        }
    }

    @Override
    public void init(ShipAPI ship, ShipSystemAPI system, ShipwideAIFlags flags, CombatEngineAPI engine) {
        this.ship = ship;
        this.flags = flags;
        this.system = system;
        this.engine = engine;
    }

}
