package autumn_2023.module.complex;

import adf.core.agent.communication.MessageManager;
import adf.core.agent.communication.standard.bundle.MessageUtil;
import adf.core.agent.communication.standard.bundle.information.MessageCivilian;
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.communication.CommunicationMessage;
import adf.core.component.module.complex.FireTargetAllocator;
import autumn_2023.module.comm.infomation.MessageCost;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import rescuecore2.standard.entities.AmbulanceTeam;
import rescuecore2.standard.entities.Civilian;
import rescuecore2.standard.entities.FireBrigade;
import rescuecore2.worldmodel.EntityID;

public class AuctionFireTargetAllocator extends FireTargetAllocator{

    // 分散エージェントからの埋没市民のメッセージリスト
    private List<EntityID> receivedAgentCivilians;
    private List<EntityID> finishreceivedCivilians;

    // 分散エージェントからのコストのメッセージリスト
    private List<MessageCost> rescueCosts;
    private Map<EntityID, EntityID> rescueResult;
    private Map<EntityID, RescueCivilianCost> calclationResult;

    public AuctionFireTargetAllocator(AgentInfo ai, WorldInfo wi, ScenarioInfo si, ModuleManager moduleManager, DevelopData developData) 
    {
        super(ai, wi, si, moduleManager, developData);

        this.receivedAgentCivilians = new ArrayList<>();
        this.finishreceivedCivilians = new ArrayList<>();
        this.rescueCosts = new ArrayList<>();
        this.rescueResult = new HashMap<>();
        this.calclationResult = new HashMap<>();
    }


    @Override
    public FireTargetAllocator resume(PrecomputeData precomputeData) 
    {
        super.resume(precomputeData);

        return this;
    }


    @Override
    public FireTargetAllocator preparate() 
    {
        super.preparate();

        return this;
    }


    @Override
    public Map<EntityID, EntityID> getResult() 
    {
        return rescueResult;
    }


    @Override
    public FireTargetAllocator calc() 
    {
        // mapの初期化
        if (!this.calclationResult.isEmpty()) this.calclationResult.clear();
        if (!this.rescueResult.isEmpty()) this.rescueResult.clear();

        // メッセージがなければ
        if (this.rescueCosts.isEmpty()) 
        {
            return this;
        }else{
            for(MessageCost mesCos : this.rescueCosts)
            {
                // コスト計算用マップが初期化状態もしくはキーがない場合
                if (this.calclationResult.isEmpty() || !this.calclationResult.containsKey(mesCos.getTargetID())) 
                {
                    this.calclationResult.put(mesCos.getTargetID(), 
                        new RescueCivilianCost(mesCos.getSenderID(), mesCos.cost()));
                // コストが現状より小さい場合
                }else if (this.calclationResult.get(mesCos.getTargetID()).rescueCost > mesCos.cost()) {
                    this.calclationResult.replace(mesCos.getTargetID(), 
                        new RescueCivilianCost(mesCos.getSenderID(), mesCos.cost()));
                }
            }
            // CommandPicker送信用のマップに変更
            for(EntityID id : this.calclationResult.keySet()) {
                this.rescueResult.put(this.calclationResult.get(id).agentID, id);
            }
            this.rescueCosts.clear();
        }

        return this;
    }


    @Override
    public FireTargetAllocator updateInfo(MessageManager messageManager) 
    {
        super.updateInfo(messageManager);

        // 受信処理
        // そのステップで受信した(前ステップで送信された)メッセージ一覧
        List<CommunicationMessage> messages = messageManager.getReceivedMessageList();
        for(CommunicationMessage mes : messages) 
        {
        // mesが市民の情報であれば
        if (mes instanceof MessageCivilian) 
        {
            MessageCivilian mesCiv = (MessageCivilian) mes;
            if(mesCiv.getHP() != 0 && mesCiv.getBuriedness() != 0)
            {
                MessageUtil.reflectMessage(this.worldInfo, mesCiv);
                if (this.worldInfo.getEntity(mesCiv.getSenderID()) instanceof AmbulanceTeam 
                    || this.worldInfo.getEntity(mesCiv.getSenderID()) instanceof FireBrigade) 
                {
                    if(!this.receivedAgentCivilians.contains(mesCiv.getAgentID())
                        || !this.finishreceivedCivilians.contains(mesCiv.getAgentID()))
                    {
                        this.receivedAgentCivilians.add(mesCiv.getAgentID());
                    }
                }
            }
        }
        // mesがコストの情報であれば
        if (mes instanceof MessageCost) {
            MessageCost mesCos = (MessageCost) mes;
            if (this.worldInfo.getEntity(mesCos.getSenderID()) instanceof FireBrigade) 
            {
                this.rescueCosts.add(mesCos);
            }
        }
        }

        // 送信処理
        // 埋没している市民の情報の送信
        for(EntityID res : this.receivedAgentCivilians) 
        {
            messageManager.addMessage(new MessageCivilian(true, (Civilian)this.worldInfo.getEntity(res)));
            this.finishreceivedCivilians.add(res);
        }
        this.receivedAgentCivilians.clear();

        return this;
    }

    private class RescueCivilianCost {
        EntityID agentID;
        int rescueCost;

        RescueCivilianCost(EntityID id, int cost) 
        {
            agentID = id;
            rescueCost = cost;
        }
    }
}