Source: lib/track.js

var defaults = require('./defaults')
var lastfm = require('./lastfm')(defaults.api)
var SpotifyRequestHandler = require('./spotify')

/**
 * Create track entry.
 * @constructor
 * @param {SpotifyRequestHandler} spotify - Spotify request handler.
 * @param {string} entry - The track to search for.
 * @param {string} [id] - The Spotify ID, if known.
 */
function Track (spotify, entry, id) {
  /**
   * Album name.
   */
  this.album = ''

  /**
   * Track artists, separated by `,`.
   */
  this.artist = ''

  /**
   * Array of track artists.
   */
  this.artists = []

  /**
   * Entry string.
   */
  this.entry = ''

  /**
   * Spotify ID.
   */
  this.id = ''

  /**
   * Last.fm global playcount.
   */
  this.lastfm = ''

  /**
   * Last.fm global playcount.
   */
  this.lastfmGlobal = ''

  /**
   * Last.fm personal playcount.
   */
  this.lastfmPersonal = ''

  /**
   * Main track artist.
   */
  this.mainArtist = ''

  /**
   * Track name.
   */
  this.name = ''

  /**
   * Spotify popularity.
   */
  this.popularity = ''

  /**
   * Spotify request handler.
   */
  this.spotify = null

  /**
   * Full track name on the form `Title - Artist`.
   */
  this.title = ''

  /**
   * Spotify URI
   * (a string on the form `spotify:track:xxxxxxxxxxxxxxxxxxxxxx`).
   */
  this.uri = ''

  this.entry = entry.trim()
  this.id = id
  this.name = entry
  this.spotify = spotify || new SpotifyRequestHandler()
  this.uri = this.id ? ('spotify:track:' + this.id) : this.uri
}

/**
 * Clone a JSON response.
 * @param {Object} response - The response.
 */
Track.prototype.clone = function (response) {
  for (var prop in response) {
    if (response.hasOwnProperty(prop)) {
      this[prop] = response[prop] || this[prop]
    }
  }
  if (response.album &&
      response.album.name) {
    this.album = response.album.name
  }
  if (response.artists) {
    this.artists = response.artists.map(function (artist) {
      return artist.name.trim()
    })
    this.artist = this.artists.join(', ')
    this.mainArtist = this.artists[0]
  }
  if (this.mainArtist && this.name) {
    this.title = this.name + ' - ' + this.mainArtist
  } else {
    this.title = this.name
  }
}

/**
 * Dispatch entry.
 * @return {Promise | Track} Itself.
 */
Track.prototype.dispatch = function () {
  if (this.popularity) {
    return Promise.resolve(this)
  } else if (this.id) {
    return this.fetchTrack()
  } else {
    return this.searchForTrack()
  }
}

/**
 * Whether this track is equal to another track.
 * @param {Track} track - The track to compare against.
 * @return {boolean} `true` if the tracks are equal,
 * `false` otherwise.
 */
Track.prototype.equals = function (track) {
  return this.uri && track.uri && this.uri === track.uri
}

/**
 * Fetch Last.fm information.
 * @return {Promise | Track} Itself.
 */
Track.prototype.fetchLastfm = function (user) {
  var self = this
  return lastfm.getInfo(this.artist, this.name, user).then(function (result) {
    self.lastfmGlobal = parseInt(result.track.playcount)
    self.lastfmPersonal = parseInt(result.track.userplaycount)
    self.lastfm = self.lastfmPersonal > -1 ? self.lastfmPersonal : self.lastfmGlobal
    return self
  })
}

/**
 * Fetch track metadata.
 * @return {Promise | Track} Itself.
 */
Track.prototype.fetchTrack = function (id) {
  id = id || this.id
  var self = this
  if (Number.isInteger(this.popularity)) {
    return Promise.resolve(this)
  } else {
    return this.spotify.getTrack(id).then(function (response) {
      self.clone(response)
      return self
    })
  }
}

/**
 * Get track popularity.
 * @return {Promise | integer} The track popularity.
 */
Track.prototype.getPopularity = function () {
  var self = this
  if (Number.isInteger(this.popularity)) {
    return Promise.resolve(this.popularity)
  } else {
    return self.refresh().then(function () {
      return self.popularity
    })
  }
}

/**
 * Whether the track has the given artist.
 * @param {string} artist - The artist.
 * @return {boolean} `true` the track has the artist,
 * `false` otherwise.
 */
Track.prototype.hasArtist = function (artist) {
  artist = artist.trim().toLowerCase()
  for (var i in this.artists) {
    var trackArtist = this.artists[i].toLowerCase().trim()
    if (trackArtist.includes(artist)) {
      return true
    }
  }
  return false
}

/**
 * Refresh track metadata.
 * @return {Promise | Track} Itself.
 */
Track.prototype.refresh = function () {
  var self = this
  return self.dispatch().then(function () {
    return self.dispatch()
  })
}

/**
 * Search for track.
 * @param {string} query - The query text.
 * @return {Promise | Track} Itself.
 */
Track.prototype.searchForTrack = function () {
  var self = this
  return this.spotify.searchForTrack(this.entry).then(function (response) {
    if (response &&
        response.tracks &&
        response.tracks.items &&
        response.tracks.items[0]) {
      response = response.tracks.items[0]
      self.clone(response)
      return Promise.resolve(self)
    } else {
      return Promise.reject(response)
    }
  }).catch(function () {
    if (self.entry.match(/^[0-9a-z]+$/i)) {
      return self.fetchTrack(self.entry)
    } else {
      // console.log('COULD NOT FIND ' + self.entry)
      return Promise.reject(null)
    }
  })
}

/**
 * Whether this track is similar to another track.
 * @param {Track} track - The track to compare against.
 * @return {boolean} `true` if the tracks are similar,
 * `false` otherwise.
 */
Track.prototype.similarTo = function (track) {
  return this.equals(track) ||
    this.title.toLowerCase() === track.title.toLowerCase()
}

/**
 * Full track title.
 * @return {string} The track title.
 */
Track.prototype.toString = function () {
  return this.title || this.name || this.entry || this.id
}

module.exports = Track