package data.scripts.everyframe;

import com.fs.starfarer.api.Global;
import com.fs.starfarer.api.combat.BaseEveryFrameCombatPlugin;
import com.fs.starfarer.api.combat.CombatEngineAPI;
import com.fs.starfarer.api.combat.CombatEntityAPI;
import com.fs.starfarer.api.combat.DamagingProjectileAPI;
import com.fs.starfarer.api.combat.ShipAPI;
import com.fs.starfarer.api.input.InputEventAPI;
import data.scripts.util.SWP_Util;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.lazywizard.lazylib.MathUtils;
import org.lazywizard.lazylib.VectorUtils;
import org.lazywizard.lazylib.combat.AIUtils;
import org.lwjgl.util.vector.Vector2f;

public class SWP_ZeroPulseHoming extends BaseEveryFrameCombatPlugin {

    private static final float ACCELERATION = 1500f;

    private static final String DATA_KEY = "SWP_ZeroPulse";

    private static final float MAX_SPEED = 1000f;
    private static final Vector2f ZERO = new Vector2f();

    private static ShipAPI findBestTarget(DamagingProjectileAPI proj) {
        ShipAPI source = proj.getSource();
        if (source != null && source.getShipTarget() != null && !source.getShipTarget().isHulk()) {
            return source.getShipTarget();
        }

        return AIUtils.getNearestEnemy(proj);
    }

    private CombatEngineAPI engine;

    @Override
    public void advance(float amount, List<InputEventAPI> events) {
        if (engine == null) {
            return;
        }

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

        final LocalData localData = (LocalData) engine.getCustomData().get(DATA_KEY);
        final Map<DamagingProjectileAPI, CombatEntityAPI> targets = localData.targets;

        List<DamagingProjectileAPI> activeProjectiles = engine.getProjectiles();
        for (DamagingProjectileAPI projectile : activeProjectiles) {
            if (projectile.getWeapon() == null || !projectile.getWeapon().getId().contentEquals("swp_arcade_zerocannon") ||
                    projectile.didDamage() || projectile.isFading()) {
                continue;
            }

            if (!targets.containsKey(projectile)) {
                if (projectile.getProjectileSpecId() != null) {
                    targets.put(projectile, findBestTarget(projectile));
                }
            }
        }

        if (targets.isEmpty()) {
            return;
        }

        Iterator<Map.Entry<DamagingProjectileAPI, CombatEntityAPI>> iter = targets.entrySet().iterator();
        while (iter.hasNext()) {
            Map.Entry<DamagingProjectileAPI, CombatEntityAPI> entry = iter.next();
            DamagingProjectileAPI projectile = entry.getKey();
            CombatEntityAPI target = entry.getValue();

            if (projectile.didDamage() || !engine.isEntityInPlay(projectile)) {
                iter.remove();
                continue;
            }

            if (target == null || (target instanceof ShipAPI && ((ShipAPI) target).isHulk()) ||
                    (projectile.getOwner() == target.getOwner()) ||
                    !Global.getCombatEngine().isEntityInPlay(target)) {
                entry.setValue(findBestTarget(projectile));
                continue;
            }

            float distance = MathUtils.getDistance(target.getLocation(), projectile.getLocation());
            Vector2f guidedTarget = SWP_Util.intercept(projectile.getLocation(), projectile.getVelocity().length(),
                                                       ACCELERATION, MAX_SPEED,
                                                       target.getLocation(), target.getVelocity());
            if (guidedTarget == null) {
                Vector2f projection = new Vector2f(target.getVelocity());
                float scalar = distance / (projectile.getVelocity().length() + 1f);
                projection.scale(scalar);
                guidedTarget = Vector2f.add(target.getLocation(), projection, null);
            }

            Vector2f acceleration = VectorUtils.getDirectionalVector(projectile.getLocation(), guidedTarget);
            acceleration.scale(ACCELERATION * amount);
            Vector2f.add(acceleration, projectile.getVelocity(), acceleration);
            float speed = acceleration.length();
            float maxSpeed = MAX_SPEED;
            if (projectile.getSource() != null) {
                maxSpeed *= projectile.getSource().getMutableStats().getProjectileSpeedMult().getModifiedValue();
            }
            if (speed > maxSpeed) {
                acceleration.scale(maxSpeed / speed);
            }

            projectile.setFacing(VectorUtils.getAngle(ZERO, acceleration));
            projectile.getVelocity().set(acceleration);
        }
    }

    @Override
    public void init(CombatEngineAPI engine) {
        this.engine = engine;
        Global.getCombatEngine().getCustomData().put(DATA_KEY, new LocalData());
    }

    private static final class LocalData {

        final Map<DamagingProjectileAPI, CombatEntityAPI> targets = new LinkedHashMap<>(50);
    }
}
