/**
 * Directives which show, hide, include or otherwise mutate the DOM based on Features and Config.
 */

/**
 * Adds a quay-require attribute includes an element in the DOM iff the features specified are true.
 */
angular.module('quay').directive('quayRequire', function ($animate, Features, AngularHelper) {
  return {
    transclude: 'element',
    priority: 600,
    terminal: true,
    restrict: 'A',
    link: AngularHelper.buildConditionalLinker($animate, 'quayRequire', function(value) {
      return Features.matchesFeatures(value);
    })
  };
});

/**
 * Adds a quay-show attribute that shows the element only if the attribute evaluates to true.
 * The Features and Config services are added into the scope's context automatically.
 */
angular.module('quay').directive('quayShow', function($animate, Features, Config) {
  return {
    priority: 590,
    restrict: 'A',
    link: function($scope, $element, $attr, ctrl, $transclude) {
      $scope.Features = Features;
      $scope.Config = Config;
      $scope.$watch($attr.quayShow, function(result) {
        $animate[!!result ? 'removeClass' : 'addClass']($element, 'ng-hide');
      });
    }
  };
});


/**
 * Adds a quay-section attribute that adds an 'active' class to the element if the current URL
 * matches the given section.
 */
angular.module('quay').directive('quaySection', function($animate, $location, $rootScope) {
  return {
    priority: 590,
    restrict: 'A',
    link: function($scope, $element, $attr, ctrl, $transclude) {
      var update = function() {
        var result = $location.path().indexOf('/' + $attr.quaySection) == 0;
        $animate[!result ? 'removeClass' : 'addClass']($element, 'active');
      };

      $scope.$watch(function(){
        return $location.path();
      }, update);

      $scope.$watch($attr.quaySection, update);
    }
  };
});

/**
 * Adds a quay-classes attribute that performs like ng-class, but with Features and Config also
 * available in the scope automatically.
 */
angular.module('quay').directive('quayClasses', function(Features, Config) {
  return {
    priority: 580,
    restrict: 'A',
    link: function($scope, $element, $attr, ctrl, $transclude) {

      // Borrowed from ngClass.
      function flattenClasses(classVal) {
        if(angular.isArray(classVal)) {
          return classVal.join(' ');
        } else if (angular.isObject(classVal)) {
          var classes = [], i = 0;
          angular.forEach(classVal, function(v, k) {
            if (v) {
              classes.push(k);
            }
          });
          return classes.join(' ');
        }

        return classVal;
      }

      function removeClass(classVal) {
        $attr.$removeClass(flattenClasses(classVal));
      }


      function addClass(classVal) {
        $attr.$addClass(flattenClasses(classVal));
      }

      $scope.$watch($attr.quayClasses, function(result) {
        var scopeVals = {
          'Features': Features,
          'Config': Config
        };

        for (var expr in result) {
          if (!result.hasOwnProperty(expr)) { continue; }

          // Evaluate the expression with the entire features list added.
          var value = $scope.$eval(expr, scopeVals);
          if (value) {
            addClass(result[expr]);
          } else {
            removeClass(result[expr]);
          }
        }
      });
    }
  };
});

/**
 * Adds a quay-include attribtue that adds a template solely if the expression evaluates to true.
 * Automatically adds the Features and Config services to the scope.
 *
  Usage: quay-include="{'Features.BILLING': 'partials/landing-normal.html', '!Features.BILLING': 'partials/landing-login.html'}"
 */
angular.module('quay').directive('quayInclude', function($compile, $templateCache, $http, Features, Config) {
  return {
    priority: 595,
    restrict: 'A',
    link: function($scope, $element, $attr, ctrl) {
      var getTemplate = function(templateName) {
        var templateUrl = '/static/' + templateName;
        return $http.get(templateUrl, {cache: $templateCache});
      };

      var result = $scope.$eval($attr.quayInclude);
      if (!result) {
        return;
      }

      var scopeVals = {
        'Features': Features,
        'Config': Config
      };

      var templatePath = null;
      for (var expr in result) {
        if (!result.hasOwnProperty(expr)) { continue; }

        // Evaluate the expression with the entire features list added.
        var value = $scope.$eval(expr, scopeVals);
        if (value) {
          templatePath = result[expr];
          break;
        }
      }

      if (!templatePath) {
        return;
      }

      var promise = getTemplate(templatePath).success(function(html) {
        $element.html(html);
      }).then(function (response) {
        $element.replaceWith($compile($element.html())($scope));
        if ($attr.onload) {
          $scope.$eval($attr.onload);
        }
      });
    }
  };
});
