(function () {
    'use strict';
    angular.module('api.module.Components')

            /**
             * @class api.module.Components.directives.EloInputAttr
             * @ngDirective
             * @xtype elo-input-attr
             * 
             * @private
             */
            .directive('eloInputAttr', ['$compile', function ($compile) {
                    return {
                        priority: 1001, // compiles first
                        terminal: true, // prevent lower priority directives to compile after it
                        require: '^eloInput',
                        scope: false,
                        compile: function (el) {
                            el.removeAttr('elo-input-attr'); // necessary to avoid infinite compile loop
                            return function (scope, element, attrs, parentCtrl) {
                                if (scope.config.disabled) {
                                    element.attr('disabled', 'true');
                                }
                                if (scope.config.kwl) {
                                    element.attr('uib-typeahead', scope.typeaheadString);
                                    element.attr('typeahead-template-url', scope.config.kwl.templateUrl);
                                    element.attr('typeahead-editable', !scope.config.kwl.onlyFromKwl);
                                    element.attr('typeahead-min-length', scope.config.kwl.typeaheadMinLength);
                                    element.attr('typeahead-loading', 'typeaheadLoading');
                                    element.attr('typeahead-no-results', 'typeaheadNoResults');
                                    element.attr('typeahead-wait-ms', scope.typeaheadWaitMs);
                                    if (scope.config.kwl.onSelect) {
                                        element.attr('typeahead-on-select', "config.kwl.onSelect($item, $model, $label, model)");
                                    }
                                    if (scope.config.kwl.popupUrl) {
                                        element.attr('typeahead-popup-template-url', scope.config.kwl.popupUrl);
                                    }
                                } else if (!scope.config.kwl && scope.config.datePickerPopup) {
                                    element.attr('uib-datepicker-popup', scope.config.datePickerPopup);
                                    element.attr('is-open', 'store.datePickerIsOpen');
                                    element.attr('current-text', api.helpers.Text.getText('ELOCMP.INPUT.CURRENTTEXT'));
                                    element.attr('clear-text', api.helpers.Text.getText('ELOCMP.INPUT.CLEARTEXT'));
                                    element.attr('close-text', api.helpers.Text.getText('ELOCMP.INPUT.CLOSETEXT'));
                                }

                                parentCtrl.compile(element);
                            };
                        }
                    };
                }])
            /**
             * @class api.module.Components.directives.EloInput
             * @ngDirective
             * @xtype elo-input
             * 
             * This directive renders an input field and provides functionality 
             * for label, validation, keywordlists etc.
             * 
             * # Example:
             * 
             *     <elo-input config="store.config" model="store.model"></elo-input>
             * 
             * @cfg {Object} config
             * The configuration object. It defines the complete behavior of the input field.
             * The config model is final, after setting it changes to that object
             * will result in undefined behaviour. The object contains:
             * 
             * @cfg {String} config.name
             * [Required] The name this input writes its value to in the model object.
             * @cfg {String} config.label
             * The label of the input field.
             * @cfg {String} config.placeholder
             * A placeholder text if the input field is empty.
             * @cfg {Boolean} config.hidden
             * true to hide this input field (invisible inputs).
             * @cfg {Boolean} config.disabled
             * true to diable the input field.
             * @cfg {Object} config.validation
             * The validation object, holding:
             * @cfg {Number} config.validation.maxLength
             * The maximal number of characters the field may contain.
             * @cfg {String} config.validation.maxLengthMsg
             * The message to show if maxLength is violated. 
             * @cfg {Number} config.validation.minLength
             * The minimal number of characters the field needs.
             * @cfg {String} config.validation.minLengthMsg
             * The message to show if minLength is violated. 
             * @cfg {Boolean} config.validation.required
             * true if this field may not be empty.
             * @cfg {String} config.validation.requiredMsg
             * The message to show if required is violated. 
             * @cfg {String} config.validation.pattern
             * A regular expression string the input value must match.
             * @cfg {String} config.validation.patternMsg
             * The message to show if pattern is violated. 
             * @cfg {Object} config.kwl
             * The configuration for a keyword list (typeahead). It contains:
             * @cfg {Function} config.kwl.getEntries
             * The function to obtain the keywords. This function gets called with 
             * $scope and $q objects and should return a promise or an array of objects.
             * @cfg {String} config.kwl.valueKey
             * The key where to get the value from objects returned by getEntries
             * @cfg {String} config.kwl.viewKey
             * The key what should be displayed in the list from objects returned by getEntries
             * @cfg {Number} config.kwl.limit
             * The limit of entries shown in the keyword list.
             * @cfg {String} config.datePickerPopup
             * The date format used to render the selected date. (See angular ui datepicker)
             *  If this is set no keywordlist is possible. The model will get the date object as value.
             * 
             * @cfg {Object} model
             * The model the value of the input field will be written to. 
             * The value is set to model[config.name] so the same model object 
             * can be used for several input fields.
             * 
             */
            .directive('eloInput', ['$compile', function ($compile) {
                    return {
                        restrict: 'E',
                        replace: true,
                        controller: ['$scope', '$filter', '$q', '$templateCache', function ($scope, $filter, $q, $templateCache) {
                                this.compile = function (element) {
                                    $compile(element)($scope);
                                };
                                $scope.store = $scope.store || {};
                                
                                if ($scope.config.kwl) {
                                    if ($scope.config.kwl.typeaheadMinLength === null || $scope.config.kwl.typeaheadMinLength === undefined) {
                                        $scope.config.kwl.typeaheadMinLength = 1;
                                    }
                                    $scope.typeaheadWaitMs = 200;
                                    $scope.typeaheadString = ['entry', ($scope.config.kwl.valueKey ? '.' : ''), $scope.config.kwl.valueKey, ' as entry', ($scope.config.kwl.viewKey ? '.' : ''), $scope.config.kwl.viewKey, ' for entry in internalGetEntries()'].join('');
                                }
                                $scope.internalGetEntries = function () {
                                    var promise1 = $q.when($scope.config.kwl.getEntries($scope, $q, $templateCache)),
                                            promise2 = $q(function (resolve, reject) {
                                                promise1.then(function (entries) {
                                                    var search = {};
                                                    // do not filter for both since name and id cannot be the same viewValue
//                                                    if ($scope.config.kwl.valueKey) {
//                                                        search[$scope.config.kwl.valueKey] = $scope.form[$scope.config.name].$viewValue;
//                                                    }
                                                    if ($scope.config.kwl.viewKey) {
                                                        search[$scope.config.kwl.viewKey] = $scope.form[$scope.config.name].$viewValue;
                                                    }
                                                    entries = $filter('filter')(entries, search);
                                                    entries = $filter('limitTo')(entries, $scope.config.kwl.limit);
                                                    resolve(entries);
                                                    return entries;
                                                });
                                            });
                                    return promise2;
                                };
                                $scope.internalTriggerFct = function () {
                                    if (!$scope.config.triggerFct) {
                                        elo.helpers.Console.error("api.module.Components.eloInput: No trigger function given.");
                                    } else {
                                        $scope.config.triggerFct($scope);
                                    }
                                };
                                
                                $scope.getInvalidMsg = function(config, error) {
                                    if (!config || !config.validation || !error) {
                                        return undefined;
                                    }
                                    if (config.validation.maxLength && config.validation.maxLengthMsg && error.maxlength) {
                                        return config.validation.maxLengthMsg;
                                    } else if (config.validation.minLength && config.validation.minLengthMsg && error.minlength) {
                                        return config.validation.minLengthMsg;
                                    } else if (config.validation.required && config.validation.requiredMsg && error.required) {
                                        return config.validation.requiredMsg;
                                    } else if (config.validation.pattern && config.validation.patternMsg && error.pattern) {
                                        return config.validation.patternMsg;
                                    } else if (config.datePickerPopup && config.validation.dateMsg && error.date) {
                                        return config.validation.dateMsg;
                                    } else if (config.kwl && config.kwl.onlyFromKwl && error.editable) {
                                        return config.validation.editableMsg;
                                    }
                                };
                                
                                if ($scope.store.hasTrigger === undefined) {
                                    $scope.store.hasTrigger = $scope.config.triggerFct && !$scope.config.disabled;
                                }
                                
                                // templateUrl
                                if ($scope.config.kwl && $scope.config.kwl.popupUrl && $scope.config.kwl.popupUrl.indexOf('POPUP-DYNAMIC-KEYWORDLIST') === 0 && !$templateCache.get($scope.config.kwl.popupUrl)) {
                                    var sb = new api.helpers.StringBuilder();
                                    sb.append("<ul class=\"container dropdown-menu\" ng-show=\"isOpen() && !moveInProgress\" ng-style=\"{top: position().top+'px', left: position().left+'px'}\" role=\"listbox\" aria-hidden=\"{{!isOpen()}}\">");
                                    sb.append("<div class=\"elo-input-dynkwl-header row\" id=\"").append($scope.config.kwl.popupUrl).append("\" elo-template></div>");
                                    sb.append("<li role=\"presentation\" class=\"divider\"></li>");
                                    sb.append("<li ng-repeat=\"match in matches track by $index\" ng-class=\"{active: isActive($index) }\" ng-mouseenter=\"selectActive($index)\" ng-click=\"selectMatch($index, $event)\" role=\"option\" id=\"{{::match.id}}\">");
                                    sb.append("<div uib-typeahead-match index=\"$index\" match=\"match\" query=\"query\" template-url=\"templateUrl\"></div>");
                                    sb.append("</li>");
                                    sb.append("</ul>");
                                    $templateCache.put($scope.config.kwl.popupUrl, sb.toString());
                                }
                            }],
                        link: function ($scope, elem, attr, formCtrl) {
                            $scope.form = $scope[$scope.config.name + 'Form'];
                            $scope.$watch('form.$error', function(error) {
                                if ($scope.config.focused && !angular.equals({}, $scope.form.$error)) {
                                    $scope.store.popover = $scope.getInvalidMsg($scope.config, $scope.form.$error);
                                    $scope.store.popoverIsOpen = true;
                                } else {
                                    $scope.store.popoverIsOpen = false;
                                }
                            });
                            $scope.$watch('config.focused', function(focused) {
                                if ($scope.config.focused && !angular.equals({}, $scope.form.$error)) {
                                    $scope.store.popover = $scope.getInvalidMsg($scope.config, $scope.form.$error);
                                    $scope.store.popoverIsOpen = true;
                                } else {
                                    $scope.store.popoverIsOpen = false;
                                }
                            });
                            setTimeout(function () {
                                $scope.inputEl = elem.find('input')[0];
                                $scope.$apply();
                            }, 100);
                        },
                        scope: {
                            config: '=',
                            model: '='
                        },
                        templateUrl: api.module.Components.relativeUrl + 'directives/input/input.html'
                    };
                }]);
}());