var _ = require('lodash');
var index = 0;

// number of images to show in the album preview grid
var PREVIEW_COUNT = 4;

var SORT_ALBUMS_BY = {
  title: function(album) { return album.title; },
  date: function(album) { return album.stats.fromDate; }
};

var SORT_MEDIA_BY = {
  filename: function(file) { return file.filename; },
  date: function(file) { return file.date; }
};

var PREVIEW_MISSING = {
  urls: {
    thumb: 'public/missing.png'
  }
};

function Album(opts) {
  if (typeof opts === 'string') opts = { title: opts };
  this.title = opts.title || ('Album ' + index++);
  this.filename = sanitise(this.title);
  this.files = opts.files || [];
  this.albums = opts.albums || [];
  this.depth = 0;
  this.home = false;
  this.stats = null;
  this.previews = null;
}

Album.prototype.finalize = function(options) {
  options = _.defaults(options, {
    sortAlbums: 'date',
    sortMedia: 'date'
  });
  // is this the top-level album?
  this.home = this.depth === 0;
  // finalize all nested albums first (recursive)
  // and set a nested filename
  for (var i = 0; i < this.albums.length; ++i) {
    this.albums[i].filename = this.filename + '-' + this.albums[i].filename;
    this.albums[i].depth = this.depth + 1;
    this.albums[i].finalize();
  }
  // perform stats & other calculations
  // once the nested albums have been finalized too
  this.calculateStats();
  this.calculateSummary();
  this.sort(options);
  this.pickPreviews();
};

Album.prototype.calculateStats = function() {
  // nested albums
  var nestedPhotos    = _.map(this.albums, 'stats.photos');
  var nestedVideos    = _.map(this.albums, 'stats.videos');
  var nestedFromDates = _.map(this.albums, 'stats.fromDate');
  var nestedToDates   = _.map(this.albums, 'stats.toDate');
  // current level
  var currentPhotos   = _.filter(this.files, {isVideo: false}).length;
  var currentVideos   = _.filter(this.files, {isVideo: true}).length;
  var currentFromDate = _.map(this.files, 'date');
  var currentToDate   = _.map(this.files, 'date');
  // aggregate all stats
  this.stats = {
    albums:   this.albums.length,
    photos:   _.sum(_.compact(_.concat(nestedPhotos, currentPhotos))) || 0,
    videos:   _.sum(_.compact(_.concat(nestedVideos, currentVideos))) || 0,
    fromDate: _.min(_.compact(_.concat(nestedFromDates, currentFromDate))),
    toDate:   _.max(_.compact(_.concat(nestedToDates, currentToDate)))
  };
  this.stats.total = this.stats.photos + this.stats.videos;
}

Album.prototype.calculateSummary = function() {
  var items = [
    itemCount(this.stats.albums, 'album'),
    itemCount(this.stats.photos, 'photo'),
    itemCount(this.stats.videos, 'video')
  ];
  this.summary = _.compact(items).join(', ');
};

Album.prototype.sort = function(options) {
  this.files = _.sortBy(this.files, SORT_MEDIA_BY[options.sortMedia]);
  this.albums = _.sortBy(this.albums, SORT_ALBUMS_BY[options.sortAlbums]);
  this.albums.forEach(function(nested) {
    nested.sort(options);
  });
};

Album.prototype.pickPreviews = function() {
  // also consider previews from nested albums
  var nestedPicks =  _.flatten(_.map(this.albums, 'previews')).filter(function(file) {
    return file !== PREVIEW_MISSING;
  });
  // then pick the top 4 overall
  var potentialPicks = _.concat(this.files, nestedPicks);
  this.previews = potentialPicks.slice(0, PREVIEW_COUNT);
  // and fill the gap with a placeholder
  var missing = PREVIEW_COUNT - this.previews.length;
  for (var i = 0; i < missing; ++i) {
    this.previews.push(PREVIEW_MISSING);
  }
};

function sanitise(filename) {
  return filename.replace(/[^a-z0-9-_]/ig, '');
}

function itemCount(count, type) {
  if (count === 0) return '';
  var plural = (count > 1) ? 's' : '';
  return '' + count + ' ' + type + plural;
}

module.exports = Album;

@@ -110,17 +110,18 @@
 COUNT = 
-4
+20
 ;%0A%0Avar S

Increase preview count for the Mosaic theme