/*
 * $Id$
 *
 * Copyright 2012-2015 Valentyn Kolesnikov
 *
 * 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.
 */

var currencyList =
{
  "CurrencyList": {
    "language": { "-value": "UKR" },
    "UKR": {
      "item": [
        {
          "-value": "0",
          "-text": "нуль"
        },
        {
          "-value": "1000_10",
          "-text": "тисяч,мільйонів,мільярдів,трильйонів"
        },
        {
          "-value": "1000_1",
          "-text": "тисяча,мільйон,мільярд,трильйон"
        },
        {
          "-value": "1000_234",
          "-text": "тисячі,мільйона,мільярда,трильйона"
        },
        {
          "-value": "1000_5",
          "-text": "тисяч,мільйонів,мільярдів,трильйонів"
        },
        {
          "-value": "10_19",
          "-text": "десять,одинадцять,дванадцять,тринадцять,чотирнадцять,п’ятнадцять,шiстнадцять,сiмнадцять,вiсiмнадцять,дев'ятнадцять"
        },
        {
          "-value": "1",
          "-text": "одна,один,один,одна"
        },
        {
          "-value": "2",
          "-text": "дві,два,два,дві"
        },
        {
          "-value": "3_9",
          "-text": "три,чотири,п’ять,шість,сім,вісім,дев’ять"
        },
        {
          "-value": "100_900",
          "-text": "сто ,двісті ,триста ,чотириста ,п’ятсот ,шістсот ,сімсот ,вісімсот ,дев’ятсот "
        },
        {
          "-value": "20_90",
          "-text": "двадцять ,тридцять ,сорок ,п’ятдесят ,шістдесят ,сімдесят ,вісімдесят ,дев’яносто "
        }
      ]
    },
    "RUS": {
      "item": [
        {
          "-value": "0",
          "-text": "ноль"
        },
        {
          "-value": "1000_10",
          "-text": "тысяч,миллионов,миллиардов,триллионов"
        },
        {
          "-value": "1000_1",
          "-text": "тысяча,миллион,миллиард,триллион"
        },
        {
          "-value": "1000_234",
          "-text": "тысячи,миллиона,миллиарда,триллиона"
        },
        {
          "-value": "1000_5",
          "-text": "тысяч,миллионов,миллиардов,триллионов"
        },
        {
          "-value": "10_19",
          "-text": "десять,одиннадцать,двенадцать,тринадцать,четырнадцать,пятнадцать,шестнадцать,семнадцать,восемнадцать,девятнадцать"
        },
        {
          "-value": "1",
          "-text": "одна,один,один,одна"
        },
        {
          "-value": "2",
          "-text": "две,два,два,две"
        },
        {
          "-value": "3_9",
          "-text": "три,четыре,пять,шесть,семь,восемь,девять"
        },
        {
          "-value": "100_900",
          "-text": "сто ,двести ,триста ,четыреста ,пятьсот ,шестьсот ,семьсот ,восемьсот ,девятьсот "
        },
        {
          "-value": "20_90",
          "-text": "двадцать ,тридцать ,сорок ,пятьдесят ,шестьдесят ,семьдесят ,восемьдесят ,девяносто "
        }
      ]
    },
    "ENG": {
      "item": [
        {
          "-value": "0",
          "-text": "zero"
        },
        {
          "-value": "1000_10",
          "-text": "thousand,million,billion,trillion"
        },
        {
          "-value": "1000_1",
          "-text": "thousand,million,billion,trillion"
        },
        {
          "-value": "1000_234",
          "-text": "thousand,million,billion,trillion"
        },
        {
          "-value": "1000_5",
          "-text": "thousand,million,billion,trillion"
        },
        {
          "-value": "10_19",
          "-text": "ten,eleven,twelve,thirteen,fourteen,fifteen,sixteen,seventeen,eighteen,nineteen"
        },
        {
          "-value": "1",
          "-text": "one,one,one,one"
        },
        {
          "-value": "2",
          "-text": "two,two,two,two"
        },
        {
          "-value": "3_9",
          "-text": "three,four,five,six,seven,eight,nine"
        },
        {
          "-value": "100_900",
          "-text": "one hundred ,two hundred ,three hundred ,four hundred ,five hundred ,six hundred ,seven hundred ,eight hundred ,nine hundred "
        },
        {
          "-value": "20_90",
          "-text": "twenty-,thirty-,forty-,fifty-,sixty-,seventy-,eighty-,ninety-"
        }
      ]
    },
    "RUR": [
      {
        "-CurrID": "810",
        "-CurrName": "Российские рубли",
        "-language": "RUS",
        "-RubOneUnit": "рубль",
        "-RubTwoUnit": "рубля",
        "-RubFiveUnit": "рублей",
        "-RubSex": "M",
        "-KopOneUnit": "копейка",
        "-KopTwoUnit": "копейки",
        "-KopFiveUnit": "копеек",
        "-KopSex": "F"
      },
      {
        "-CurrID": "810",
        "-CurrName": "Российские рубли",
        "-language": "UKR",
        "-RubOneUnit": "рубль",
        "-RubTwoUnit": "рубля",
        "-RubFiveUnit": "рублей",
        "-RubSex": "M",
        "-KopOneUnit": "копейка",
        "-KopTwoUnit": "копейки",
        "-KopFiveUnit": "копеек",
        "-KopSex": "F"
      }
    ],
    "UAH": [
      {
        "-CurrID": "980",
        "-CurrName": "Украинскі гривні",
        "-language": "RUS",
        "-RubOneUnit": "гривня",
        "-RubTwoUnit": "гривни",
        "-RubFiveUnit": "гривень",
        "-RubSex": "F",
        "-KopOneUnit": "копейка",
        "-KopTwoUnit": "копейки",
        "-KopFiveUnit": "копеек",
        "-KopSex": "F"
      },
      {
        "-CurrID": "980",
        "-CurrName": "Украинскі гривні",
        "-language": "UKR",
        "-RubOneUnit": "гривня",
        "-RubTwoUnit": "гривні",
        "-RubFiveUnit": "гривень",
        "-RubSex": "F",
        "-KopOneUnit": "копійка",
        "-KopTwoUnit": "копійки",
        "-KopFiveUnit": "копійок",
        "-KopSex": "F"
      }
    ],
    "USD": [
      {
        "-CurrID": "840",
        "-CurrName": "Долари США",
        "-language": "RUS",
        "-RubOneUnit": "долар",
        "-RubTwoUnit": "долара",
        "-RubFiveUnit": "доларів",
        "-RubSex": "M",
        "-KopOneUnit": "цент",
        "-KopTwoUnit": "цента",
        "-KopFiveUnit": "центов",
        "-KopSex": "M"
      },
      {
        "-CurrID": "840",
        "-CurrName": "Долари США",
        "-language": "UKR",
        "-RubOneUnit": "долар",
        "-RubTwoUnit": "долара",
        "-RubFiveUnit": "доларів",
        "-RubSex": "M",
        "-KopOneUnit": "цент",
        "-KopTwoUnit": "цента",
        "-KopFiveUnit": "центів",
        "-KopSex": "M"
      },
      {
        "-CurrID": "840",
        "-CurrName": "Долари США",
        "-language": "ENG",
        "-RubOneUnit": "dollar",
        "-RubTwoUnit": "dollars",
        "-RubFiveUnit": "dollars",
        "-RubSex": "M",
        "-KopOneUnit": "cent",
        "-KopTwoUnit": "cents",
        "-KopFiveUnit": "cents",
        "-KopSex": "M"
      }
     ],
    "PER10": [
      {
        "-CurrID": "556",
        "-CurrName": "Вiдсотки з десятими частинами",
        "-language": "RUS",
        "-RubOneUnit": "целая,",
        "-RubTwoUnit": "целых,",
        "-RubFiveUnit": "целых,",
        "-RubSex": "F",
        "-KopOneUnit": "десятая процента",
        "-KopTwoUnit": "десятых процента",
        "-KopFiveUnit": "десятых процента",
        "-KopSex": "F"
      },
      {
        "-CurrID": "556",
        "-CurrName": "Вiдсотки з десятими частинами",
        "-language": "UKR",
        "-RubOneUnit": "ціла,",
        "-RubTwoUnit": "цілих,",
        "-RubFiveUnit": "цілих,",
        "-RubSex": "F",
        "-KopOneUnit": "десята відсотка",
        "-KopTwoUnit": "десятих відсотка",
        "-KopFiveUnit": "десятих відсотка",
        "-KopSex": "F"
      }
    ],
    "PER100": [
      {
        "-CurrID": "557",
        "-CurrName": "Вiдсотки з сотими частинами",
        "-language": "RUS",
        "-RubOneUnit": "целая,",
        "-RubTwoUnit": "целых,",
        "-RubFiveUnit": "целых,",
        "-RubSex": "F",
        "-KopOneUnit": "сотая процента",
        "-KopTwoUnit": "сотых процента",
        "-KopFiveUnit": "сотых процента",
        "-KopSex": "F"
      },
      {
        "-CurrID": "557",
        "-CurrName": "Вiдсотки з сотими частинами",
        "-language": "UKR",
        "-RubOneUnit": "ціла,",
        "-RubTwoUnit": "цілих,",
        "-RubFiveUnit": "цілих,",
        "-RubSex": "F",
        "-KopOneUnit": "сота відсотка",
        "-KopTwoUnit": "сотих відсотка",
        "-KopFiveUnit": "сотих відсотка",
        "-KopSex": "F"
      }
    ],
    "PER1000": [
      {
        "-CurrID": "558",
        "-CurrName": "Вiдсотки з тисячними частинами",
        "-language": "RUS",
        "-RubOneUnit": "целая,",
        "-RubTwoUnit": "целых,",
        "-RubFiveUnit": "целых,",
        "-RubSex": "F",
        "-KopOneUnit": "тысячная процента",
        "-KopTwoUnit": "тысячных процента",
        "-KopFiveUnit": "тысячных процента",
        "-KopSex": "F"
      },
      {
        "-CurrID": "558",
        "-CurrName": "Вiдсотки з тисячними частинами",
        "-language": "UKR",
        "-RubOneUnit": "ціла,",
        "-RubTwoUnit": "цілих,",
        "-RubFiveUnit": "цілих,",
        "-RubSex": "F",
        "-KopOneUnit": "тисячна відсотка",
        "-KopTwoUnit": "тисячних відсотка",
        "-KopFiveUnit": "тисячних відсотка",
        "-KopSex": "F"
      }
    ],
    "PER10000": [
      {
        "-CurrID": "559",
        "-CurrName": "Вiдсотки з десяти тисячними частинами",
        "-language": "RUS",
        "-RubOneUnit": "целая,",
        "-RubTwoUnit": "целых,",
        "-RubFiveUnit": "целых,",
        "-RubSex": "F",
        "-KopOneUnit": "десятитысячная процента",
        "-KopTwoUnit": "десятитысячные процента",
        "-KopFiveUnit": "десятитысячных процента",
        "-KopSex": "F"
      },
      {
        "-CurrID": "559",
        "-CurrName": "Вiдсотки з десяти тисячними частинами",
        "-language": "UKR",
        "-RubOneUnit": "ціла,",
        "-RubTwoUnit": "цілих,",
        "-RubFiveUnit": "цілих,",
        "-RubSex": "F",
        "-KopOneUnit": "десятитисячна відсотка",
        "-KopTwoUnit": "десятитисячних відсотка",
        "-KopFiveUnit": "десятитисячних відсотка",
        "-KopSex": "M"
      }
    ]
  }
};
/**
 * Converts numbers to symbols.
 *
 * @author Valentyn Kolesnikov
 * @version $Revision$ $Date$
*/

    /** Currency. */
enum Currency {
    UAH,
    RUR,
    USD,
    PER10,
    PER100,
    PER1000,
    PER10000
};

    /** Language. */
enum Language {
    RUS,
    UKR,
    ENG
};

    /** Pennies. */
enum Pennies {
    NUMBER,
    TEXT
};

class StringBuilder {
    private _buffer;
    constructor() {
        this._buffer = [];
    }

    append(text) {
            this._buffer[this._buffer.length] = text;
            return this;
    }
        
    insert(index, text) { 
            this._buffer.splice(index, 0, text);
            return this;
    }

    length() {
            return this.toString().length;
    }

    deleteCharAt(index) {
            var str = this.toString();
            this._buffer = [];
            this.append(str.substring(0, index)); 
            return this;
    }

    toString() {
            return this._buffer.join("");
    }
};

class MoneyToStr {
    static NUM0 = 0;
    static NUM1 = 1;
    static NUM2 = 2;
    static NUM3 = 3;
    static NUM4 = 4;
    static NUM5 = 5;
    static NUM6 = 6;
    static NUM7 = 7;
    static NUM8 = 8;
    static NUM9 = 9;
    static NUM10 = 10;
    static NUM11 = 11;
    static NUM14 = 14;
    static NUM100 = 100;
    static NUM1000 = 1000;
    static NUM10000 = 10000;
    static INDEX_0 = 0;
    static INDEX_1 = 1;
    static INDEX_2 = 2;
    static INDEX_3 = 3;
    private rubOneUnit;
    private rubTwoUnit;
    private rubFiveUnit;
    private kopOneUnit;
    private kopTwoUnit;
    private kopFiveUnit;
    private rubSex;
    private kopSex;
    private currency : Currency;
    private language : Language;
    private pennies : Pennies;
    private messages;

    static percentToStr(amount, lang : Language) {
        if (amount == null) {
            throw new Error("amount is null");
        }
        if (lang == null) {
            throw new Error("Language is null");
        }
        var intPart = parseInt(amount);
        var fractPart = 0;
        var result;
        if (amount == amount.toInt()) {
            result = new MoneyToStr(Currency.PER10, lang, Pennies.TEXT).convert(amount, 0);
        } else if ((amount * MoneyToStr.NUM10).toFixed(4) == parseInt("" + amount * MoneyToStr.NUM10).toFixed(4)) {
            fractPart = Math.round((amount - intPart) * MoneyToStr.NUM10);
            result = new MoneyToStr(Currency.PER10, lang, Pennies.TEXT).convert(intPart, fractPart);
        } else if ((amount * MoneyToStr.NUM100).toFixed(4) == parseInt("" + amount * MoneyToStr.NUM100).toFixed(4)) {
            fractPart = Math.round((amount - intPart) * MoneyToStr.NUM100);
            result = new MoneyToStr(Currency.PER100, lang, Pennies.TEXT).convert(intPart, fractPart);
        } else if ((amount * MoneyToStr.NUM1000).toFixed(4) == parseInt("" + amount * MoneyToStr.NUM1000).toFixed(4)) {
            fractPart = Math.round((amount - intPart) * MoneyToStr.NUM1000);
            result = new MoneyToStr(Currency.PER1000, lang, Pennies.TEXT).convert(intPart, fractPart);
        } else {
            fractPart = Math.round((amount - intPart) * MoneyToStr.NUM10000);
            result = new MoneyToStr(Currency.PER10000, lang, Pennies.TEXT).convert(intPart, fractPart);
        }
        return result;
    }
    constructor(currency : Currency, language : Language, pennies : Pennies) {
        this.currency = currency;
        this.language = language;
        this.pennies = pennies;
        var languageElement = Language[language];
        var items = currencyList['CurrencyList'][languageElement]['item'];
        this.messages = {};
        for (var index in items) {
            var languageItem = items[index];
            if (languageItem["-text"]) {
                this.messages[languageItem["-value"]] = languageItem["-text"].split(",");
            }
        }
        var currencyItem = currencyList['CurrencyList'][Currency[currency]]
        var theISOElement = null;
        for (var index in currencyItem) {
            if (currencyItem[index]["-language"] === languageElement) {
                theISOElement = currencyItem[index];
                break;
            }
        }
        if (theISOElement == null) {
            throw new Error("Currency not found " + Currency[currency]);
        }
        this.rubOneUnit = theISOElement["-RubOneUnit"];
        this.rubTwoUnit = theISOElement["-RubTwoUnit"];
        this.rubFiveUnit = theISOElement["-RubFiveUnit"];
        this.kopOneUnit = theISOElement["-KopOneUnit"];
        this.kopTwoUnit = theISOElement["-KopTwoUnit"];
        this.kopFiveUnit = theISOElement["-KopFiveUnit"];
        this.rubSex = theISOElement["-RubSex"];
        this.kopSex = theISOElement["-KopSex"];
    }

    /**
     * Converts double value to the text description.
     *
     * @param theMoney
     *            the amount of money in format major.minor
     * @return the string description of money value
     */
    convertValue(theMoney) {
        if (typeof theMoney === undefined || theMoney == null) {
            throw new Error("theMoney is null");
        }
        var intPart = parseInt(theMoney);
        var fractPart = Math.round((theMoney - intPart) * MoneyToStr.NUM100);
        if (this.currency == Currency.PER1000) {
            fractPart = Math.round((theMoney - intPart) * MoneyToStr.NUM1000);
        }
        return this.convert(intPart, fractPart);
    }

    /**
     * Converts number to currency. Usage: MoneyToStr moneyToStr = new MoneyToStr("UAH"); String result =
     * moneyToStr.convert(123D); Expected: result = сто двадцять три гривні 00 копійок
     *
     * @param theMoney
     *            the amount of money major currency
     * @param theKopeiki
     *            the amount of money minor currency
     * @return the string description of money value
     */
    convert(theMoney, theKopeiki) {
        if (typeof theMoney === undefined || theMoney == null) {
            throw new Error("theMoney is null");
        }
        if (typeof theKopeiki === undefined || theKopeiki == null) {
            throw new Error("theKopeiki is null");
        }
        var money2str = new StringBuilder();
        var triadNum = 0;
        var theTriad = 0;
        var intPart = theMoney;
        if (intPart == 0) {
            money2str.append(this.messages["0"][0] + " ");
        }
        do {
            theTriad = parseInt("" + intPart % MoneyToStr.NUM1000);
            money2str.insert(0, this.triad2Word(theTriad, triadNum, this.rubSex));
            if (triadNum == 0) {
                var range10 = parseInt("" + (theTriad % MoneyToStr.NUM100) / MoneyToStr.NUM10);
                var range = parseInt("" + theTriad % MoneyToStr.NUM10);
                if (range10 == MoneyToStr.NUM1) {
                    money2str.append(this.rubFiveUnit);
                } else {
                    switch (range) {
                    case MoneyToStr.NUM1:
                        money2str.append(this.rubOneUnit);
                        break;
                    case MoneyToStr.NUM2:
                    case MoneyToStr.NUM3:
                    case MoneyToStr.NUM4:
                        money2str.append(this.rubTwoUnit);
                        break;
                    default:
                        money2str.append(this.rubFiveUnit);
                        break;
                    }
                }
            }
            intPart = parseInt("" + intPart / MoneyToStr.NUM1000);
            triadNum++;
        } while (intPart > 0);

        if (this.pennies == Pennies.TEXT) {
            money2str.append(this.language == Language.ENG ? " and " : " ").append(theKopeiki == 0 ? this.messages["0"][0] + " " : this.triad2Word(theKopeiki, 0, this.kopSex));
        } else {
            money2str.append(" " + (theKopeiki < 10 ? "0" + theKopeiki : theKopeiki) + " ");
        }
        if (theKopeiki >= MoneyToStr.NUM11 && theKopeiki <= MoneyToStr.NUM14) {
            money2str.append(this.kopFiveUnit);
        } else {
            switch (parseInt("" + theKopeiki % MoneyToStr.NUM10)) {
            case MoneyToStr.NUM1:
                money2str.append(this.kopOneUnit);
                break;
            case MoneyToStr.NUM2:
            case MoneyToStr.NUM3:
            case MoneyToStr.NUM4:
                money2str.append(this.kopTwoUnit);
                break;
            default:
                money2str.append(this.kopFiveUnit);
                break;
            }
        }
        return money2str.toString().trim();
    }
    triad2Word(triad, triadNum, sex) {
        var triadWord = new StringBuilder();

        if (triad == 0) {
            return "";
        }

        var range = this.check1(triad, triadWord);
        if (this.language == Language.ENG && triadWord.length() > 0 && triad % MoneyToStr.NUM10 == 0) {
            triadWord.deleteCharAt(triadWord.length() - 1);
            triadWord.append(" ");
        }

        var range10 = range;
        range = parseInt("" + triad % MoneyToStr.NUM10);
        this.check2(triadNum, sex, triadWord, triad, range10);
        switch (triadNum) {
        case MoneyToStr.NUM0:
            break;
        case MoneyToStr.NUM1:
        case MoneyToStr.NUM2:
        case MoneyToStr.NUM3:
        case MoneyToStr.NUM4:
            if (range10 == MoneyToStr.NUM1) {
                triadWord.append(this.messages["1000_10"][triadNum - 1] + " ");
            } else {
                switch (range) {
                case MoneyToStr.NUM1:
                    triadWord.append(this.messages["1000_1"][triadNum - 1] + " ");
                    break;
                case MoneyToStr.NUM2:
                case MoneyToStr.NUM3:
                case MoneyToStr.NUM4:
                    triadWord.append(this.messages["1000_234"][triadNum - 1] + " ");
                    break;
                default:
                    triadWord.append(this.messages["1000_5"][triadNum - 1] + " ");
                    break;
                }
            }
            break;
        default:
            triadWord.append("??? ");
            break;
        }
        return triadWord.toString();
    }

    /**
     * @param triadNum the triad num
     * @param sex the sex
     * @param triadWord the triad word
     * @param triad the triad
     * @param range10 the range 10
     */
    check2(triadNum, sex, triadWord, triad, range10) {
        var range = parseInt("" + triad % MoneyToStr.NUM10);
        if (range10 == 1) {
            triadWord.append(this.messages["10_19"][range] + " ");
        } else {
            switch (range) {
            case MoneyToStr.NUM1:
                if (triadNum == MoneyToStr.NUM1) {
                    triadWord.append(this.messages["1"][MoneyToStr.INDEX_0] + " ");
                } else if (triadNum == MoneyToStr.NUM2 || triadNum == MoneyToStr.NUM3 || triadNum == MoneyToStr.NUM4) {
                    triadWord.append(this.messages["1"][MoneyToStr.INDEX_1] + " ");
                } else if ("M" == sex) {
                    triadWord.append(this.messages["1"][MoneyToStr.INDEX_2] + " ");
                } else if ("F" == sex) {
                    triadWord.append(this.messages["1"][MoneyToStr.INDEX_3] + " ");
                }
                break;
            case MoneyToStr.NUM2:
                if (triadNum == MoneyToStr.NUM1) {
                    triadWord.append(this.messages["2"][MoneyToStr.INDEX_0] + " ");
                } else if (triadNum == MoneyToStr.NUM2 || triadNum == MoneyToStr.NUM3 || triadNum == MoneyToStr.NUM4) {
                    triadWord.append(this.messages["2"][MoneyToStr.INDEX_1] + " ");
                } else if ("M" == sex) {
                    triadWord.append(this.messages["2"][MoneyToStr.INDEX_2] + " ");
                } else if ("F" == sex) {
                    triadWord.append(this.messages["2"][MoneyToStr.INDEX_3] + " ");
                }
                break;
            case MoneyToStr.NUM3:
            case MoneyToStr.NUM4:
            case MoneyToStr.NUM5:
            case MoneyToStr.NUM6:
            case MoneyToStr.NUM7:
            case MoneyToStr.NUM8:
            case MoneyToStr.NUM9:
                triadWord.append(["", "", ""].concat(this.messages["3_9"])[range] + " ");
                break;
            default:
                break;
            }
        }
    }

    /**
     * @param triad the triad
     * @param triadWord the triad word
     * @return the range
     */
    check1(triad, triadWord) {
        var range = parseInt("" + triad / MoneyToStr.NUM100);
        triadWord.append([""].concat(this.messages["100_900"])[range]);

        range = parseInt("" + (triad % MoneyToStr.NUM100) / MoneyToStr.NUM10);
        triadWord.append(["", ""].concat(this.messages["20_90"])[range]);
        return range;
    }
}
