angular.module("app").directive("sqInput", function ($rootScope, $timeout, L) {
    return {
        restrict: 'E',
        replace: true,
        scope: {
            sqModel: '=',
            options: '=',
            optionsValue: '@',
            optionsDisplay: '@',
            inputtype: '@',
            numbertype: '=?',
            label: '@',
            tooltip: '=?',
            hideTooltip: '=',
            min: '@',
            max: '@',
            step: '@',
            change: '&',
            ngModelOptions: '=?',
            req: '@',
            readonly: '@',
            name: '@',
            currentAction: '=',
            ngDisabled: '='
        },
        templateUrl: "../app/directives/sqInput/sqInput.html",
        controller: function ($scope) {
            $scope.ngModelOptions = angular.isDefined($scope.ngModelOptions) ? $scope.ngModelOptions : { updateOn: 'blur' };
        },
        link: function (scope, element, attrs) {
            scope.modelSQ = {
                val: scope.inputtype == 'text' ? 'not set' : scope.sqModel,
                selectSpan: ''
            };

            scope.tooltip = (scope.tooltip == 'Null' || scope.tooltip == null) ? false : scope.tooltip;

            updateSelectOverlay();

            var watcher1 = scope.$watch('modelSQ.val', function (newValue, oldValue) {
                onValueChange(newValue, oldValue, false);
            });

            var watcher2 = scope.$watch('sqModel', function (newValue, oldValue) {
                onValueChange(newValue, oldValue, false);
            }, true);

            if (scope.inputtype == 'select') {
                var watcher3 = scope.$watch('options', function (newValue, oldValue) {
                    updateSelectOverlay();
                }, true);
            }

            scope.$on('$destroy', function (event, args) {
                watcher1();
                watcher2();

                if (scope.inputtype == 'select') watcher3();
            }, true);

            scope.onSelect = function () {
                if (scope.modelSQ.val != undefined) {
                    updateValues(scope.modelSQ.val);
                }

                if (scope.change) {
                    $timeout(function () {
                        scope.change({ value: scope.modelSQ.val });
                    });
                }
            }

            scope.changeValue = function (sign) {
                var newValue = 0;
                if (scope.numbertype == 'time') {
                    var timeParts = scope.modelSQ.val.split(':');
                    var totalMinutes = parseInt(timeParts[0]) * 60 + parseInt(timeParts[1]);
                    newValue = totalMinutes * 60;
                }
                else {
                    newValue = parseFloat(scope.modelSQ.val);
                }

                var min = (angular.isUndefined(scope.min)) ? 0 : parseFloat(scope.min);
                var max = (angular.isUndefined(scope.max)) ? 10000000 : parseFloat(scope.max);
                var step = (angular.isUndefined(scope.step)) ? 1 : parseFloat(scope.step);

                if (sign == '-') {
                    if (newValue - step >= min) {
                        newValue -= step;
                    } else {
                        newValue = min;

                        $rootScope.showNotice(L.tsq("Minimum value is") + " " + min);
                    }
                }
                else {
                    if (String(scope.modelSQ.val) == '') {
                        newValue = min;

                        $rootScope.showNotice(L.tsq("Minimum value is") + " " + min);
                    } else if (newValue + step <= max) {
                        newValue += step;
                    } else {
                        newValue = max;

                        $rootScope.showNotice(L.tsq("Maximum value is") + " " + max);
                    }
                }

                var modelValue = parseFloat(correctDoubleValue(newValue, '' + step));

                updateValues(modelValue);
            }

            function onValueChange(newValue, oldValue, showError) {
                var processedValue = processValue(newValue, oldValue, showError);
                if (processedValue != null) {
                    updateValues(processedValue);
                }

                updateSelectOverlay();
            }

            function updateValues(newValue) {
                if (newValue != undefined && newValue != null) {
                    if (scope.inputtype == 'number') {
                        if (scope.numbertype == 'int') {
                            scope.modelSQ.val = parseInt(newValue);
                            scope.sqModel = parseInt(newValue);
                        }
                        else {
                            scope.modelSQ.val = parseFloat(newValue);
                            scope.sqModel = parseFloat(newValue);
                        }
                    }
                    else if (scope.inputtype == 'text' && scope.numbertype == 'time') {
                        if (newValue == '') newValue = 0;
                        newValue = String(newValue);
                        if (newValue.indexOf(':') == -1) {
                            scope.sqModel = parseFloat(newValue);
                            if (!angular.isUndefined(scope.max) && scope.sqModel > scope.max) scope.sqModel = scope.max;
                            if (!angular.isUndefined(scope.min) && scope.sqModel < scope.min) scope.sqModel = scope.min;
                            var minutesTotal = Math.floor(parseFloat(newValue) / 60);
                            var minutes = minutesTotal % 60;
                            var hours = Math.floor(minutesTotal / 60);
                            scope.modelSQ.val = padHourMinute(hours) + ':' + padHourMinute(minutes);
                        }
                        else {
                            scope.modelSQ.val = newValue;
                            var timeParts = newValue.split(':');
                            var totalMinutes = parseInt(timeParts[0]) * 60 + parseInt(timeParts[1]);
                            scope.sqModel = totalMinutes * 60;
                            if (!angular.isUndefined(scope.max) && scope.sqModel > scope.max) scope.sqModel = scope.max;
                            if (!angular.isUndefined(scope.min) && scope.sqModel < scope.min) scope.sqModel = scope.min;
                        }
                    }
                    else {
                        scope.modelSQ.val = newValue;
                        scope.sqModel = newValue;
                    }
                }
            }

            function padHourMinute(number) {
                if (number <= 99) { number = ("00" + number).slice(-2); }
                return number;
            }

            function processValue(newValue, oldValue, showError) {

                if (newValue === undefined || newValue === null) {
                    try {
                        newValue = $(element.find('input')[0]).val();       //assign an input value
                    } catch (err) {
                        return null;
                    }
                }

                if (scope.inputtype == 'number') {
                    if (String(newValue).trim().length > 0) {
                        scope.numbertype = scope.numbertype || 'double';

                        if (scope.numbertype == 'int') {
                            if (!checkIntegerValue('' + newValue)) {
                                if (showError) {
                                    $rootScope.showError(L.tsq("Expected integer value"));
                                    console.error("Expected integer value, got: " + newValue);
                                }
                                return tryGetOldValue(oldValue);
                            }
                        }
                        else if (scope.numbertype == 'double') {
                            if (!checkDoubleValue('' + newValue)) {
                                if (showError) {
                                    $rootScope.showError(L.tsq("Expected numeric value"));
                                    console.error("Expected numeric value, got: " + newValue);
                                }
                                return tryGetOldValue(oldValue);
                            }
                        }
                    }

                    return fitInRange(newValue);
                }
                else {
                    if (String(newValue).trim().length > 0) {
                        if (newValue == 'not set') {
                            scope.modelSQ.val = "";
                            return null;
                        }
                        else {
                            return newValue;
                        }
                    }
                    else {
                        if (scope.inputtype && scope.inputtype != 'select') {
                            scope.sqModel = 'not set';
                        }

                        return null;
                    }
                }
            }

            function fitInRange(value) {
                if (angular.isDefined(scope.min) && parseFloat(value) < parseFloat(scope.min)) return scope.min;
                else if (angular.isDefined(scope.max) && parseFloat(value) > parseFloat(scope.max)) return scope.max;
                else return value;
            }

            function tryGetOldValue(oldValue) {
                if (oldValue != null && oldValue != undefined) {
                    if (scope.numbertype == 'double') {
                        return parseFloat(oldValue);
                    }
                    else return parseInt(oldValue);
                }
                else return null;
            }

            function correctDoubleValue(value, step) {
                var decimals = 2;
                var stepParts = step.split(".");

                if (stepParts[1]) {
                    decimals = stepParts[1].length;
                    var n = Math.pow(10, decimals);
                    return (Math.round(value * n) / n).toFixed(decimals);
                }
                else {
                    return correctDoubleValue(value, "0.01");
                }
            }

            function updateSelectOverlay() {
                $timeout(function () {
                    if (scope.inputtype == 'select') {
                        element.find('span').text(element.find('select option:selected').text());
                        scope.modelSQ.selectSpan = element.find('select option:selected').text();
                    }
                }, 1);
            }
        }
    };
});
