/*
 * Copyright 2007-2010 WorldWide Conferencing, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.liftweb {
package util {

import _root_.java.util.Locale
import _root_.java.text.{NumberFormat, DecimalFormat}

trait TwoFractionDigits {
    val numberOfFractionDigits = 2
    val scale = 10
}

trait DollarCurrency extends TwoFractionDigits {
    val currencySymbol = "$"
}

/* Various Currencies */
object AU extends CurrencyZone {
    type Currency = AUD
    var locale = new Locale("en", "AU")
    def make(x: BigDecimal) = new Currency{val amount = x}
    abstract class AUD extends AbstractCurrency("AUD") with DollarCurrency {}
}

object US extends CurrencyZone {
    type Currency = USD
    var locale = Locale.US
    def make(x: BigDecimal) = new Currency{val amount = x}
    abstract class USD extends AbstractCurrency("USD") with DollarCurrency {}
}

object GB extends CurrencyZone {
    type Currency = GBP
    var locale = Locale.UK
    def make(x: BigDecimal) = new Currency{val amount = x}
    abstract class GBP extends AbstractCurrency("GBP") with TwoFractionDigits {val currencySymbol = "£"}
}

object EU extends CurrencyZone {
    type Currency = EUR
    var locale = Locale.GERMANY // guess this is why its a var
    def make(x: BigDecimal) = new Currency{val amount = x; override val _locale = locale}
    abstract class EUR extends AbstractCurrency("EUR") with TwoFractionDigits {val currencySymbol = "€"}
}

abstract class CurrencyZone {
    type Currency <: AbstractCurrency

    var locale: Locale
    def make(x: BigDecimal): Currency

    def apply(x: String): Currency = {
        try {
            make(BigDecimal(x)) // try normal number
        } catch { case e: _root_.java.lang.NumberFormatException => {
                    try {
                        make(BigDecimal(""+NumberFormat.getNumberInstance(locale).parse(x))) // try with grouping separator
                    } catch { case e: _root_.java.text.ParseException => {
                                make(BigDecimal(""+NumberFormat.getCurrencyInstance(locale).parse(x))) } // try with currency symbol and grouping separator
                    }
                }
        }

    }

    def apply(x: BigDecimal): Currency = make(x)

    /* currency factory*/
    abstract class AbstractCurrency(val designation: String) extends Ordered[Currency] {

        val _locale: Locale = locale
        def amount: BigDecimal
        def floatValue = amount.floatValue
        def doubleValue = amount.doubleValue
        val currencySymbol: String
        val numberOfFractionDigits: Int
        val scale: Int

        def +(that: Currency): Currency = make(this.amount + that.amount)
        def +(that: Int): Currency = this + make(that)

        def *(that: Currency): Currency = make(this.amount * that.amount)
        def *(that: Int): Currency = this * make(that)

        def -(that: Currency): Currency = make(this.amount - that.amount)
        def -(that: Int): Currency = this - make(that)

        def /(that: Currency): Currency =
        make(new BigDecimal(this.amount.bigDecimal.divide(that.amount.bigDecimal, scale, _root_.java.math.BigDecimal.ROUND_HALF_UP)) )
        def /(that: Int): Currency = this / make(that)

        def compare(that: Currency) = this.amount compare that.amount

        override def equals(that: Any) = that match {
            case that: AbstractCurrency => this.designation+this.format("", scale) == that.designation+that.format("", scale)
            case _ => false
        }

        override def hashCode = (this.designation+format("", scale)).hashCode

        def round(precision: Int) = make(BigDecimal(get(precision)))

        override def toString = format("", numberOfFractionDigits)

        def format(fd: Int): String = format(currencySymbol, fd)

        def format: String = format(currencySymbol, numberOfFractionDigits)

        def format(currencySymbol: String, numberOfFractionDigits: Int): String = {
            val moneyValue = amount match {
                case null => 0
                case _ => amount.setScale(numberOfFractionDigits, BigDecimal.RoundingMode.ROUND_HALF_UP).doubleValue;
            }

            val numberFormat = NumberFormat.getCurrencyInstance(_locale);
            numberFormat.setMinimumFractionDigits(numberOfFractionDigits);
            numberFormat.setMaximumFractionDigits(numberOfFractionDigits);
            val symbol=numberFormat.getCurrency.getSymbol(_locale)
            numberFormat.format(moneyValue).replace(symbol, currencySymbol)

        }

        def get: String = get(numberOfFractionDigits)

        def get(numberOfFractionDigits: Int): String = {
            val nf = NumberFormat.getNumberInstance(_locale)
            val df = nf.asInstanceOf[DecimalFormat]
            val groupingSeparator = df.getDecimalFormatSymbols.getGroupingSeparator

            format("", numberOfFractionDigits).replaceAll(groupingSeparator+"", "");
        }

    }

}

}
}