package AIT_2022.module.complex;

import java.util.*;
import java.util.stream.Collectors;

import adf.core.agent.action.Action;
import adf.core.agent.communication.MessageManager;
import adf.core.agent.communication.standard.bundle.centralized.CommandAmbulance;
import adf.core.agent.communication.standard.bundle.centralized.CommandFire;
import adf.core.agent.communication.standard.bundle.centralized.CommandPolice;
import adf.core.agent.communication.standard.bundle.information.MessageFireBrigade;
import adf.core.agent.develop.DevelopData;
import adf.core.agent.info.*;
import adf.core.agent.module.ModuleManager;
import adf.core.component.module.algorithm.Clustering;
import adf.core.component.module.algorithm.DynamicClustering;
import adf.core.component.module.algorithm.PathPlanning;
import adf.core.component.module.complex.HumanDetector;
import rescuecore2.misc.geometry.*;
import java.awt.geom.Ellipse2D;
import rescuecore2.standard.entities.*;
import rescuecore2.worldmodel.EntityID;

// @ DEBUG {{
import com.mrl.debugger.remote.VDClient;
// }}

import AIT_2022.module.complex.common.EntityFilter;
import AIT_2022.module.complex.common.Stucked;
import AIT_2022.module.complex.fb.TargetSelector;

public class AITFbDetector extends HumanDetector {

    private PathPlanning pathPlanner;
    private Clustering clusterer;
    private DynamicClustering stuckedHumans;
    private EntityID result = null;
    private Set<EntityID> cluster = new HashSet<>();
    private TargetSelector targetSelector;
    
    private Map<EntityID, Integer> tasks = new HashMap<>();
    private final int life = 10;

    private final Random random = new Random();
    private final boolean VOICE = false;
    private final boolean RADIO = true;

    // @ DEBUG {{{
    private VDClient vdclient = VDClient.getInstance();
    // }}}

    public AITFbDetector(
                AgentInfo ai, WorldInfo wi,
                ScenarioInfo si, ModuleManager mm, DevelopData dd) {

        super(ai, wi, si, mm, dd);

        this.pathPlanner =
                mm.getModule("ActionTransport.PathPlanning",
                        "adf.sample.module.algorithm.SamplePathPlanning");
        this.registerModule(this.pathPlanner);

        this.clusterer =
                mm.getModule("SampleHumanDetector.Clustering",
                        "adf.sample.module.algorithm.SampleKMeans");
        this.registerModule(this.clusterer);

        this.stuckedHumans =
            mm.getModule("AIT_2022.module.algorithm.StuckedHumans");
        this.registerModule(this.stuckedHumans);

        this.targetSelector =
            mm.getModule("AIT.FB.TargetSelector");
        this.registerModule(this.targetSelector);

        // @ DEBUG {{{
        if (this.scenarioInfo.isDebugMode()) 
            this.vdclient.init("localhost", 1099);
        // }}}
    }

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

    @Override
    public HumanDetector updateInfo(MessageManager mm) {

        super.updateInfo(mm);

        if (this.getCountUpdateInfo() > 1)
            return this;

        this.random.setSeed(
                this.agentInfo.getID().getValue() + this.agentInfo.getTime());

        if (this.cluster.isEmpty()) this.initCluster();
        
        this.sendMessage(mm);

        return this;
    }

    @Override
    public HumanDetector calc() {
        this.targetSelector.calc();
        this.result = this.targetSelector.getResult();
        return this;
    }

    private void initCluster() {
        this.clusterer.calc();

        final EntityID me = this.agentInfo.getID();
        final int index = this.clusterer.getClusterIndex(me);
        final Collection<EntityID> ids =
                this.clusterer.getClusterEntityIDs(index);
        this.cluster.addAll(ids);
        this.targetSelector.setEntityInCluster(ids);
    }

    private void sendMessage(MessageManager mm) {
        final EntityFilter filter 
                = new EntityFilter(this.worldInfo, this.agentInfo);
        final Set<Blockade> blockades = filter.getBlockades();
        final Stucked Stucked = new Stucked(
                blockades, this.agentInfo.getX(), this.agentInfo.getY());
        final EntityID positionID = this.agentInfo.getPosition();
        if (Stucked.isStucked()) {
            final EntityID toID = filter.getNearyPF();
            // System.out.println(String.format("[FB_Stucked(%3d):%10d] toID(%10d)",
            //         this.agentInfo.getTime(),
            //         this.agentInfo.getID(),
            //         toID == null ? -1 : toID.getValue()
            // ));
            mm.addMessage(new CommandPolice(
                    RADIO, toID, positionID, CommandPolice.ACTION_CLEAR));
            mm.addMessage(new CommandPolice(
                    VOICE, toID, positionID, CommandPolice.ACTION_CLEAR));
            return;
        } else if (this.isMyselfBuried()) {
            final EntityID toID = filter.getNearyFB();
            mm.addMessage(new CommandFire(
                    RADIO, toID, positionID, CommandFire.ACTION_RESCUE));
            mm.addMessage(new CommandFire(
                    VOICE, toID, positionID, CommandFire.ACTION_RESCUE));
            // System.out.println(String.format("[FB_Buryed(%3d):%10d] toID(%10d)",
            //         this.agentInfo.getTime(),
            //         this.agentInfo.getID(),
            //         toID == null ? -1 : toID.getValue()
            // ));
            return;
        }

        if (this.agentInfo.getTime() == 1) return;
        final Action action
                = this.agentInfo.getExecutedAction(this.agentInfo.getTime()-1);

        if (action.toString().contains("ActionRescue")) {
            final EntityID toID = this.worldInfo.getChanged()
                    .getChangedEntities()
                    .stream()
                    .map(this.worldInfo::getEntity)
                    .filter(AmbulanceTeam.class::isInstance)
                    .map(AmbulanceTeam.class::cast)
                    .map(AmbulanceTeam::getID)
                    .min(Comparator.comparing(EntityID::getValue)).orElse(null);
            if (toID != null) {
                final EntityID targetID 
                        = this.worldInfo.getPosition(this.result).getID();
                mm.addMessage(new CommandAmbulance(
                        VOICE, toID, targetID, CommandAmbulance.ACTION_LOAD));
            }
        }
    }

    private boolean isMyselfBuried() {
        EntityID myID = this.agentInfo.getID();
        Human me = (Human) this.worldInfo.getEntity(myID);
        if (!(this.agentInfo.getPositionArea() instanceof Building)) return false;
        if (!me.isBuriednessDefined()) return false;
        return me.getBuriedness() != 0;
    }

    public static Point2D getPoint(Human human)
    {
        final double x = human.getX();
        final double y = human.getY();
        return new Point2D(x, y);
    }

    public static java.awt.geom.Area makeAWTArea(Point2D p, double rad)
    {
        final double d = rad * 2.0;
        final double x = p.getX() - rad;
        final double y = p.getY() - rad;
        return new java.awt.geom.Area(new Ellipse2D.Double(x, y, d, d));
    }
}
