import {LOCALES} from './locales';
const registeredLocales: any = {
    el: LOCALES.el,
    en: LOCALES.en
};

export class NumberFormatter {
    /**
     * Registers the given local configuration
     * @param locale
     * @param configuration
     */
    static registerLocale(locale: string, configuration: any) {
        Object.defineProperty(registeredLocales, locale, {
            configurable: true,
            enumerable: true,
            writable: true,
            value: configuration
        })
    }

    /**
     * Creates a new instance of NumberFormatter class
     */
    static create(): NumberFormatter {
        return new NumberFormatter();
    }

    protected formatIntegerPart(number: number, locale: string) {
        // format number
        const currentLocale = registeredLocales[locale];
        let result = '';
        let nextValue = number;
        const keys = currentLocale.values.map( (key) => {
            return key[0];
        });
        // if nextValue is zero return zero string
        if (nextValue === 0) {
            const findZero = currentLocale.values.find( (key) => {
                return key[0] === '0';
            });
            if (findZero) {
                result = findZero[1];
            }
            return result;

        }

        // enumerate formatter keys
        for (let index = 0; index < keys.length; index++) {
            const key = keys[index];
            // convert property value to number
            const divider = parseFloat(key);
            // divide object
            const nextResult = Math.floor(nextValue / divider);
            if (nextResult >= 1) {
                if (nextResult > 1) {
                    result += " ";
                    result += this.formatIntegerPart(nextResult, locale);
                }
                const value = currentLocale.values[index][1];
                if (typeof value === 'string') {
                    result += " ";
                    result += value;
                } else if (typeof value === 'object') {
                    result += " ";
                    if (nextResult === 1 && nextValue > divider && Object.prototype.hasOwnProperty.call(value, 'more')) {
                        result += value.more;
                    }
                    else {
                        result += nextResult === 1 ? value.one : value.many;
                    }
                } else {
                    throw new Error('Invalid number formatter provider value.');
                }
            }
            // get next value
            nextValue = nextValue % divider;
            // if value is zero break and exit
            if (nextValue === 0) {
                break;
            }
            // otherwise continue
        }
        // trim result
        return result.trim();
    }

    protected formatFractionalPart(number: number, locale: string, fractionDigits: number): string {
        if (number < 0) {
            throw new Error('Fractional part of a number cannot be less than zero');
        }
        // if number is zero
        if (number === 0) {
            // do nothing
            return '';
        }
        const currentLocale = registeredLocales[locale];
        // get values lower than 1
        const values = currentLocale.values.map( (key) => {
            return parseFloat(key[0]);
        });
        let result;
        // enumerate locale values
        for (let i = 0; i < values.length; i++) {
            let value = values[i];
            // test value that it is lower than 1 e.g. 0.01
            if (value > 0 && value < 1) {
                let valueText = currentLocale.values[i][0];
                // if result is greater or equal to 1
                if (valueText.length - 2 === fractionDigits) {
                    // get key value
                    let keyValue = currentLocale.values[i];
                    // format number e.g. 45 => a cardinal number
                    result = this.formatIntegerPart(number, locale);
                    result += " ";
                    // and get key value description
                    if (typeof keyValue === 'string') {
                        result += keyValue[1];
                    } else if (typeof keyValue === 'object') {
                        result += number === 1 ? keyValue[1].one : keyValue[1].many;
                    } else {
                        throw new Error('Invalid number formatter provider value.');
                    }
                    break;
                }
            }
        }
        return result;
    }

    format(number: number, locale: string, fractionDigits?: number) {
        if (Object.prototype.hasOwnProperty.call(registeredLocales, locale) === false) {
            throw new Error(`The specified locale is missing for registered locales.`)
        }
        // format number
        const currentLocale = registeredLocales[locale];
        // get integer part
        const numberString = fractionDigits? number.toFixed(fractionDigits) : number.toString();
        const integerPart = parseInt(/^(\d+)\.?/.exec(numberString)[0]);
        let result = this.formatIntegerPart(integerPart, locale);
        const fractionalMatch = /\.(\d+)$/.exec(numberString);
        if (fractionalMatch) {
            const fractionalPart = Math.round(Number((parseFloat(fractionalMatch[0]) * Math.pow(10, fractionalMatch[1].length)).toFixed(0)));
            const fractionalResult = this.formatFractionalPart(fractionalPart, locale,fractionDigits ? fractionDigits : fractionalMatch[1].length );
            if (fractionalResult && fractionalResult.length) {
                result += " ";
                if (currentLocale.decimalSeparator && currentLocale.decimalSeparator.length) {
                    result += currentLocale.decimalSeparator;
                    result += " ";
                }
                result += fractionalResult;
            }
        }
        if (typeof currentLocale.spellcheck === 'function') {
            result = currentLocale.spellcheck(result);
        }
        return result;
    }
}
