/*
 * This code is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public 
 * License as published by the Free Software Foundation; either 
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This code is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public 
 * License along with this program; if not, write to the Free 
 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, 
 * MA  02111-1307, USA.
 */
package no.geosoft.cc.currency;



import java.lang.ref.*;
import java.util.*;



/**
 * A currency manager object. The client machine must be online in order
 * for dynamic exchange rates to work.
 * 
 * 
 * @author <a href="mailto:info@geosoft.no">GeoSoft</a>
 */   
public class CurrencyManager
{
  private Currency        displayCurrency_;
  private CurrencyServer  currencyServer_;
  private Collection      listeners_;   // of WeakReference (TimeZoneListener)  
  

  /**
   * Create a currency manager with default currency server.
   */
  public CurrencyManager()
  {
    this (new SauderCurrencyServer());
  }


  
  /**
   * Create currency manager with the specified currency server.
   * 
   * @param currencyServer  Currency server.
   */
  public CurrencyManager (CurrencyServer currencyServer)
  {
    currencyServer_  = currencyServer;
    displayCurrency_ = null;
    listeners_       = null;
  }

  

  /**
   * Set the current "display currency". The "dipslay currency" feature is
   * optional, but useful for GUI clients that display money amounts 
   * in a user defined currency.
   * @see   #addCurrencyListener
   * 
   * @param currency  New display currency.
   */
  public void setDisplayCurrency (Currency currency)
  {
    if (currency.equals (displayCurrency_))
      return;
    
    displayCurrency_ = currency;

    notifyListeners();
  }


  
  /**
   * Get current "display currency".
   * 
   * @return  Current display currency.
   */
  public Currency getDisplayCurrency()
  {
    if (displayCurrency_ == null)
      displayCurrency_ = currencyServer_.getCurrency ("USD");

    return displayCurrency_;
  }

  

  /**
   * Notify listeners about change in display currency.
   */
  private void notifyListeners()
  {
    if (listeners_ != null) {
      for (Iterator i = listeners_.iterator(); i.hasNext(); ) {
        WeakReference reference = (WeakReference) i.next();
        CurrencyListener listener = (CurrencyListener) reference.get();
        if (listener == null)
          i.remove();
        else
          listener.currencyChanged();
      }
    }
  }
  

  
  /**
   * Get currency for specified ISO code. The present currency server
   * defines which currencies are defined.
   * 
   * @param isoCode  ISO code of currency to get.
   * @return         Requested currency (or null if not supported by the
   *                 current currency server.
   */
  public Currency getCurrency (String isoCode)
  {
    return currencyServer_.getCurrency (isoCode);
  }

  

  /**
   * Convert an amount in a given currency to base currency (US$).
   * 
   * @param amount    Amount to covert.
   * @param currency  Currency to convert from.
   * @return          Equivalent in base currency (US$) according to
   *                  exchange rates given by current currency server.
   */
  public double convertToBase (double amount, Currency currency)
  {
    return amount / currencyServer_.getExchangeRate (currency);
  }

  

  /**
   * Convert an amount in current display currency to base currency (US$).
   * 
   * @param amount  Amount to covert.
   * @return        Equivalent in base currency (US$) according to
   *                exchange rates given by current currency server.
   */
  public double getBaseAmount (double amount)
  {
    return convertToBase (amount, getDisplayCurrency());
  }

  
  
  /**
   * Convert an amount in base currency (US$) to the given currency.
   * 
   * @param amount    Amount to covert.
   * @param currency  Currency to convert to.
   * @return          Equivalent in given currency according to
   *                  exchange rates given by current currency server.
   */
  public double convertFromBase (double amount, Currency currency)
  {
    return amount * currencyServer_.getExchangeRate (currency);
  }


  
  /**
   * Convert a base amount (US$) to current display currency.
   * 
   * @param amount  Amount to covert.
   * @return        Equivalent in display currency according to
   *                exchange rates given by current currency server.
   */
  public double getDisplayAmount (double amount)
  {
    return convertFromBase (amount, getDisplayCurrency());
  }
  

  
  /**
   * Convert an amount between two currencies.
   * 
   * @param amount        Amount to convert.
   * @param fromCurrency  Currency to convert from.
   * @param toCurrency    Currency to convert to.
   * @return              Converted amount according to exchange
   *                      rates given by current currency server.
   */
  public double convert (double amount, Currency fromCurrency, Currency toCurrency)
  {
    double baseAmount = convertToBase (amount, fromCurrency);
    return convertFromBase (baseAmount, toCurrency);
  }


  
  /**
   * Add a display currency listener. The listener is notified when the
   * display currency changes.
   * 
   * @param currencyListener  Listener to add.
   */
  public void addCurrencyListener (CurrencyListener currencyListener)
  {
    if (listeners_ == null)
      listeners_ = new ArrayList();

    // Check to see if it is there already
    for (Iterator i = listeners_.iterator(); i.hasNext(); ) {
      WeakReference reference = (WeakReference) i.next();
      CurrencyListener listener = (CurrencyListener) reference.get();
      if (listener == currencyListener)
        return;
    }

    // Add the listener
    listeners_.add (new WeakReference (currencyListener));
  }

  

  /**
   * Remove a display currency listener.
   * 
   * @param currencyListener  Listener to remove.
   */
  public void removeCurrencyListener (CurrencyListener currencyListener)
  {
    if (listeners_ == null) return;

    for (Iterator i = listeners_.iterator(); i.hasNext(); ) {
      WeakReference reference = (WeakReference) i.next();
      CurrencyListener listener = (CurrencyListener) reference.get();
      if (listener == currencyListener) {
        i.remove();
        break;
      }
    }
  }


  
  /**
   * Testing this class.
   * 
   * @param args  Not used.
   */
  public static void main (String[] args)
  {
    double usdAmount = 100.0;

    CurrencyManager currencyManager = new CurrencyManager();
    Currency euro = currencyManager.getCurrency ("EUR");
    
    double euroAmount = currencyManager.convertFromBase (usdAmount, euro);

    System.out.println (usdAmount + "US$ == " + euroAmount + "EURO");
  }
}