/** {@inheritDoc} */
  @Override
  public Mission doMission(LogBuilder lb) {
    lb.add(tag);
    String reason = invalidReason();
    if (isTargetReason(reason)) {
      return retargetMission(reason, lb);
    } else if (reason != null) {
      return lbFail(lb, false, reason);
    }

    for (; ; ) {
      // Go to the target.
      final Unit unit = getUnit();
      Unit.MoveType mt =
          travelToTarget(getTarget(), CostDeciders.avoidSettlementsAndBlockingUnits(), lb);
      switch (mt) {
        case MOVE: // Arrived
          break;

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

        case MOVE_NO_ACCESS_EMBARK:
        case MOVE_NO_TILE:
          return this;

        default:
          return lbMove(lb, mt);
      }

      // Cash in now if:
      // - already in Europe
      // - or can never get there
      // - it is free to transport the treasure
      // - or there is no potential carrier to get the treasure to there
      // Otherwise, it is better to send to Europe.
      lbAt(lb);
      final AIUnit aiUnit = getAIUnit();
      final Europe europe = getUnit().getOwner().getEurope();
      if (unit.canCashInTreasureTrain()) {
        AIUnit aiCarrier = null;
        boolean cashin = unit.isInEurope() || europe == null || unit.getTransportFee() == 0;
        if (!cashin && aiUnit.getTransport() == null) {
          cashin = assignCarrier(lb) == null;
        }
        if (cashin)
          return (AIMessage.askCashInTreasureTrain(aiUnit))
              ? lbDone(lb, false, "cashed in")
              : lbFail(lb, false, "cashin");
      }
      return retargetMission("transport expected", lb);
    }
  }