/*
 *     Copyright: Eugene M. Hutorny (c) 2009, eugene@hutorny.in.ua
 *     License:   Licensed to public under The GNU Lesser General Public License (LGPLv3) 
 *                http://www.opensource.org/licenses/lgpl-3.0.html
 */
package ua.in.hutorny.otj.exer1.bank;
import java.math.BigDecimal;

import ua.in.hutorny.otj.exer1.accounting.Account;
import ua.in.hutorny.otj.exer1.exeptions.NotEnoughCash;
import ua.in.hutorny.otj.exer1.money.Cash;
import ua.in.hutorny.otj.exer1.money.Currency;
import ua.in.hutorny.otj.exer1.money.Exchequer;
import ua.in.hutorny.otj.exer1.money.Treasury;


public class Bank {

	/*
	 * Creation of a bank should be a collaboration between founders and authorities
	 * but it is not in the scope of this exercise
	 */
	public Bank(String name, Currency nativeCurrency) {
		super();
		this.name = name;
		this.safe = new Safe();
		this.operationalAccount = new BankAccount(nativeCurrency);
		fillTheSafe();
	}

	/*
	 * BankAccount shall not escape the Bank 
	 */
	protected class BankAccount extends Account {

		protected BankAccount(BigDecimal deposit, Currency currency) {
			super(deposit, currency);
		}

		protected BankAccount(Currency currency) {
			super(currency);
		}
		protected Bank bank() {
			return Bank.this;
		}
		
	}
	
	public Account openAccount(Cash deposit) {
		Account account = new BankAccount(deposit.value(),deposit.currency());
		safe.put(deposit);
		return account;
	}
	
	public Account openCreditAccount(int credit, Currency currency) {
		return new BankAccount(new BigDecimal(credit),currency);		
	}
	    
    public BigDecimal internalConversion(Currency from, BigDecimal amount){
    	return new BigDecimal(amount.doubleValue() * INTERNAL_RATES[from.ordinal()]);
    }

	protected final class Safe extends Treasury {
		
	}
    
	private void fillTheSafe() {
		for(Currency i : Currency.values()) {
			safe.put(Exchequer.acquireCash(2000000, i));
		}
	}
    
    
    protected Account operationalAccount() { 
    	return operationalAccount; 
    }
    public String report() {
		return  toString() + "{" + operationalAccount.toString() + "; " + safe.toString() + "}";
    }
    @Override
    public String toString() {
    	return getClass().getSimpleName() + "(" + name +")";
    }
    
    public WireTransfer wireOperator(Account creditAccount) {
    	validateAccount(creditAccount);
    	return new WireTransferCollaboration(this,validateAccount(creditAccount), WIRE_TRANSFER_RATE);
    	/* The following line causes NoSuchMethodError exception
    	 * Exception in thread "main" java.lang.NoSuchMethodError: ua.in.hutorny.otj.exer1.accounting.Account._OT$decaps$credit(Ljava/math/BigDecimal;)V
    	 */
    	//return new WireTransferCollaboration2(this,validateAccount(creditAccount), WIRE_TRANSFER_RATE); 
    }
    
    private BankAccount validateAccount(Account account) {
    	if( ! (account instanceof BankAccount) ) {
    		throw new IllegalArgumentException("Inappropriate account: '" + account + "'");
    	}
    	BankAccount bankAccount = (BankAccount) account;
    	if( this != bankAccount.bank() ) {
    		throw new IllegalArgumentException("Inappropriate bank account: '" + account + "'");    		
    	}    	
    	return bankAccount;
	}

	public Cashier cashier(Account account) {
		validateAccount(account);
    	return new GetCashFromBankCollaboration(this,validateAccount(account),CASHING_INTEREST_RATE);
		/* The following line causes NoSuchMethodError exception
		 * Exception in thread "main" java.lang.NoSuchMethodError: ua.in.hutorny.otj.exer1.accounting.Account._OT$decaps$credit(Ljava/math/BigDecimal;)V
		 */
    	//return new GetCashFromBankCollaboration2(this,validateAccount(account),CASHING_INTEREST_RATE);
    }

	Cash acquireCash(BigDecimal amount, Currency currency) throws NotEnoughCash {
		return safe.get(amount, currency);
	}

	void returnCash(Cash cash) {
		safe.put(cash);
	}

	protected static final double[] INTERNAL_RATES = new double[] { 1.0, 0.714, 0.71 }; 
    protected static final double CASHING_INTEREST_RATE = 0.03; 
    protected static final double WIRE_TRANSFER_RATE = 0.01;
	private final Safe safe;
	private String name;
    private final Account operationalAccount;
	
}

