Source: lib/collection.js

var eol = require('eol')
var Album = require('./album')
var CSV = require('./csv')
var Queue = require('./queue')
var SpotifyRequestHandler = require('./spotify')
var Track = require('./track')

/**
 * Create a playlist collection.
 * @constructor
 * @param {SpotifyRequestHandler} [spotify] - Spotify request handler.
 */
function Collection (spotify) {
  /**
   * Playlist alternating.
   */
  this.alternating = null

  /**
   * Whether to output as CSV.
   */
  this.csv = false

  /**
   * List of entries.
   */
  this.entries = new Queue()

  /**
   * Playlist grouping.
   */
  this.grouping = null

  /**
   * Last.fm user.
   */
  this.lastfmUser = null

  /**
   * Playlist order.
   */
  this.ordering = null

  /**
   * Whether to reverse the playlist order.
   */
  this.reverse = false

  /**
   * Whether to shuffle the playlist.
   */
  this.shuffle = false

  /**
   * Whether to remove duplicates.
   */
  this.unique = true

  /**
   * Spotify request handler.
   */
  this.spotify = spotify || new SpotifyRequestHandler()
}

/**
 * Add an entry to the end of the collection queue.
 * @param {Track | Album | Artist} entry -
 * The entry to add.
 */
Collection.prototype.add = function (entry) {
  this.entries.add(entry)
}

/**
 * Alternate the collection entries.
 */
Collection.prototype.alternate = function () {
  var self = this
  if (this.alternating === 'artist') {
    return this.entries.alternate(function (track) {
      return track.mainArtist.toLowerCase()
    })
  } else if (this.alternating === 'album') {
    return this.refreshTracks().then(function () {
      return self.entries.alternate(function (track) {
        return track.album.toLowerCase()
      })
    })
  } else if (this.alternating === 'entry') {
    return this.entries.alternate(function (track) {
      return track.entry.toLowerCase()
    })
  } else {
    return Promise.resolve(this.entries)
  }
}

/**
 * Remove duplicate entries.
 * @return {Promise | Collection} - Itself.
 */
Collection.prototype.dedup = function () {
  if (this.unique) {
    return this.entries.dedup()
  }
  return Promise.resolve(this.entries)
}

/**
 * Dispatch all the entries in the collection.
 * @return {Promise | Queue} A queue of results.
 */
Collection.prototype.dispatch = function () {
  var self = this
  return this.fetchTracks().then(function () {
    return self.dedup()
  }).then(function () {
    return self.order()
  }).then(function () {
    return self.group()
  }).then(function () {
    return self.alternate()
  }).then(function () {
    return self.reorder()
  })
}

/**
 * Dispatch all the entries in the collection
 * and return the track listing.
 * @return {Promise | string} A newline-separated list
 * of Spotify URIs.
 */
Collection.prototype.execute = function () {
  var self = this
  return this.dispatch().then(function () {
    return self.toString()
  })
}

/**
 * Fetch Last.fm metadata of each collection entry.
 * @return {Promise | Queue} A queue of results.
 */
Collection.prototype.fetchLastfm = function () {
  var self = this
  return this.entries.forEachPromise(function (entry) {
    return entry.fetchLastfm(self.lastfmUser)
  })
}

/**
 * Dispatch the entries in the collection.
 * @return {Promise} A Promise to perform the action.
 */
Collection.prototype.fetchTracks = function () {
  var self = this
  return this.entries.dispatch().then(function (queue) {
    self.entries = queue.flatten()
    return self.entries
  })
}

/**
 * Group the collection entries.
 */
Collection.prototype.group = function () {
  var self = this
  if (this.grouping === 'artist') {
    return this.entries.group(function (track) {
      return track.mainArtist.toLowerCase()
    })
  } else if (this.grouping === 'album') {
    return this.refreshTracks().then(function () {
      return self.entries.group(function (track) {
        return track.album.toLowerCase()
      })
    })
  } else if (this.grouping === 'entry') {
    return this.entries.group(function (track) {
      return track.entry.toLowerCase()
    })
  } else {
    return Promise.resolve(this.entries)
  }
}

/**
 * Order the collection entries.
 * @return {Promise} A Promise to perform the action.
 */
Collection.prototype.order = function () {
  var self = this
  if (this.ordering === 'popularity') {
    return this.refreshTracks().then(function () {
      self.entries.orderByPopularity()
    })
  } else if (this.ordering === 'lastfm') {
    return this.fetchLastfm().then(function () {
      self.entries.orderByLastfm()
    })
  } else {
    return Promise.resolve(this.entries)
  }
}

/**
 * Print the collection to the console.
 */
Collection.prototype.print = function () {
  console.log(this.toString())
}

/**
 * Refresh the metadata of each collection entry.
 * @return {Promise} A Promise to perform the action.
 */
Collection.prototype.refreshTracks = function () {
  var self = this
  return this.entries.dispatch().then(function (result) {
    self.entries = result.flatten()
  })
}

/**
 * Reverse the order of the entries.
 * @return {Promise | Collection} - Itself.
 */
Collection.prototype.reorder = function () {
  if (this.reverse) {
    return this.entries.reverse()
  } else if (this.shuffle) {
    return this.entries.shuffle()
  }
  return Promise.resolve(this.entries)
}

/**
 * Convert the collection to a string.
 * @return {string} A newline-separated list of Spotify URIs.
 */
Collection.prototype.toString = function () {
  var self = this
  var result = ''
  if (self.csv) {
    result += 'sep=,\n'
  }
  this.entries.forEach(function (entry) {
    if (entry instanceof Track || entry instanceof Album) {
      if (entry instanceof Track) {
        var log = entry.title
        if (entry.popularity || entry.lastfm) {
          log += ' ('
          log += entry.popularity ? ('Spotify popularity: ' + entry.popularity) : ''
          log += (entry.popularity && entry.lastfm) ? ', ' : ''
          log += entry.lastfm ? 'Last.fm rating: ' + entry.lastfm : ''
          log += ')'
        }
        console.log(log)
      }
      if (self.csv) {
        var csvFormat = new CSV(entry)
        var csvLine = csvFormat.toString()
        result += csvLine + '\n'
      } else {
        if (entry.uri) {
          result += entry.uri + '\n'
        }
      }
    }
  })
  result = eol.auto(result.trim())
  return result
}

module.exports = Collection