package AITB_2022.module.complex;

import adf.core.agent.communication.MessageManager;
import adf.core.agent.develop.DevelopData;
import adf.core.agent.info.AgentInfo;
import adf.core.agent.info.ScenarioInfo;
import adf.core.agent.info.WorldInfo;
import adf.core.agent.module.ModuleManager;
import adf.core.agent.precompute.PrecomputeData;
import adf.core.component.module.algorithm.Clustering;
import adf.core.component.module.complex.HumanDetector;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import rescuecore2.standard.entities.Area;
import rescuecore2.standard.entities.Human;
import rescuecore2.standard.entities.StandardEntity;
import rescuecore2.standard.entities.StandardEntityURN;
import rescuecore2.worldmodel.EntityID;

import static rescuecore2.standard.entities.StandardEntityURN.*;

public class AITFBCivilianDetector extends HumanDetector {

    private Clustering clustering;

    private EntityID result;

    public AITFBCivilianDetector(
            AgentInfo ai, WorldInfo wi, ScenarioInfo si, ModuleManager mm, DevelopData dd) {
        super(ai, wi, si, mm, dd);

        this.result = null;

        switch (scenarioInfo.getMode()) {
            case PRECOMPUTATION_PHASE:
                this.clustering = mm.getModule(
                        "SampleHumanDetector.Clustering",
                        "adf.impl.module.algorithm.SampleKMeans");
                break;
            case PRECOMPUTED:
                this.clustering = mm.getModule(
                        "SampleHumanDetector.Clustering",
                        "adf.impl.module.algorithm.SampleKMeans");
                break;
            case NON_PRECOMPUTE:
                this.clustering = mm.getModule(
                        "SampleHumanDetector.Clustering",
                        "adf.impl.module.algorithm.SampleKMeans");
                break;
        }
        registerModule(this.clustering);
    }

    @Override
    public HumanDetector updateInfo(MessageManager messageManager) {
        super.updateInfo(messageManager);
        if (this.getCountUpdateInfo() > 1)
            return this;
        return this;
    }

    @Override
    public HumanDetector calc() {
        Human transportHuman = this.agentInfo.someoneOnBoard();
        if (transportHuman != null) {
            this.result = transportHuman.getID();
            return this;
        }
        if (this.result != null) {
            Human target = (Human) this.worldInfo.getEntity(this.result);
            if (target != null) {
                if (!target.isHPDefined() || target.getHP() == 0)
                    this.result = null;
                else if (!target.isPositionDefined())
                    this.result = null;
                else if (!target.isBuriednessDefined() || target.getBuriedness() == 0)
                    this.result = null;
                else {
                    StandardEntity position = this.worldInfo.getPosition(target);
                    if (position != null) {
                        StandardEntityURN positionURN = position.getStandardURN();
                        if (positionURN == REFUGE || positionURN == AMBULANCE_TEAM)
                            this.result = null;
                    }
                }
            }
        }
        if (this.result == null) {
            if (clustering == null) {
                this.result = this.calcTargetInWorld();
                return this;
            }
            this.result = this.calcTargetInCluster(clustering);
            if (this.result == null) {
                this.result = this.calcTargetInWorld();
            }
        }
        return this;
    }

    private EntityID calcTargetInCluster(Clustering clustering) {
        int clusterIndex = clustering.getClusterIndex(this.agentInfo.getID());
        EntityID me = this.agentInfo.getID();
        Collection<StandardEntity> elements = clustering.getClusterEntities(clusterIndex);

        List<Human> loadTargets = new ArrayList<>();

        if (elements == null || elements.isEmpty())
            return null;

        for (StandardEntity next : this.worldInfo.getEntitiesOfType(CIVILIAN)) {
            Human h = (Human) next;
            StandardEntity positionEntity = this.worldInfo.getPosition(h);
            EntityID id = next.getID();
            if (positionEntity != null && positionEntity instanceof Area
                    && elements.contains(positionEntity)) {
                if (h.isHPDefined() && h.isBuriednessDefined() && h.getHP() > (h.getBuriedness() * h.getDamage())) {
                    if (positionEntity.getStandardURN() == BUILDING // 市民が建物内にいた場合に救出する
                            && h.isBuriednessDefined() && h.getBuriedness() > 0
                            && positionEntity.getStandardURN() != REFUGE) {
                        loadTargets.add(h);
                    }
                }
            }
        }
        if (loadTargets.size() > 0) {
            loadTargets.sort(new DistanceSorter(this.worldInfo, this.agentInfo.me()));
            return loadTargets.get(0).getID();
        }
        return null;
    }

    private EntityID calcTargetInWorld() {
        EntityID me = this.agentInfo.getID();
        List<Human> loadTargets = new ArrayList<>();

        for (StandardEntity next : this.worldInfo.getEntitiesOfType(CIVILIAN)) {
            Human h = (Human) next;
            StandardEntity positionEntity = this.worldInfo.getPosition(h);
            EntityID id = next.getID();
            if (positionEntity != null && positionEntity instanceof Area) {
                if (this.worldInfo.getDistance(this.agentInfo.getID(), positionEntity.getID()) < 2000) {
                    if (h.isHPDefined() && h.isBuriednessDefined() && h.getHP() > (h.getBuriedness() * h.getDamage())) {
                        if (positionEntity.getStandardURN() == BUILDING // 市民が建物内にいた場合に救出する
                                && h.isBuriednessDefined() && h.getBuriedness() > 0
                                && positionEntity.getStandardURN() != REFUGE)
                            loadTargets.add(h);
                    }
                }
            }
        }
        if (loadTargets.size() > 0) {
            loadTargets.sort(new DistanceSorter(this.worldInfo, this.agentInfo.me()));
            return loadTargets.get(0).getID();
        }
        return null;
    }

    @Override
    public EntityID getTarget() {
        return this.result;
    }

    @Override
    public HumanDetector precompute(PrecomputeData precomputeData) {
        super.precompute(precomputeData);
        if (this.getCountPrecompute() >= 2)
            return this;
        return this;
    }

    @Override
    public HumanDetector resume(PrecomputeData precomputeData) {
        super.resume(precomputeData);
        if (this.getCountResume() >= 2)
            return this;
        return this;
    }

    @Override
    public HumanDetector preparate() {
        super.preparate();
        if (this.getCountPreparate() >= 2)
            return this;
        return this;
    }

    private class DistanceSorter implements Comparator<StandardEntity> {
        private StandardEntity reference;
        private WorldInfo worldInfo;

        DistanceSorter(WorldInfo wi, StandardEntity reference) {
            this.reference = reference;
            this.worldInfo = wi;
        }

        public int compare(StandardEntity a, StandardEntity b) {
            int d1 = this.worldInfo.getDistance(this.reference, a);
            int d2 = this.worldInfo.getDistance(this.reference, b);
            return d1 - d2;
        }
    }
}
