const Plugin = require('../Plugin')
const Translator = require('../../core/Translator')
const dragDrop = require('drag-drop')
const Dashboard = require('./Dashboard')
const StatusBar = require('../StatusBar')
const Informer = require('../Informer')
const { findDOMElement } = require('../../core/Utils')
const prettyBytes = require('prettier-bytes')
const { defaultTabIcon } = require('./icons')

/**
 * Modal Dialog & Dashboard
 */
module.exports = class DashboardUI extends Plugin {
  constructor (core, opts) {
    super(core, opts)
    this.id = 'DashboardUI'
    this.title = 'Dashboard UI'
    this.type = 'orchestrator'

    const defaultLocale = {
      strings: {
        selectToUpload: 'Select files to upload',
        closeModal: 'Close Modal',
        upload: 'Upload',
        importFrom: 'Import files from',
        dashboardWindowTitle: 'Uppy Dashboard Window (Press escape to close)',
        dashboardTitle: 'Uppy Dashboard',
        copyLinkToClipboardSuccess: 'Link copied to clipboard.',
        copyLinkToClipboardFallback: 'Copy the URL below',
        done: 'Done',
        localDisk: 'Local Disk',
        dropPasteImport: 'Drop files here, paste, import from one of the locations above or',
        dropPaste: 'Drop files here, paste or',
        browse: 'browse',
        fileProgress: 'File progress: upload speed and ETA',
        numberOfSelectedFiles: 'Number of selected files',
        uploadAllNewFiles: 'Upload all new files'
      }
    }

    // set default options
    const defaultOptions = {
      target: 'body',
      inline: false,
      width: 750,
      height: 550,
      semiTransparent: false,
      defaultTabIcon: defaultTabIcon(),
      showProgressDetails: false,
      locale: defaultLocale
    }

    // merge default options with the ones set by user
    this.opts = Object.assign({}, defaultOptions, opts)

    this.locale = Object.assign({}, defaultLocale, this.opts.locale)
    this.locale.strings = Object.assign({}, defaultLocale.strings, this.opts.locale.strings)

    this.translator = new Translator({locale: this.locale})
    this.containerWidth = this.translator.translate.bind(this.translator)

    this.hideModal = this.hideModal.bind(this)
    this.showModal = this.showModal.bind(this)

    this.addTarget = this.addTarget.bind(this)
    this.actions = this.actions.bind(this)
    this.hideAllPanels = this.hideAllPanels.bind(this)
    this.showPanel = this.showPanel.bind(this)
    this.initEvents = this.initEvents.bind(this)
    this.handleEscapeKeyPress = this.handleEscapeKeyPress.bind(this)
    this.handleFileCard = this.handleFileCard.bind(this)
    this.handleDrop = this.handleDrop.bind(this)
    this.pauseAll = this.pauseAll.bind(this)
    this.resumeAll = this.resumeAll.bind(this)
    this.cancelAll = this.cancelAll.bind(this)
    this.updateDashboardElWidth = this.updateDashboardElWidth.bind(this)
    this.render = this.render.bind(this)
    this.install = this.install.bind(this)
  }

  addTarget (plugin) {
    const callerPluginId = plugin.id || plugin.constructor.name
    const callerPluginName = plugin.title || callerPluginId
    const callerPluginIcon = plugin.icon || this.opts.defaultTabIcon
    const callerPluginType = plugin.type

    if (callerPluginType !== 'acquirer' &&
        callerPluginType !== 'progressindicator' &&
        callerPluginType !== 'presenter') {
      let msg = 'Error: Modal can only be used by plugins of types: acquirer, progressindicator, presenter'
      this.core.log(msg)
      return
    }

    const target = {
      id: callerPluginId,
      name: callerPluginName,
      icon: callerPluginIcon,
      type: callerPluginType,
      focus: plugin.focus,
      render: plugin.render,
      isHidden: true
    }

    const modal = this.core.getState().modal
    const newTargets = modal.targets.slice()
    newTargets.push(target)

    this.core.setState({
      modal: Object.assign({}, modal, {
        targets: newTargets
      })
    })

    return this.target
  }

  hideAllPanels () {
    const modal = this.core.getState().modal

    this.core.setState({modal: Object.assign({}, modal, {
      activePanel: false
    })})
  }

  showPanel (id) {
    const modal = this.core.getState().modal

    const activePanel = modal.targets.filter((target) => {
      return target.type === 'acquirer' && target.id === id
    })[0]

    this.core.setState({modal: Object.assign({}, modal, {
      activePanel: activePanel
    })})
  }

  hideModal () {
    const modal = this.core.getState().modal

    this.core.setState({
      modal: Object.assign({}, modal, {
        isHidden: true
      })
    })

    document.body.classList.remove('is-UppyDashboard-open')
  }

  showModal () {
    const modal = this.core.getState().modal

    this.core.setState({
      modal: Object.assign({}, modal, {
        isHidden: false
      })
    })

    // add class to body that sets position fixed
    document.body.classList.add('is-UppyDashboard-open')
    // focus on modal inner block
    this.target.querySelector('.UppyDashboard-inner').focus()

    this.updateDashboardElWidth()
    // to be sure, sometimes when the function runs, container size is still 0
    setTimeout(this.updateDashboardElWidth, 300)
  }

  // Close the Modal on esc key press
  handleEscapeKeyPress (event) {
    if (event.keyCode === 27) {
      this.hideModal()
    }
  }

  initEvents () {
    // const dashboardEl = this.target.querySelector(`${this.opts.target} .UppyDashboard`)

    // Modal open button
    const showModalTrigger = findDOMElement(this.opts.trigger)
    if (!this.opts.inline && showModalTrigger) {
      showModalTrigger.addEventListener('click', this.showModal)
    } else {
      this.core.log('Modal trigger wasn’t found')
    }

    document.body.addEventListener('keyup', this.handleEscapeKeyPress)

    // Drag Drop
    this.removeDragDropListener = dragDrop(this.el, (files) => {
      this.handleDrop(files)
    })
  }

  removeEvents () {
    const showModalTrigger = findDOMElement(this.opts.trigger)
    if (!this.opts.inline && showModalTrigger) {
      showModalTrigger.removeEventListener('click', this.showModal)
    }

    this.removeDragDropListener()
    document.body.removeEventListener('keyup', this.handleEscapeKeyPress)
  }

  actions () {
    const bus = this.core.bus

    bus.on('core:file-add', this.hideAllPanels)
    bus.on('dashboard:file-card', this.handleFileCard)

    window.addEventListener('resize', this.updateDashboardElWidth)
  }

  removeActions () {
    const bus = this.core.bus

    window.removeEventListener('resize', this.updateDashboardElWidth)

    bus.off('core:file-add', this.hideAllPanels)
    bus.off('dashboard:file-card', this.handleFileCard)
  }

  updateDashboardElWidth () {
    const dashboardEl = this.target.querySelector('.UppyDashboard-inner')
    // console.log(dashboardEl.offsetWidth)

    const modal = this.core.getState().modal
    this.core.setState({
      modal: Object.assign({}, modal, {
        containerWidth: dashboardEl.offsetWidth
      })
    })
  }

  handleFileCard (fileId) {
    const modal = this.core.getState().modal

    this.core.setState({
      modal: Object.assign({}, modal, {
        fileCardFor: fileId || false
      })
    })
  }

  handleDrop (files) {
    this.core.log('All right, someone dropped something...')

    files.forEach((file) => {
      this.core.bus.emit('core:file-add', {
        source: this.id,
        name: file.name,
        type: file.type,
        data: file
      })
    })
  }

  cancelAll () {
    this.core.bus.emit('core:cancel-all')
  }

  pauseAll () {
    this.core.bus.emit('core:pause-all')
  }

  resumeAll () {
    this.core.bus.emit('core:resume-all')
  }

  render (state) {
    const files = state.files

    const newFiles = Object.keys(files).filter((file) => {
      return !files[file].progress.uploadStarted
    })
    const inProgressFiles = Object.keys(files).filter((file) => {
      return !files[file].progress.uploadComplete &&
             files[file].progress.uploadStarted &&
             !files[file].isPaused
    })

    let inProgressFilesArray = []
    inProgressFiles.forEach((file) => {
      inProgressFilesArray.push(files[file])
    })

    // total size and uploaded size
    let totalSize = 0
    let totalUploadedSize = 0
    inProgressFilesArray.forEach((file) => {
      totalSize = totalSize + (file.progress.bytesTotal || 0)
      totalUploadedSize = totalUploadedSize + (file.progress.bytesUploaded || 0)
    })
    totalSize = prettyBytes(totalSize)
    totalUploadedSize = prettyBytes(totalUploadedSize)

    const acquirers = state.modal.targets.filter((target) => {
      return target.type === 'acquirer'
    })

    const progressindicators = state.modal.targets.filter((target) => {
      return target.type === 'progressindicator'
    })

    const addFile = (file) => {
      this.core.emitter.emit('core:file-add', file)
    }

    const removeFile = (fileID) => {
      this.core.emitter.emit('core:file-remove', fileID)
    }

    const startUpload = (ev) => {
      this.core.upload().catch((err) => {
        // Log error.
        console.error(err.stack || err.message)
      })
    }

    const pauseUpload = (fileID) => {
      this.core.emitter.emit('core:upload-pause', fileID)
    }

    const cancelUpload = (fileID) => {
      this.core.emitter.emit('core:upload-cancel', fileID)
      this.core.emitter.emit('core:file-remove', fileID)
    }

    const showFileCard = (fileID) => {
      this.core.emitter.emit('dashboard:file-card', fileID)
    }

    const fileCardDone = (meta, fileID) => {
      this.core.emitter.emit('core:update-meta', meta, fileID)
      this.core.emitter.emit('dashboard:file-card')
    }

    const info = (text, type, duration) => {
      this.core.emitter.emit('informer', text, type, duration)
    }

    const resumableUploads = this.core.getState().capabilities.resumableUploads || false

    return Dashboard({
      state: state,
      modal: state.modal,
      newFiles: newFiles,
      files: files,
      totalFileCount: Object.keys(files).length,
      totalProgress: state.totalProgress,
      acquirers: acquirers,
      activePanel: state.modal.activePanel,
      progressindicators: progressindicators,
      autoProceed: this.core.opts.autoProceed,
      id: this.id,
      hideModal: this.hideModal,
      showProgressDetails: this.opts.showProgressDetails,
      inline: this.opts.inline,
      semiTransparent: this.opts.semiTransparent,
      showPanel: this.showPanel,
      hideAllPanels: this.hideAllPanels,
      log: this.core.log,
      i18n: this.containerWidth,
      pauseAll: this.pauseAll,
      resumeAll: this.resumeAll,
      addFile: addFile,
      removeFile: removeFile,
      info: info,
      metaFields: state.metaFields,
      resumableUploads: resumableUploads,
      startUpload: startUpload,
      pauseUpload: pauseUpload,
      cancelUpload: cancelUpload,
      fileCardFor: state.modal.fileCardFor,
      showFileCard: showFileCard,
      fileCardDone: fileCardDone,
      updateDashboardElWidth: this.updateDashboardElWidth,
      maxWidth: this.opts.maxWidth,
      maxHeight: this.opts.maxHeight,
      currentWidth: state.modal.containerWidth,
      isWide: state.modal.containerWidth > 400
    })
  }

  install () {
    // Set default state for Modal
    this.core.setState({modal: {
      isHidden: true,
      showFileCard: false,
      activePanel: false,
      targets: []
    }})

    const target = this.opts.target
    const plugin = this
    this.target = this.mount(target, plugin)

    if (!this.opts.disableStatusBar) {
      this.core.use(StatusBar, {
        target: DashboardUI
      })
    }

    if (!this.opts.disableInformer) {
      this.core.use(Informer, {
        target: DashboardUI
      })
    }

    this.initEvents()
    this.actions()
  }

  uninstall () {
    this.unmount()
    this.removeActions()
    this.removeEvents()
  }
}
