define([
  'angular',
  'app',
  'lodash',
  'jquery',
],
function (angular, app, _) {
  'use strict';

  angular
    .module('grafana.controllers')
    .controller('ValueSelectDropdownCtrl', function($q) {
      var vm = this;

      vm.show = function() {
        vm.oldVariableText = vm.variable.current.text;
        vm.highlightIndex = -1;

        vm.options = vm.variable.options;
        vm.selectedValues = _.filter(vm.options, {selected: true});

        vm.tags = _.map(vm.variable.tags, function(value) {
          var tag = { text: value, selected: false };
          _.each(vm.variable.current.tags, function(tagObj) {
            if (tagObj.text === value) {
              tag = tagObj;
            }
          });
          return tag;
        });

        vm.search = {
          query: '',
          options: vm.options.slice(0, Math.min(vm.options.length, 1000))
        };

        vm.dropdownVisible = true;
      };

      vm.updateLinkText = function() {
        var current = vm.variable.current;

        if (current.tags && current.tags.length) {
          // filer out values that are in selected tags
          var selectedAndNotInTag = _.filter(vm.variable.options, function(option) {
            if (!option.selected) { return false; }
            for (var i = 0; i < current.tags.length; i++)  {
              var tag = current.tags[i];
              if (_.indexOf(tag.values, option.value) !== -1) {
                return false;
              }
            }
            return true;
          });

          // convert values to text
          var currentTexts = _.pluck(selectedAndNotInTag, 'text');

          // join texts
          vm.linkText = currentTexts.join(' + ');
          if (vm.linkText.length > 0) {
            vm.linkText += ' + ';
          }
        } else {
          vm.linkText = vm.variable.current.text;
        }
      };

      vm.clearSelections = function() {
        _.each(vm.options, function(option) {
          option.selected = false;
        });

        vm.selectionsChanged(false);
      };

      vm.selectTag = function(tag) {
        tag.selected = !tag.selected;
        var tagValuesPromise;
        if (!tag.values) {
          tagValuesPromise = vm.getValuesForTag({tagKey: tag.text});
        } else {
          tagValuesPromise = $q.when(tag.values);
        }

        tagValuesPromise.then(function(values) {
          tag.values = values;
          tag.valuesText = values.join(' + ');
          _.each(vm.options, function(option) {
            if (_.indexOf(tag.values, option.value) !== -1) {
              option.selected = tag.selected;
            }
          });

          vm.selectionsChanged(false);
        });
      };

      vm.keyDown = function (evt) {
        if (evt.keyCode === 27) {
          vm.hide();
        }
        if (evt.keyCode === 40) {
          vm.moveHighlight(1);
        }
        if (evt.keyCode === 38) {
          vm.moveHighlight(-1);
        }
        if (evt.keyCode === 13) {
          if (vm.search.options.length === 0) {
            vm.commitChanges();
          } else {
            vm.selectValue(vm.search.options[vm.highlightIndex], {}, true, false);
          }
        }
        if (evt.keyCode === 32) {
          vm.selectValue(vm.search.options[vm.highlightIndex], {}, false, false);
        }
      };

      vm.moveHighlight = function(direction) {
        vm.highlightIndex = (vm.highlightIndex + direction) % vm.search.options.length;
      };

      vm.selectValue = function(option, event, commitChange, excludeOthers) {
        if (!option) { return; }

        option.selected = !option.selected;

        commitChange = commitChange || false;
        excludeOthers = excludeOthers || false;

        var setAllExceptCurrentTo = function(newValue) {
          _.each(vm.options, function(other) {
            if (option !== other) { other.selected = newValue; }
          });
        };

        // commit action (enter key), should not deselect it
        if (commitChange) {
          option.selected = true;
        }

        if (option.text === 'All' || excludeOthers) {
          setAllExceptCurrentTo(false);
          commitChange = true;
        }
        else if (!vm.variable.multi) {
          setAllExceptCurrentTo(false);
          commitChange = true;
        } else if (event.ctrlKey || event.metaKey || event.shiftKey) {
          commitChange = true;
          setAllExceptCurrentTo(false);
        }

        vm.selectionsChanged(commitChange);
      };

      vm.selectionsChanged = function(commitChange) {
        vm.selectedValues = _.filter(vm.options, {selected: true});

        if (vm.selectedValues.length > 1 && vm.selectedValues.length !== vm.options.length) {
          if (vm.selectedValues[0].text === 'All') {
            vm.selectedValues[0].selected = false;
            vm.selectedValues = vm.selectedValues.slice(1, vm.selectedValues.length);
          }
        }

        // validate selected tags
        _.each(vm.tags, function(tag) {
          if (tag.selected)  {
            _.each(tag.values, function(value) {
              if (!_.findWhere(vm.selectedValues, {value: value})) {
                tag.selected = false;
              }
            });
          }
        });

        vm.selectedTags = _.filter(vm.tags, {selected: true});
        vm.variable.current.value = _.pluck(vm.selectedValues, 'value');
        vm.variable.current.text = _.pluck(vm.selectedValues, 'text').join(' + ');
        vm.variable.current.tags = vm.selectedTags;

        // only single value
        if (vm.selectedValues.length === 1) {
          vm.variable.current.value = vm.selectedValues[0].value;
        }

        if (commitChange) {
          vm.commitChanges();
        }
      };

      vm.commitChanges = function() {
        // if we have a search query and no options use that
        if (vm.search.options.length === 0 && vm.search.query.length > 0) {
          vm.variable.current = {text: vm.search.query, value: vm.search.query};
        }
        else if (vm.selectedValues.length === 0) {
          // make sure one option is selected
          vm.options[0].selected = true;
          vm.selectionsChanged(false);
        }

        vm.dropdownVisible = false;
        vm.updateLinkText();

        if (vm.variable.current.text !== vm.oldVariableText) {
          vm.onUpdated();
        }
      };

      vm.queryChanged = function() {
        vm.highlightIndex = -1;
        vm.search.options = _.filter(vm.options, function(option) {
          return option.text.toLowerCase().indexOf(vm.search.query.toLowerCase()) !== -1;
        });

        vm.search.options = vm.search.options.slice(0, Math.min(vm.search.options.length, 1000));
      };

      vm.init = function() {
        vm.selectedTags = vm.variable.current.tags || [];
        vm.updateLinkText();
      };

    });

  angular
    .module('grafana.directives')
    .directive('valueSelectDropdown', function($compile, $window, $timeout, $rootScope) {

      return {
        scope: { variable: "=", onUpdated: "&", getValuesForTag: "&" },
        templateUrl: 'app/partials/valueSelectDropdown.html',
        controller: 'ValueSelectDropdownCtrl',
        controllerAs: 'vm',
        bindToController: true,
        link: function(scope, elem) {
          var bodyEl = angular.element($window.document.body);
          var linkEl = elem.find('.variable-value-link');
          var inputEl = elem.find('input');

          function openDropdown() {
            inputEl.css('width', Math.max(linkEl.width(), 30) + 'px');

            inputEl.show();
            linkEl.hide();

            inputEl.focus();
            $timeout(function() { bodyEl.on('click', bodyOnClick); }, 0, false);
          }

          function switchToLink() {
            inputEl.hide();
            linkEl.show();
            bodyEl.off('click', bodyOnClick);
          }

          function bodyOnClick (e) {
            if (elem.has(e.target).length === 0) {
              scope.$apply(function() {
                scope.vm.commitChanges();
              });
            }
          }

          scope.$watch('vm.dropdownVisible', function(newValue) {
            if (newValue) {
              openDropdown();
            } else {
              switchToLink();
            }
          });

          var cleanUp = $rootScope.$on('template-variable-value-updated', function() {
            scope.vm.updateLinkText();
          });

          scope.$on("$destroy", function() {
            cleanUp();
          });

          scope.vm.init();
        },
      };
    });

});
