/**
 * An element which displays the changes visualization panel for a repository view.
 */
angular.module('quay').directive('repoPanelChanges', function () {
  var RepositoryImageTracker = function(repository, imageLoader) {
    this.repository = repository;
    this.imageLoader = imageLoader;

    // Build a map of image ID -> image.
    var images = imageLoader.images;
    var imageIDMap = {};

    images.forEach(function(image) {
      imageIDMap[image.id] = image;
    });

    this.imageMap_ = imageIDMap;
  };

  RepositoryImageTracker.prototype.imageLink = function(image) {
    return '/repository/' + this.repository.namespace + '/' +
           this.repository.name + '/image/' + image;
  };

  RepositoryImageTracker.prototype.getImageForTag = function(tag) {
    var tagData = this.lookupTag(tag);
    if (!tagData) { return null; }

    return this.imageMap_[tagData.image_id];
  };

  RepositoryImageTracker.prototype.lookupTag = function(tag) {
    return this.repository.tags[tag];
  };

  RepositoryImageTracker.prototype.lookupImage = function(image) {
    return this.imageMap_[image];
  };

  RepositoryImageTracker.prototype.forAllTagImages = function(tag, callback) {
    var tagData = this.lookupTag(tag);
    if (!tagData) { return; }

    var tagImage =  this.imageMap_[tagData.image_id];
    if (!tagImage) { return; }

    // Callback the tag's image itself.
    callback(tagImage);

    // Callback any parent images.
    if (!tagImage.ancestors) { return; }

    var ancestors = tagImage.ancestors.split('/');
    for (var i = 0; i < ancestors.length; ++i) {
      var image = this.imageMap_[ancestors[i]];
      if (image) {
        callback(image);
      }
    }
  };

  RepositoryImageTracker.prototype.getTotalSize = function(tag) {
    var size = 0;
    this.forAllTagImages(tag, function(image) {
      size += image.size;
    });
    return size;
  };

  RepositoryImageTracker.prototype.getImagesForTagBySize = function(tag) {
    var images = [];
    this.forAllTagImages(tag, function(image) {
      images.push(image);
    });

    images.sort(function(a, b) {
      return b.size - a.size;
    });

    return images;
  };

  ///////////////////////////////////////////////////////////////////////////////////////

  var directiveDefinitionObject = {
    priority: 0,
    templateUrl: '/static/directives/repo-view/repo-panel-changes.html',
    replace: false,
    transclude: false,
    restrict: 'C',
    scope: {
      'repository': '=repository',
      'selectedTags': '=selectedTags',

      'imagesResource': '=imagesResource',
      'imageLoader': '=imageLoader',

      'isEnabled': '=isEnabled'
    },
    controller: function($scope, $element, $timeout, ApiService, UtilService, ImageMetadataService) {
      $scope.tagNames = [];
      $scope.loading = true;

      $scope.$watch('selectedTags', function(selectedTags) {
        if (!selectedTags) { return; }
        $scope.selectedTagsSlice = selectedTags.slice(0, 10);
      });

      var update = function() {
        if (!$scope.repository || !$scope.isEnabled) { return; }

        $scope.tagNames = Object.keys($scope.repository.tags);
        $scope.currentImage = null;
        $scope.currentTag = null;

        $scope.loading = true;
        $scope.imageLoader.loadImages($scope.selectedTagsSlice, function() {
          $scope.loading = false;
          updateImages();
        });
      };

      var updateImages = function() {
        if (!$scope.repository || !$scope.imageLoader || !$scope.isEnabled) { return; }

        $scope.tracker = new RepositoryImageTracker($scope.repository, $scope.imageLoader);
        if ($scope.selectedTagsSlice && $scope.selectedTagsSlice.length) {
          refreshTree();
        }
      };

      $scope.$watch('selectedTagsSlice', update)
      $scope.$watch('repository', update);
      $scope.$watch('isEnabled', update);

      $scope.updateState = function() {
        update();
      };

      var refreshTree = function() {
        if (!$scope.repository || !$scope.imageLoader || !$scope.isEnabled) { return; }
        if ($scope.selectedTagsSlice.length < 1) { return; }

        $('#image-history-container').empty();

        var getTagsForImage = function(image) {
          return $scope.imageLoader.getTagsForImage(image);
        };

        var tree = new ImageHistoryTree(
            $scope.repository.namespace,
            $scope.repository.name,
            $scope.imageLoader.images,
            getTagsForImage,
            UtilService.getFirstMarkdownLineAsText,
            $scope.getTimeSince,
            ImageMetadataService.getEscapedFormattedCommand,
            function(tag) {
              return $.inArray(tag, $scope.selectedTagsSlice) >= 0;
            });

        $scope.tree = tree.draw('image-history-container');
        if ($scope.tree) {
          // Give enough time for the UI to be drawn before we resize the tree.
          $timeout(function() {
            $scope.tree.notifyResized();
            $scope.setTag($scope.selectedTagsSlice[0]);
          }, 100);

          // Listen for changes to the selected tag and image in the tree.
          $($scope.tree).bind('tagChanged', function(e) {
            $scope.$apply(function() { $scope.setTag(e.tag); });
          });

          $($scope.tree).bind('imageChanged', function(e) {
            $scope.$apply(function() { $scope.setImage(e.image.id); });
          });
        }
      };

      $scope.setImage = function(image_id) {
        $scope.currentTag = null;
        $scope.currentImage = image_id;
        $scope.tree.setImage(image_id);
      };

      $scope.setTag = function(tag) {
        $scope.currentTag = tag;
        $scope.currentImage = null;
        $scope.tree.setTag(tag);
      };

      $scope.parseDate = function(dateString) {
        return Date.parse(dateString);
      };

      $scope.getTimeSince = function(createdTime) {
        return moment($scope.parseDate(createdTime)).fromNow();
      };

      $scope.handleTagChanged = function(data) {
        data.removed.map(function(tag) {
          $scope.currentImage = null;
          $scope.currentTag = null;
        });

        data.added.map(function(tag) {
          $scope.selectedTags.push(tag);
          $scope.currentTag = tag;
        });

        update();
      };
    }
  };
  return directiveDefinitionObject;
});