export function CilNumbericDirective() {
    var directive = {
        link: link,
        require: 'ngModel',
        restrict: 'A'
    };
    return directive;


    function link(scope, el, attrs, ngModelCtrl) {
        // var decimalSeparator = $locale.NUMBER_FORMATS.DECIMAL_SEP;
        // var groupSeparator = $locale.NUMBER_FORMATS.GROUP_SEP;

        var decimalSeparator = '.';
        var groupSeparator = ',';

        // Create new regular expression with current decimal separator.
        var NUMBER_REGEXP = "^\\s*(\\-|\\+)?(\\d+|(\\d*(\\.\\d*)))\\s*$";
        var regex = new RegExp(NUMBER_REGEXP);

        var formatting = true;
        var maxInputLength = 16; // Maximum input length. Default max ECMA script.
        var max; // Maximum value. Default undefined.
        var min; // Minimum value. Default undefined.
        var limitMax = true; // Limit input to max value (value is capped). Default true.
        var limitMin = true; // Limit input to min value (value is capped). Default true.
        var decimals = 2; // Number of decimals. Default 2.
        var lastValidValue; // Last valid value.

        // Create parsers and formatters.
        ngModelCtrl.$parsers.push(parseViewValue);
        ngModelCtrl.$parsers.push(minValidator);
        ngModelCtrl.$parsers.push(maxValidator);
        ngModelCtrl.$formatters.push(formatViewValue);

        el.bind('blur', onBlur); // Event handler for the leave event.
        el.bind('focus', onFocus); // Event handler for the focus event.

        // Put a watch on the min, max and decimal value changes in the attribute.
        scope.$watch(attrs.min, onMinChanged);
        scope.$watch(attrs.max, onMaxChanged);
        scope.$watch(attrs.limitMax, onLimitMaxChanged);
        scope.$watch(attrs.limitMin, onLimitMinChanged);
        scope.$watch(attrs.decimals, onDecimalsChanged);
        scope.$watch(attrs.formatting, onFormattingChanged);

        // Setup decimal formatting.
        setTimeout(() => {
            if (decimals > -1) {
                ngModelCtrl.$parsers.push(function (value) {
                    return (value) ? round(value) : value;
                });
                ngModelCtrl.$formatters.push(function (value) {
                    return (value || value === 0) ? formatPrecision(value) : value;
                });
            }
        }, 0);

        function onMinChanged(value) {
            if (!angular.isUndefined(value)) {
                min = parseFloat(value);
                lastValidValue = minValidator(ngModelCtrl.$modelValue);
                ngModelCtrl.$setViewValue(formatPrecision(lastValidValue));
                ngModelCtrl.$render();
            }
        }

        function onMaxChanged(value) {
            if (!angular.isUndefined(value)) {
                max = parseFloat(value);
                maxInputLength = calculateMaxLength(max);
                lastValidValue = maxValidator(ngModelCtrl.$modelValue);
                ngModelCtrl.$setViewValue(formatPrecision(lastValidValue));
                ngModelCtrl.$render();
            }
        }

        function onDecimalsChanged(value) {
            if (!angular.isUndefined(value)) {
                decimals = parseFloat(value);
                maxInputLength = calculateMaxLength(max);
                if (lastValidValue !== undefined) {
                    ngModelCtrl.$setViewValue(formatPrecision(lastValidValue));
                    ngModelCtrl.$render();
                }
            }
        }

        function onFormattingChanged(value) {
            if (!angular.isUndefined(value)) {
                formatting = (value !== false);
                ngModelCtrl.$setViewValue(formatPrecision(lastValidValue));
                ngModelCtrl.$render();
            }
        }

        function onLimitMinChanged(value) {
            if (!angular.isUndefined(value)) {
                limitMin = (value == "true");
            }
        }

        function onLimitMaxChanged(value) {
            if (!angular.isUndefined(value)) {
                limitMax = (value == "true");
            }
        }

        /**
         * Round the value to the closest decimal.
         */
        function round(value) {
            var d = Math.pow(10, decimals);
            return Math.round(value * d) / d;
        }

        /**
         * Format a number with the thousand group separator.
         */
        function numberWithCommas(value) {
            if (formatting) {
                var parts = ("" + value).split(decimalSeparator);
                parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, groupSeparator);
                return parts.join(decimalSeparator);
            } else {
                // No formatting applies.
                return value;
            }
        }

        /**
         * Format a value with thousand group separator and correct decimal char.
         */
        function formatPrecision(value) {
            if (!(value || value === 0)) {
                return '';
            }
            var formattedValue = parseFloat(value).toFixed(decimals);
            formattedValue = formattedValue.replace('.', decimalSeparator);
            return numberWithCommas(formattedValue);
        }

        function formatViewValue(value) {
            return ngModelCtrl.$isEmpty(value) ? '' : '' + value;
        }

        /**
         * Parse the view value.
         */
        function parseViewValue(value) {
            if (angular.isUndefined(value)) {
                value = '';
            }
            value = ("" + value).replace(decimalSeparator, '.');

            // Handle leading decimal point, like ".5"
            if (value.indexOf('.') === 0) {
                value = '0' + value;
            }

            // Allow "-" inputs only when min < 0
            if (value.indexOf('-') === 0) {
                if (min >= 0) {
                    value = null;
                    ngModelCtrl.$setViewValue(formatViewValue(lastValidValue));
                    ngModelCtrl.$render();
                } else if (value === '-') {
                    value = '';
                }
            }

            var empty = ngModelCtrl.$isEmpty(value);
            if (empty) {
                lastValidValue = '';
                // ngModelCtrl.$modelValue = undefined;
            } else {
                var parts = ("" + value).split(decimalSeparator);
                if (regex.test(value) && (value.length <= maxInputLength) &&
                    (!parts[1] || (parts[1] && parts[1].length <= decimals)) &&
                    (decimals > 0 || (decimals === 0 && ("" + value).indexOf('.') === -1))) {
                    if ((value > max) && limitMax) {
                        lastValidValue = max;
                    } else if ((value < min) && limitMin) {
                        lastValidValue = min;
                    } else {
                        lastValidValue = (value === '') ? null : parseFloat(value);
                    }
                } else {
                    // Render the last valid input in the field
                    ngModelCtrl.$setViewValue(formatViewValue(lastValidValue));
                    ngModelCtrl.$render();
                }
            }

            return lastValidValue;
        }

        /**
         * Calculate the maximum input length in characters.
         * If no maximum the input will be limited to 16; the maximum ECMA script int.
         */
        function calculateMaxLength(value) {
            var length = 16;
            if (!angular.isUndefined(value)) {
                length = Math.floor(value).toString().length;
            }
            if (decimals > 0) {
                // Add extra length for the decimals plus one for the decimal separator.
                length += decimals + 1;
            }
            if (min < 0) {
                // Add extra length for the - sign.
                length++;
            }
            return length;
        }

        /**
         * Value validator for min and max.
         */
        function minmaxValidator(value, testValue, validityName, limit, compareFunction) {
            if (!angular.isUndefined(testValue) && limit) {
                if (!ngModelCtrl.$isEmpty(value) && compareFunction) {
                    return testValue;
                } else {
                    return value;
                }
            } else {
                if (!limit) {
                    ngModelCtrl.$setValidity(validityName, !compareFunction);
                }
                return value;
            }
        }

        /**
         * Minimum value validator.
         */
        function minValidator(value) {
            return minmaxValidator(value, min, 'min', limitMin, (value < min));
        }

        /**
         * Maximum value validator.
         */
        function maxValidator(value) {
            return minmaxValidator(value, max, 'max', limitMax, (value > max));
        }

        /**
         * Function for handeling the blur (leave) event on the control.
         */
        function onBlur() {
            var value = ngModelCtrl.$modelValue;
            if (!angular.isUndefined(value)) {
                // Format the model value.
                ngModelCtrl.$viewValue = formatPrecision(value);
                ngModelCtrl.$render();
            }
        }

        /**
         * Function for handeling the focus (enter) event on the control.
         * On focus show the value without the group separators.
         */
        function onFocus() {
            var value = ngModelCtrl.$modelValue;
            if (!angular.isUndefined(value)) {
                ngModelCtrl.$viewValue = ("" + value).replace(".", decimalSeparator);
                ngModelCtrl.$render();
            }
        }
    }
}