/*
 * Money.java
 *
 * Created on January 15, 2003, 8:34 PM
 */

package org.ipov.util;

import java.util.Currency;
/** 
 * Implements a (currently) simple Money Class.
 * @author  sanders
 */
public class Money {
    private long amount = 0;
    private Currency currency = null;
    
    private static final int[] cents = new int[] {1, 10, 100, 1000};
    
    public Money() {
        // Default constructor for OJB.
    }
    
    public Money(long amount, Currency currency) {
        this.currency = currency;
        this.amount = amount;
    }
    
    public Money(double amount, Currency currency) {
        this.currency = currency;
        this.amount = Math.round(amount * centFactor());
    }
    
    private int centFactor() {
        return cents[ currency.getDefaultFractionDigits() ];
    }
    
    public java.math.BigDecimal getAmount() {
        return java.math.BigDecimal.valueOf( this.amount, currency.getDefaultFractionDigits() );
    }
    
    public Currency getCurrency() {
        return this.currency;
    }
    
    public static Money dollars(long amount) {
        return new Money(amount, Currency.getInstance("USD"));
    }
    
    public static Money dollars(double amount) {
        return new Money(amount, Currency.getInstance("USD"));
    }
    
    public boolean equals(Object other) {
        return ((other instanceof Money) && (equals((Money)other)));
    }
    
    public boolean equals(Money other) {
        return ((currency.equals(other.currency)) && (amount == other.amount));
    }
    
    public int hasCode() {
        return (int)(amount ^ (amount >>> 32));
    }
    
    private Money newMoney(long amount) {
        return new Money(amount, this.currency);
    }
    
    private void assertSameCurrencyAs(Money other) {
        if (! currency.equals(other)) {
            throw new RuntimeException("Error : Money Objects of different Currencies.");
        }
    }
    
    public Money add(Money other) {
        assertSameCurrencyAs(other);
        return newMoney(amount + other.amount);
    }
    
    public Money subtract(Money other) {
        assertSameCurrencyAs(other);
        return newMoney(amount - other.amount);
    }
    
    public int compareTo(Object other) {
        return compareTo( (Money)other );
    }
    
    public int compareTo(Money other) {
        assertSameCurrencyAs(other);
        if (amount < other.amount) {
            return -1;
        } else if (amount == other.amount) {
            return 0;
        }
        return 1;
    }
    
    public boolean greaterThan(Money other) {
        return (compareTo(other) > 0);
    }
    
    public boolean lessThan(Money other) {
        return (compareTo(other) < 0);
    }
    
    public Money[] allocate(int n) {
        Money lowResult = newMoney( amount / n );
        Money highResult = newMoney( lowResult.amount + 1);
        Money[] results = new Money[n];
        int remainder = (int) amount % n;
        for (int i = 0; i < remainder; i++) {
            results[i] = highResult;
        }
        for (int i = remainder; i < n; i++) {
            results[i] = lowResult;
        }
        return results;
    }
    
    public Money[] allocate(long[] ratios) {
        long total = 0;
        for (int i = 0; i < ratios.length; i++) {
            total += ratios[i];
        }
        long remainder = amount;
        Money[] results = new Money[ratios.length];
        for (int i = 0; i < results.length; i++) {
            results[i] = newMoney( amount * ratios[i] / total );
            remainder -= results[i].amount;
        }
        for (int i = 0; i < remainder; i++) {
            results[i].amount++;
        }
        return results;
    }
}
