/*
 *     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.money;

import java.math.BigDecimal;
import java.util.logging.Level;
import java.util.logging.Logger;

import ua.in.hutorny.otj.exer1.exeptions.NotEnoughCash;

public final class Cash {
	private BigDecimal value;
	private final Currency currency;
	
	/**
	 * Valuable cash creation is a subject of regulation, no public constructor given 
	 */
	Cash(BigDecimal value, Currency currency) {
		this.value = value;
		this.currency = currency;
	}

	/**
	 * Zero-value cash can be created by anyone 
	 */	
	public Cash(Currency currency) {
		this.value = new BigDecimal(0);
		this.currency = currency;
	}
	/**
	 * Specific of the add operation executed on Cash is that
	 * the addendum is being wiped out 
	 */
	public synchronized final void add(Cash cash) {
		if( ! isValidArgument(cash) ) return;
		value = value.add(cash.value);
		cash.value = new BigDecimal(0);
	}
	/**
	 * One way of creating a Cash instance is to subtract some amount
	 * from existing Cash 
	 */
	public final synchronized Cash subtract(BigDecimal amount) throws NotEnoughCash {
		if( value.compareTo(amount) < 0  )
			throw new NotEnoughCash(currency.name());
		value = value.subtract(amount);
		return new Cash(amount,currency);
	}
		
	public final Cash subtract(double amount) throws NotEnoughCash {
		return subtract(new BigDecimal(amount));
	}
	public final Cash subtract(int amount) throws NotEnoughCash {
		return subtract(new BigDecimal(amount));		
	}
	
	@Override
	protected Object clone() throws CloneNotSupportedException {
		throw new CloneNotSupportedException();
	}

	@Override
	public boolean equals(Object obj) {
		if( this == obj ) return true;
		if( !( obj instanceof Cash )) return false;
		Cash cash = (Cash) obj;
		return  currency.equals(cash.currency) && cash.value.equals(value);
	}

	@Override
	protected void finalize() throws Throwable {
		if( null != value && value.signum() != 0 ) {
			logError("Cash in the garbage " + toString());
		}
		super.finalize();
	}

	private void logError(String string) {
		Logger log = Logger.getLogger(getClass().getCanonicalName()); 
		log.log(Level.SEVERE, string);
	}

	@Override
	public int hashCode() {
		return value.hashCode() + currency.hashCode() * 27;
	}

	@Override
	public String toString() {		
		return value.toString() + " " + currency.toString();
	}

	private boolean isValidArgument(Cash cash) {
		if( null == cash || cash.value.signum() == 0 )
			return false;
		if( cash.value.signum() < 0)
			throw new IllegalArgumentException("Negative cash '" + cash.value.toString() + "';");
		if( ! cash.currency.equals(currency) )
			throw new IllegalArgumentException("Expected currency '" + currency.name() + "' received currency '" + cash.currency.name() + "';");
		return true;
	}
	
	final synchronized void discard() {
		value = null;
	}
	
	public Currency currency() {
		return currency;
	}
	public BigDecimal value() {
		return value;
	}
}
