module.exports = torrentPoster

const captureFrame = require('capture-frame')
const path = require('path')

const mediaExtensions = require('./media-extensions')

const msgNoSuitablePoster = 'Cannot generate a poster from any files in the torrent'

function torrentPoster (torrent, cb) {
  // First, try to use a poster image if available
  const posterFile = torrent.files.filter(function (file) {
    return /^poster\.(jpg|png|gif)$/.test(file.name)
  })[0]
  if (posterFile) return extractPoster(posterFile, cb)

  // 'score' each media type based on total size present in torrent
  const bestScore = ['audio', 'video', 'image'].map(mediaType => {
    return {
      type: mediaType,
      size: calculateDataLengthByExtension(torrent, mediaExtensions[mediaType])
    }
  }).sort((a, b) => { // sort descending on size
    return b.size - a.size
  })[0]

  if (bestScore.size === 0) {
    // Admit defeat, no video, audio or image had a significant presence
    return cb(new Error(msgNoSuitablePoster))
  }

  // Based on which media type is dominant we select the corresponding poster function
  switch (bestScore.type) {
    case 'audio':
      return torrentPosterFromAudio(torrent, cb)
    case 'image':
      return torrentPosterFromImage(torrent, cb)
    case 'video':
      return torrentPosterFromVideo(torrent, cb)
  }
}

/**
 * Calculate the total data size of file matching one of the provided extensions
 * @param torrent
 * @param extensions List of extension to match
 * @returns {number} total size, of matches found (>= 0)
 */
function calculateDataLengthByExtension (torrent, extensions) {
  const files = filterOnExtension(torrent, extensions)
  if (files.length === 0) return 0
  return files
    .map(file => file.length)
    .reduce((a, b) => {
      return a + b
    })
}

/**
 * Get the largest file of a given torrent, filtered by provided extension
 * @param torrent Torrent to search in
 * @param extensions Extension whitelist filter
 * @returns Torrent file object
 */
function getLargestFileByExtension (torrent, extensions) {
  const files = filterOnExtension(torrent, extensions)
  if (files.length === 0) return undefined
  return files.reduce((a, b) => {
    return a.length > b.length ? a : b
  })
}

/**
 * Filter file on a list extension, can be used to find al image files
 * @param torrent Torrent to filter files from
 * @param extensions File extensions to filter on
 * @returns {Array} Array of torrent file objects matching one of the given extensions
 */
function filterOnExtension (torrent, extensions) {
  return torrent.files.filter(file => {
    const extname = path.extname(file.name).toLowerCase()
    return extensions.indexOf(extname) !== -1
  })
}

/**
 * Returns a score how likely the file is suitable as a poster
 * @param imgFile File object of an image
 * @returns {number} Score, higher score is a better match
 */
function scoreAudioCoverFile (imgFile) {
  const fileName = path.basename(imgFile.name, path.extname(imgFile.name)).toLowerCase()
  const relevanceScore = {
    cover: 80,
    folder: 80,
    album: 80,
    front: 80,
    back: 20,
    spectrogram: -80
  }

  for (const keyword in relevanceScore) {
    if (fileName === keyword) {
      return relevanceScore[keyword]
    }
    if (fileName.indexOf(keyword) !== -1) {
      return relevanceScore[keyword]
    }
  }
  return 0
}

function torrentPosterFromAudio (torrent, cb) {
  const imageFiles = filterOnExtension(torrent, mediaExtensions.image)

  if (imageFiles.length === 0) return cb(new Error(msgNoSuitablePoster))

  const bestCover = imageFiles.map(file => {
    return {
      file: file,
      score: scoreAudioCoverFile(file)
    }
  }).reduce((a, b) => {
    if (a.score > b.score) {
      return a
    }
    if (b.score > a.score) {
      return b
    }
    // If score is equal, pick the largest file, aiming for highest resolution
    if (a.file.length > b.file.length) {
      return a
    }
    return b
  })

  const extname = path.extname(bestCover.file.name)
  bestCover.file.getBuffer((err, buf) => cb(err, buf, extname))
}

function torrentPosterFromVideo (torrent, cb) {
  const file = getLargestFileByExtension(torrent, mediaExtensions.video)

  const index = torrent.files.indexOf(file)

  const server = torrent.createServer(0)
  server.listen(0, onListening)

  function onListening () {
    const port = server.address().port
    const url = 'http://localhost:' + port + '/' + index
    const video = document.createElement('video')
    video.addEventListener('canplay', onCanPlay)

    video.volume = 0
    video.src = url
    video.play()

    function onCanPlay () {
      video.removeEventListener('canplay', onCanPlay)
      video.addEventListener('seeked', onSeeked)

      video.currentTime = Math.min((video.duration || 600) * 0.03, 60)
    }

    function onSeeked () {
      video.removeEventListener('seeked', onSeeked)

      const buf = captureFrame(video)

      // unload video element
      video.pause()
      video.src = ''
      video.load()

      server.destroy()

      if (buf.length === 0) return cb(new Error(msgNoSuitablePoster))

      cb(null, buf, '.jpg')
    }
  }
}

function torrentPosterFromImage (torrent, cb) {
  const file = getLargestFileByExtension(torrent, mediaExtensions.image)
  extractPoster(file, cb)
}

function extractPoster (file, cb) {
  const extname = path.extname(file.name)
  file.getBuffer((err, buf) => { return cb(err, buf, extname) })
}
