package autumn_2023.centralized;

import static rescuecore2.standard.entities.StandardEntityURN.CIVILIAN;
import static rescuecore2.standard.entities.StandardEntityURN.REFUGE;

import adf.core.agent.action.Action;
import adf.core.agent.action.common.ActionMove;
import adf.core.agent.action.common.ActionRest;
import adf.core.agent.communication.MessageManager;
import adf.core.agent.communication.standard.bundle.centralized.CommandFire;
import adf.core.agent.communication.standard.bundle.centralized.MessageReport;
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.centralized.CommandExecutor;
import adf.core.component.extaction.ExtAction;
import adf.core.component.module.algorithm.PathPlanning;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import rescuecore2.standard.entities.Area;
import rescuecore2.standard.entities.Human;
import rescuecore2.standard.entities.StandardEntity;
import rescuecore2.worldmodel.EntityID;

public class CentralizedControlCommandExecutorFire extends CommandExecutor<CommandFire> {

  private static final int ACTION_UNKNOWN = -1;
  private static final int ACTION_REST = CommandFire.ACTION_REST;
  private static final int ACTION_MOVE = CommandFire.ACTION_MOVE;
  private static final int ACTION_RESCUE = CommandFire.ACTION_RESCUE;
  private static final int ACTION_AUTONOMY = CommandFire.ACTION_AUTONOMY;

  private PathPlanning pathPlanning;

  private ExtAction actionFireRescue;
  private ExtAction actionExtMove;

  private int commandType;
  private EntityID target;
  private EntityID commanderID;

  private boolean commandCompleted;

  public CentralizedControlCommandExecutorFire(AgentInfo ai, WorldInfo wi, ScenarioInfo si, ModuleManager moduleManager, DevelopData developData) {
    super(ai, wi, si, moduleManager, developData);
    this.commandType = ACTION_UNKNOWN;
    this.target = null;
    this.commanderID = null;
    switch (scenarioInfo.getMode()) {
      case PRECOMPUTATION_PHASE:
      case PRECOMPUTED:
      case NON_PRECOMPUTE:
        this.pathPlanning = moduleManager.getModule(
            "DefaultCommandExecutorFire.PathPlanning",
            "adf.impl.module.algorithm.DijkstraPathPlanning");
        this.actionFireRescue = moduleManager.getExtAction(
            "DefaultCommandExecutorFire.ExtActionFireRescue",
            "adf.impl.extaction.DefaultExtActionFireRescue");
        this.actionExtMove = moduleManager.getExtAction(
            "DefaultCommandExecutorFire.ExtActionMove",
            "adf.impl.extaction.DefaultExtActionMove");
        break;
    }
  }

  /**
   * 受信した司令を入力するためのメソッド
   * @param command 受信した司令
   */
  @Override
  public CommandExecutor setCommand(CommandFire command) {
    EntityID agentID = this.agentInfo.getID();
    if(!this.commandCompleted) return this;
    if (command.isToIDDefined() && Objects.requireNonNull(command.getToID()).getValue() == agentID.getValue()) {
      this.commandType = command.getAction();// 司令で出された行動内容
      this.target = command.getTargetID();// タスクとなる対象のID
      this.commanderID = command.getSenderID();// 司令を出した司令所のID
    }
    return this;
  }

  /**
   * 現在実行している司令が完了しているかを判定する
   * 完了しているなら，消防司令所へ救助完了の報告を送る
   */
  @Override
  public CommandExecutor updateInfo(MessageManager messageManager) {
    super.updateInfo(messageManager);
    if (this.getCountUpdateInfo() >= 2) {
      return this;
    }
    this.pathPlanning.updateInfo(messageManager);
    this.actionFireRescue.updateInfo(messageManager);
    this.actionExtMove.updateInfo(messageManager);

    this.commandCompleted = this.isCommandCompleted(this.commandType);
    if (this.commandCompleted) {
      if (this.commandType != ACTION_UNKNOWN) {
        messageManager.addMessage(new MessageReport(true, true, false, this.target));

        this.commandType = ACTION_UNKNOWN;
        this.target = null;
        this.commanderID = null;
      }
    }
    return this;
  }


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


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


  @Override
  public CommandExecutor preparate() {
    super.preparate();
    if (this.getCountPreparate() >= 2) {
      return this;
    }
    this.pathPlanning.preparate();
    this.actionFireRescue.preparate();
    this.actionExtMove.preparate();
    return this;
  }

  /**
   * 受け取った司令を元に行動を決定・実行するためのメソッド
   * 
   * 司令で出された行動内容によって救助活動を決定する
   * ※実装では司令で出される行動内容はACTION_AUTONOMYになっている
   */
  @Override
  public CommandExecutor calc() {
    this.result = null;
    switch (this.commandType) {
      case ACTION_REST:
        EntityID position = this.agentInfo.getPosition();
        if (this.target == null) {
          Collection<EntityID> refuges = this.worldInfo.getEntityIDsOfType(REFUGE);
          if (refuges.contains(position)) {
            this.result = new ActionRest();
          } else {
            this.pathPlanning.setFrom(position);
            this.pathPlanning.setDestination(refuges);
            List<EntityID> path = this.pathPlanning.calc().getResult();
            if (path != null && path.size() > 0) {
              this.result = new ActionMove(path);
            } else {
              this.result = new ActionRest();
            }
          }
          return this;
        }
        if (position.getValue() != this.target.getValue()) {
          List<EntityID> path = this.pathPlanning.getResult(position,
              this.target);
          if (path != null && path.size() > 0) {
            this.result = new ActionMove(path);
            return this;
          }
        }
        this.result = new ActionRest();
        return this;
      case ACTION_MOVE:
        if (this.target != null) {
          this.result = this.actionExtMove.setTarget(this.target).calc().getAction();
        }
        return this;
      case ACTION_RESCUE:
        if (this.target != null) {
          this.result = this.actionFireRescue.setTarget(this.target).calc().getAction();
        }
        return this;
      case ACTION_AUTONOMY:
        if (this.target == null) {
          return this;
        }
        StandardEntity targetEntity = this.worldInfo.getEntity(this.target);
        if (targetEntity instanceof Area) {
          this.result = this.actionExtMove.setTarget(this.target).calc().getAction();
        } else if (targetEntity instanceof Human) {
          this.result = this.actionFireRescue.setTarget(this.target).calc().getAction();
        }
    }
    return this;
  }

  /**
   * 司令が完了したかを判定するメソッド
   * 
   * @param commandType 司令の種類
   * @return true : 完了 false : 実行中
   */
  private boolean isCommandCompleted(int commandType) {
    Human agent = (Human) this.agentInfo.me();
    switch (commandType) {
      case ACTION_REST:
        if (this.target == null) {
          return (agent.getDamage() == 0);
        }
        if (Objects.requireNonNull(this.worldInfo.getEntity(this.target))
            .getStandardURN() == REFUGE) {
          if (agent.getPosition().getValue() == this.target.getValue()) {
            return (agent.getDamage() == 0);
          }
        }
        return false;
      case ACTION_MOVE:
        return this.target == null || this.agentInfo.getPosition()
            .getValue() == this.target.getValue();
      case ACTION_RESCUE:
        if (this.target == null) {
          return true;
        }
        Human human = (Human) Objects.requireNonNull(this.worldInfo.getEntity(this.target));
        return human.isBuriednessDefined() && human.getBuriedness() == 0
            || (human.isHPDefined() && human.getHP() == 0);
        
      case ACTION_AUTONOMY:
        if (this.target != null) {
          StandardEntity targetEntity = this.worldInfo.getEntity(this.target);
          if (targetEntity instanceof Area) {
            commandType = ACTION_MOVE;
            return this.isCommandCompleted(commandType);
          } else if (targetEntity instanceof Human) {
            commandType = ACTION_RESCUE;
            return this.isCommandCompleted(commandType);
          }
        }
        return true;
    }
    return true;
  }
}