/**
   * Assign a carrier for this treasure.
   *
   * @param lb A <code>LogBuilder</code> to log to.
   * @return A suitable carrier <code>AIUnit</code>, to which this unit has been queued for
   *     transport.
   */
  private AIUnit assignCarrier(LogBuilder lb) {
    final AIUnit aiUnit = getAIUnit();
    final Unit unit = getUnit();
    final Player player = unit.getOwner();
    final Europe europe = player.getEurope();

    List<Unit> carriers = player.getCarriersForUnit(unit);
    if (carriers.isEmpty()) return null;

    // Pick the closest carrier and queue this unit.
    final Location here = unit.getLocation();
    int turns = INFINITY;
    Unit closest = null;
    for (Unit c : carriers) {
      int t = c.getTurnsToReach(here);
      if (turns > t) {
        turns = t;
        closest = c;
      }
    }
    final AIMain aiMain = getAIMain();
    TransportMission tm;
    AIUnit aiCarrier;
    if (closest != null
        && (aiCarrier = aiMain.getAIUnit(closest)) != null
        && (tm = aiCarrier.getMission(TransportMission.class)) != null) {
      setTarget(europe);
      aiUnit.changeTransport(aiCarrier);
      if (tm.forceCollection(aiUnit, lb)) {
        lb.add(" forced collection on ", aiCarrier.getUnit());
        return aiCarrier;
      }
    }
    return null;
  }
 /**
  * Why would an IndianDemandMission be invalid with the given unit and colony.
  *
  * @param aiUnit The <code>AIUnit</code> to test.
  * @param colony The <code>Colony</code> to test.
  * @return A reason why the mission would be invalid with the unit and colony or null if none
  *     found.
  */
 private static String invalidColonyReason(AIUnit aiUnit, Colony colony) {
   String reason = invalidTargetReason(colony);
   if (reason != null) return reason;
   final Unit unit = aiUnit.getUnit();
   final Player owner = unit.getOwner();
   Player targetPlayer = colony.getOwner();
   switch (owner.getStance(targetPlayer)) {
     case UNCONTACTED:
     case PEACE:
     case ALLIANCE:
       return "bad-stance";
     case WAR:
     case CEASE_FIRE:
       Tension tension = unit.getHomeIndianSettlement().getAlarm(targetPlayer);
       if (tension != null && tension.getLevel().compareTo(Tension.Level.CONTENT) <= 0)
         return "happy";
       break;
   }
   return null;
 }
  /**
   * Find a suitable cash in location for this unit.
   *
   * @param aiUnit The <code>AIUnit</code> to execute this mission.
   * @param range The maximum number of moves to search.
   * @param deferOK Enables deferring to a fallback colony.
   * @return A <code>PathNode</code> to the target, or null if not found which includes the case
   *     when Europe should be preferred (because the unit can not get there by itself).
   */
  private static PathNode findTargetPath(AIUnit aiUnit, int range, boolean deferOK) {
    if (invalidAIUnitReason(aiUnit) != null) return null;
    final Unit unit = aiUnit.getUnit();
    final Location start = unit.getPathStartLocation();
    final Player player = unit.getOwner();
    final Europe europe = player.getEurope();
    final Unit carrier = unit.getCarrier();
    final CostDecider standardCd = CostDeciders.avoidSettlementsAndBlockingUnits();
    PathNode path;

    if (player.getNumberOfSettlements() <= 0 || start == null) {
      // No settlements or not on the map, so go straight to
      // Europe.  If Europe does not exist then this mission is
      // doomed.
      return (europe == null)
          ? null
          : unit.findPath(unit.getLocation(), europe, carrier, standardCd);
    }

    // Can the unit get to a cash in site?
    return unit.search(start, getGoalDecider(aiUnit, deferOK), standardCd, range, carrier);
  }
  /** {@inheritDoc} */
  @Override
  public Mission doMission(LogBuilder lb) {
    lb.add(tag);
    String reason = invalidReason();
    if (reason != null) return lbFail(lb, false, reason);

    final AIUnit aiUnit = getAIUnit();
    final Unit unit = getUnit();
    final IndianSettlement is = unit.getHomeIndianSettlement();
    Direction d;

    while (!this.demanded) {
      Unit.MoveType mt = travelToTarget(getTarget(), null, lb);
      switch (mt) {
        case MOVE_HIGH_SEAS:
        case MOVE_NO_MOVES:
        case MOVE_ILLEGAL:
          return lbWait(lb);

        case MOVE_NO_REPAIR:
          return lbFail(lb, false, AIUNITDIED);

        case MOVE_NO_TILE:
          return this;

        case ATTACK_SETTLEMENT: // Arrived?
          d = unit.getTile().getDirection(getTarget().getTile());
          if (d != null) break; // Yes, arrived at target
          // Fall through
        case ATTACK_UNIT: // Something is blocking our path
          Location blocker = resolveBlockage(aiUnit, getTarget());
          if (blocker == null) {
            moveRandomly(tag, null);
            continue;
          }
          d = unit.getTile().getDirection(blocker.getTile());
          if (AIMessage.askAttack(aiUnit, d)) {
            return lbAttack(lb, blocker);
          }
          continue;

        default:
          return lbMove(lb, mt);
      }

      // Load the goods.
      lbAt(lb);
      Colony colony = (Colony) getTarget();
      Player enemy = colony.getOwner();
      Goods goods = selectGoods(colony);
      GoodsType type = (goods == null) ? null : goods.getType();
      int amount = (goods == null) ? 0 : goods.getAmount();
      if (goods == null) {
        if (!enemy.checkGold(1)) {
          return lbDone(lb, false, "empty handed");
        }
        amount = enemy.getGold() / 20;
        if (amount == 0) amount = enemy.getGold();
      }
      this.demanded = AIMessage.askIndianDemand(aiUnit, colony, type, amount);
      if (this.demanded && (goods == null || hasTribute())) {
        if (goods == null) {
          return lbDone(lb, false, "accepted tribute ", amount, " gold");
        }
        lb.add(", accepted tribute ", goods);
        return lbRetarget(lb);
      }

      // Consider attacking if not content.
      int unitTension = (is == null) ? 0 : is.getAlarm(enemy).getValue();
      int tension = Math.max(unitTension, unit.getOwner().getTension(enemy).getValue());
      d = unit.getTile().getDirection(colony.getTile());
      if (tension >= Tension.Level.CONTENT.getLimit() && d != null) {
        if (AIMessage.askAttack(aiUnit, d)) lbAttack(lb, colony);
      }
      return lbDone(lb, false, "refused at ", colony);
    }

    // Take the goods home
    for (; ; ) {
      Unit.MoveType mt =
          travelToTarget(getTarget(), CostDeciders.avoidSettlementsAndBlockingUnits(), lb);
      switch (mt) {
        case MOVE: // Arrived
          break;

        case MOVE_HIGH_SEAS:
        case MOVE_NO_MOVES:
        case MOVE_ILLEGAL:
          return lbWait(lb);

        case MOVE_NO_REPAIR:
          return lbFail(lb, false, AIUNITDIED);

        case MOVE_NO_TILE:
          return this;

        default:
          return lbMove(lb, mt);
      }

      // Unload the goods
      lbAt(lb);
      GoodsContainer container = unit.getGoodsContainer();
      for (Goods goods : container.getCompactGoods()) {
        Goods tribute = container.removeGoods(goods.getType());
        is.addGoods(tribute);
      }
      return lbDone(lb, false, "unloaded tribute");
    }
  }