/**
   * Simulate processing of a withdrawal
   *
   * @param message the message describing the withdrawal requested
   * @param balances (out) balances in account after withdrawal
   * @return status code derived from current values
   */
  private Status withdrawal(Message message, Balances balances) {
    int cardNumber = message.getCard().getNumber();

    int accountNumber = ACCOUNT_NUMBER[cardNumber][message.getFromAccount()];
    if (accountNumber == 0) return new Failure("Invalid account type");

    Money amount = message.getAmount();

    Money limitRemaining = new Money(DAILY_WITHDRAWAL_LIMIT);
    limitRemaining.subtract(WITHDRAWALS_TODAY[cardNumber]);
    if (!amount.lessEqual(limitRemaining)) return new Failure("Daily withdrawal limit exceeded");

    if (!amount.lessEqual(AVAILABLE_BALANCE[accountNumber]))
      return new Failure("Insufficient available balance");

    // Update withdrawals today and account balances once we know everything is
    // OK

    WITHDRAWALS_TODAY[cardNumber].add(amount);
    BALANCE[accountNumber].subtract(amount);
    AVAILABLE_BALANCE[accountNumber].subtract(amount);

    // Return updated balances

    balances.setBalances(BALANCE[accountNumber], AVAILABLE_BALANCE[accountNumber]);

    return new Success();
  }
  /**
   * Simulate processing of a transfer
   *
   * @param message the message describing the transfer requested
   * @param balances (out) balances in "to" account after transfer
   * @return status code derived from current values
   */
  private Status transfer(Message message, Balances balances) {
    int cardNumber = message.getCard().getNumber();

    int fromAccountNumber = ACCOUNT_NUMBER[cardNumber][message.getFromAccount()];
    if (fromAccountNumber == 0) return new Failure("Invalid from account type");

    int toAccountNumber = ACCOUNT_NUMBER[cardNumber][message.getToAccount()];
    if (toAccountNumber == 0) return new Failure("Invalid to account type");
    if (fromAccountNumber == toAccountNumber)
      return new Failure("Can't transfer money from\n" + "an account to itself");

    Money amount = message.getAmount();

    if (!amount.lessEqual(AVAILABLE_BALANCE[fromAccountNumber]))
      return new Failure("Insufficient available balance");

    // Update account balances once we know everything is OK

    BALANCE[fromAccountNumber].subtract(amount);
    AVAILABLE_BALANCE[fromAccountNumber].subtract(amount);
    BALANCE[toAccountNumber].add(amount);
    AVAILABLE_BALANCE[toAccountNumber].add(amount);

    // Return updated balances

    balances.setBalances(BALANCE[toAccountNumber], AVAILABLE_BALANCE[toAccountNumber]);

    return new Success();
  }
  /**
   * Simulate processing of an inquiry
   *
   * @param message the message describing the inquiry requested
   * @param balances (out) balances in account
   * @return status code derived from current values
   */
  private Status inquiry(Message message, Balances balances) {
    int cardNumber = message.getCard().getNumber();

    int accountNumber = ACCOUNT_NUMBER[cardNumber][message.getFromAccount()];
    if (accountNumber == 0) return new Failure("Invalid account type");

    // Return requested balances

    balances.setBalances(BALANCE[accountNumber], AVAILABLE_BALANCE[accountNumber]);

    return new Success();
  }
  /**
   * Simulate completion of a deposit
   *
   * @param message the message describing the deposit requested
   * @param balances (out) balances (not updated until completed)
   * @return status code - must always be success in this case
   */
  private Status completeDeposit(Message message, Balances balances) {
    int cardNumber = message.getCard().getNumber();

    int accountNumber = ACCOUNT_NUMBER[cardNumber][message.getToAccount()];
    if (accountNumber == 0) return new Failure("Invalid account type");

    // Now we can update the balance

    Money amount = message.getAmount();
    BALANCE[accountNumber].add(amount);

    // Return updated balances

    balances.setBalances(BALANCE[accountNumber], AVAILABLE_BALANCE[accountNumber]);

    return new Success();
  }