app.factory('Repeater', function() {

function Repeater(expression) {
  var match = expression.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+track\s+by\s+([\s\S]+?))?\s*$/);

  var lhs = match[1];
  this.rhs = match[2];
  this.aliasAs = match[3];
  this.trackByExp = match[4];

  match = lhs.match(/^(?:(\s*[\$\w]+)|\(\s*([\$\w]+)\s*,\s*([\$\w]+)\s*\))$/);

  this.valueIdentifier = match[3] || match[1];
  this.keyIdentifier = match[2];
}

Repeater.prototype.$watch = function(fn) {
  this.$scope.$watchCollection(this.rhs, fn);
};

Repeater.prototype.$transclude = function(length, value, key, index, fn) {
  var self = this;
  self._transclude(function(clone, scope) {
    scope[self.valueIdentifier] = value;
    if (self.keyIdentifier) scope[self.keyIdentifier] = key;
    scope.$index = index;
    scope.$first = (index === 0);
    scope.$last = (index === (length - 1));
    scope.$middle = !(scope.$first || scope.$last);
    // jshint bitwise: false
    scope.$odd = !(scope.$even = (index&1) === 0);
    // jshint bitwise: true
    fn(clone, scope);
  });
};

return {
  compile: function(repeatAttr, linkFn) {
    return function(element, attrs) {
      var expression = attrs[repeatAttr];
      var repeater = new Repeater(expression);
      return function($scope, $element, $attr, ctrl, $transclude) {
        repeater._transclude = $transclude;
        repeater.$scope = $scope;
        linkFn($scope, $element, $attr, ctrl, repeater);
      };
    };
  }
};

}); app.directive('readableList', function(Repeater) {

return {
  restrict: 'E',
  transclude: true,
  compile: Repeater.compile('repeat', function($scope, $element, $attr, ctrl, $repeat) {
    $repeat.$watch(function(inputList) {
      $element.empty();
      if (inputList.length == 1) {
        $repeat.$transclude(1, inputList[0], null, 0, function(clone) {
          $element.append(clone);
        });
      } else {
        var finalJoin = ' and ';

        var join = ', ',
            arr = inputList.slice(0),
            last = arr.pop(),
            beforeLast = arr.pop();

        _.each(arr, function(data, index) {
          $repeat.$transclude(inputList.length, data, null, index, function(clone) {
            $element.append(clone);
            $element.append(document.createTextNode(join));
          });
        });
        $repeat.$transclude(inputList.length, beforeLast, null, inputList.length - 2, function(clone) {
          $element.append(clone);
          $element.append(document.createTextNode(finalJoin));
        });
        $repeat.$transclude(inputList.length, last, null, inputList.length - 1, function(clone) {
          $element.append(clone);
        });
      }
    });
  })
};

});