package edu.columbia.cs.cs1007.banking;

/**
 * The class BankAccount implements an interest bank account.
 
 @author Julia Stoyanovich (jds2109@columbia.edu)
 *        COMS 1007, Summer 2009
 
 */
import java.util.ArrayList;
import java.util.Date;

import edu.columbia.cs.cs1007.banking.Transaction.TRANSACTION_TYPE;

public class BankAccount {
  
  /* Constants */
  private static final double DEFAULT_INTEREST_RATE = 0.03
  private static final int DAYS_IN_YEAR = 365;
  private static final int BALANCE_HISTORY_LENGTH = 100;
  
  /* Instance variables */
  private int _accountNumber;
  private double _balance;
  private Person _owner;

  // annual percentage rate for interest accounts
  private double _interestRate;
  
  // balances and transactions
  private ArrayList<Transaction> _transactionHistory;
  private double[] _recentBalances;

  /**
   * Constructor method.
   @param accountNumber - account number
   @param owner
   */
  public BankAccount(int accountNumber, Person owner) {
    _accountNumber = accountNumber;
    _owner = new Person(owner);
    _interestRate = DEFAULT_INTEREST_RATE;
    _balance = 0.0;
    _transactionHistory = new ArrayList<Transaction>();
    _recentBalances = new double[BALANCE_HISTORY_LENGTH];
  }

  /**
   * Constructor method.
   @param accountNumber
   @param owner
   @param balance
   */
  public BankAccount(int accountNumber, Person owner, double balance) {  
    this(accountNumber, owner);
    _balance = balance;
  }

  /**
   * Constructor method.
   @param accountNumber
   @param owner
   @param balance
   @param interestRate - annual percentage rate
   */
  public BankAccount(int accountNumber, Person owner,  double balance, double interestRate) {  
    this(accountNumber, owner, balance);
    _interestRate = interestRate;
  }

  /**
   * Get the current account balance.
   @return balance
   */
  public double checkBalance() {
    return _balance;
  }

  /**
   * Mutator method that sets account balance to the given value.
   @param balance
   */
  public void setBalance(double balance) {
    _balance = balance;
  }

  /**
   * Get the current annual percentage rate.
   @return interest rate
   */
  public double getInterestRate() {
    return _interestRate;
  }
  
  /**
   * Mutator that sets the current annual percentage rate to the given value.
   @param interestRate
   */  
  public void setInterestRate(double interestRate) {
    _interestRate = interestRate;
  }
  
  /* Methods that implement various aspects of account maintenance. */
  
  /**
   * Method that checks whether a bank account is overdrawn.
   @return returns true if account is overdrawn, false otherwise
   */
  public boolean isOverdrawn() {
    boolean overdrawn = (_balance < 0);
    return overdrawn;
  }

  /**
   * Record current balance in the balance history data structure.
   */
  public void recordBalance() {
    
    // move all balances to the right by one position
    for (int i=_recentBalances.length-2; i >= 0; i--) {
        _recentBalances[i+1= _recentBalances[i];
    }
    
    // place current balance at the beginning of the array
    _recentBalances[0= _balance;
  }

  /**
   * Record transaction in the transaction history data structure.
   @param transaction - transaction to be recorded
   */
  public void recordTransaction(Transaction transaction) {
    _transactionHistory.add(new Transaction(transaction));
  }
  
  /**
   * Execute the transaction.
   @param transaction - transaction to be executed.
   @return true if transaction was executed successfully, false if it failed.
   */
  public boolean executeTransaction(Transaction transaction) {
    
    boolean greatSuccess = true;

    if (transaction.getType() == TRANSACTION_TYPE.DEPOSIT) {
      greatSuccess = deposit(transaction.getAmount());

    else if (transaction.getType() == TRANSACTION_TYPE.WITHDRAWAL) {
      greatSuccess = withdraw(transaction.getAmount());
      
    else if (transaction.getType() == TRANSACTION_TYPE.INTEREST) {
      greatSuccess = compoundDailyInterest();
      transaction.setAmount(_interestRate / DAYS_IN_YEAR);      
    }

    if (greatSuccess) {
      recordTransaction(transaction);
      recordBalance();
    }
    return greatSuccess;
  }
  
  /**
   * Update the current account balance by the interest earned in one day.
   * Interest is compounded only if the beginning balance was positive.  
   @return true if the balance was updated, false otherwise.
   */
  public boolean compoundDailyInterest() {

    boolean greatSuccess = false;
    
    if (_balance > 0) {
      double dailyInterest = _interestRate / DAYS_IN_YEAR;
      _balance += _balance * dailyInterest;
      greatSuccess = true;
    }
    return greatSuccess;
  }

  /**
   * Deposit the specified amount into the account.
   @param amount to be deposited
   @return true
   */
  public boolean deposit(double amount) {
    boolean greatSuccess = true;
    
    _balance += amount;
    return greatSuccess;
  }
  
  /**
   * Withdraw the specified amount from the account.  The amount is withdrawn
   * only if the resulting account balance is non-negative.
   @param amount to be withdrawn
   @return true if withdrawal succeeded, false otherwise
   */
  public boolean withdraw(double amount) {
    
    boolean greatSuccess = true;
    
    if _balance - amount >= 0) {
      _balance -= amount;
    else {
      greatSuccess = false;
    }
    return greatSuccess;
  }
  
  /** 
   * Generate a string representation of the bank account.
   @return stringRepresentation
   */
  public String toString() {

    String result = "#" + _accountNumber + ", balance " + _balance + ", interest rate " + _interestRate;
    result += "\nOwner: " + _owner.toString();
    return result;
  }
  
  public static void main(String[] args) {
    
    double balance = 500;
    int accountNumber = 101;
    
    Address juliasAddress = new Address("Amsterdam Avenue""1214""New York""NY""10027-7003""USA");
    Person julia = new Person("Julia""Stoyanovich""jds2109@columbia.edu", juliasAddress);
  
    BankAccount acct1 = new BankAccount(accountNumber, julia, balance);
    System.out.println(acct1.toString());
    
    acct1.executeTransaction(new Transaction(TRANSACTION_TYPE.DEPOSIT, 100new Date()));
    System.out.println(acct1.toString());

    acct1.executeTransaction(new Transaction(TRANSACTION_TYPE.INTEREST, 0.0003new Date()));
    System.out.println(acct1.toString());

    balance = acct1.checkBalance();
    boolean outcome = acct1.executeTransaction(new Transaction(TRANSACTION_TYPE.WITHDRAWAL, * balance, new Date()));
    if (outcome) {
      System.out.println(acct1.toString());
    else {
      System.out.println("Withdrawal did not succeed");
    }
  }
}