package autumn_2023.module.complex;

import adf.core.agent.communication.MessageManager;
import adf.core.agent.communication.standard.bundle.StandardMessagePriority;
import adf.core.agent.communication.standard.bundle.centralized.CommandAmbulance;
import adf.core.agent.communication.standard.bundle.information.MessageCivilian;
import adf.core.agent.info.*;
import adf.core.agent.precompute.PrecomputeData;
import adf.core.component.communication.CommunicationMessage;
import adf.core.component.module.complex.Search;
import adf.core.component.module.algorithm.Clustering;
import adf.core.agent.module.ModuleManager;
import adf.core.agent.develop.DevelopData;
import rescuecore2.standard.entities.*;
import rescuecore2.worldmodel.EntityID;
import java.util.*;
import java.util.stream.Collector;
import java.util.stream.Collectors;

import static java.lang.Double.POSITIVE_INFINITY;
import static rescuecore2.standard.entities.StandardEntityURN.AMBULANCE_TEAM;

public class SampleSearch extends Search
{
  private EntityID result;
  private Clustering clustering;
  private Set<EntityID> searchedSet = new HashSet<>();
  private List<EntityID> list;
  private MessageManager messageManager;
  private static final int ACTION_LOAD = CommandAmbulance.ACTION_LOAD;

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

    String clusteringKey = "SampleSearch.Clustering.Ambulance";
    this.clustering =
            mm.getModule(clusteringKey,
                    "adf.sample.module.algorithm.SampleKMeans");
    this.registerModule(this.clustering);
  }

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

  @Override
  public Search updateInfo(MessageManager mm)
  {
    super.updateInfo(mm);
    if (this.getCountUpdateInfo() > 1) return this;

    // そのステップで受信した(前ステップで送信された)メッセージ一覧
    List<CommunicationMessage> messages = mm.getReceivedMessageList();
    this.messageManager = mm;

    int idx = this.clustering.getClusterIndex(this.agentInfo.me());
    Collection<EntityID> cluster = this.clustering.getClusterEntityIDs(idx);
    if (cluster == null) return this;

    // エージェントが動けるステップではないなら
    if(this.agentInfo.getTime() < this.scenarioInfo.getKernelAgentsIgnoreuntil()) return this;
    // エージェントが動けるステップのはじめに
    if(this.agentInfo.getTime() == this.scenarioInfo.getKernelAgentsIgnoreuntil()){
      this.list = new ArrayList<>(cluster);
    }
    

    // エージェントが現在位置する建物や道路のIDを取得する
    EntityID position = this.agentInfo.getPosition();
    // 探索済みの (辿り着いたことがある) 対象を候補から除外する
    this.searchedSet.add(position);

    // 探索済みの (辿り着いたことがある) 対象を候補から除外する
    list.removeAll(this.searchedSet);

    // クラスタ内の建物や道路のIDリストから建物のIDリストを取得
    List<EntityID> buildinglist = this.list.stream()// listのストリームを取得
    .map(this.worldInfo::getEntity)// EntityIDをStandardEntityに変換
    .filter(e -> (e instanceof Building))// StandardEntityオブジェクトのうちBuildingクラスのものだけ抽出
    .map(StandardEntity::getID)// StandardEntityをEntityIDに変換
    .collect(Collectors.toList());// ストリームをリストにして取得

    // すべての対象が探索済みの場合，探索済みリストをリセット候補をリセットする
    if (buildinglist.isEmpty()){
      this.searchedSet.clear();
      this.list = new ArrayList<>(cluster);
    }

    return this;

  }

  @Override
  public Search calc()
  {
    // エージェントが動けるステップではないなら
    if(this.agentInfo.getTime() < this.scenarioInfo.getKernelAgentsIgnoreuntil()) return this;
    
    EntityID me = this.agentInfo.getID();
    int idx = this.clustering.getClusterIndex(me);
    if (idx == -1) return this;

    Collection<EntityID> cluster = this.clustering.getClusterEntityIDs(idx);
    if (cluster == null) return this;

    //System.out.println("time:"+this.agentInfo.getTime()+" id:"+me+" target:"+this.getTarget());

    // エージェントが現在位置する建物や道路のIDを取得する
    EntityID position = this.agentInfo.getPosition();

    if (this.getTarget() != null && !position.equals(this.getTarget())) {
      return this;
    }
    
    // クラスタ内の建物や道路のIDリストから建物のIDリストを取得
    List<EntityID> buildinglist = list.stream()// listのストリームを取得
    .map(this.worldInfo::getEntity)// EntityIDをStandardEntityに変換
    .filter(e -> (e instanceof Building))// StandardEntityオブジェクトのうちBuildingクラスのものだけ抽出
    .map(StandardEntity::getID)// StandardEntityをEntityIDに変換
    .collect(Collectors.toList());// ストリームをリストにして取得

    double near = Double.MAX_VALUE;
    for (EntityID lists : buildinglist) {
      double distance = this.worldInfo.getDistance(position, lists);
      if (distance < near){
        near = distance;
        this.result = lists;
      }
    }

    return this;
  }
}