/* global FileReader */
var async = require('async')
var $ = require('jquery')
var yo = require('yo-yo')
var CompilerMetadata = require('../files/compiler-metadata')
var EventManager = require('../../lib/events')
var Gists = require('gists')
var FileExplorer = require('../files/file-explorer')
var modalDialog = require('../ui/modaldialog')
var modalDialogCustom = require('../ui/modal-dialog-custom')
var tooltip = require('../ui/tooltip')
var QueryParams = require('../../lib/query-params')
var queryParams = new QueryParams()
var helper = require('../../lib/helper')

var globalRegistry = require('../../global/registry')

var styleGuide = require('../ui/styles-guide/theme-chooser')
var styles = styleGuide.chooser()

var css = require('./styles/file-panel-styles')

var limit = 60
var canUpload = window.File || window.FileReader || window.FileList || window.Blob
var ghostbar = yo`<div class=${css.ghostbar}></div>`

/*
  Overview of APIs:
   * fileManager: @args fileProviders (browser, shared-folder, swarm, github, etc ...) & config & editor
      - listen on browser & localhost file provider (`fileRenamed` & `fileRemoved`)
      - update the tabs, switchFile
      - trigger `currentFileChanged`
      - set the current file in the config
   * fileProvider: currently browser, swarm, localhost, github, gist
      - link to backend
      - provide properties `type`, `readonly`
      - provide API `resolveDirectory`, `remove`, `exists`, `rename`, `get`, `set`
      - trigger `fileExternallyChanged`, `fileRemoved`, `fileRenamed`, `fileRenamedError`, `fileAdded`
   * file-explorer: treeview @args fileProvider
      - listen on events triggered by fileProvider
      - call fileProvider API
*/

function filepanel (localRegistry) {
  var self = this
  self._components = {}
  self._components.registry = localRegistry || globalRegistry
  self._deps = {
    fileProviders: self._components.registry.get('fileproviders').api,
    fileManager: self._components.registry.get('filemanager').api,
    config: self._components.registry.get('config').api,
    pluginManager: self._components.registry.get('pluginmanager').api
  }
  var fileExplorer = new FileExplorer(self._components.registry, self._deps.fileProviders['browser'])
  var fileSystemExplorer = new FileExplorer(self._components.registry, self._deps.fileProviders['localhost'])
  var swarmExplorer = new FileExplorer(self._components.registry, self._deps.fileProviders['swarm'])
  var githubExplorer = new FileExplorer(self._components.registry, self._deps.fileProviders['github'])
  var gistExplorer = new FileExplorer(self._components.registry, self._deps.fileProviders['gist'])
  var configExplorer = new FileExplorer(self._components.registry, self._deps.fileProviders['config'])
  var httpExplorer = new FileExplorer(self._components.registry, self._deps.fileProviders['http'])
  var httpsExplorer = new FileExplorer(self._components.registry, self._deps.fileProviders['https'])

  // ----------------- editor panel ----------------------
  self._compilerMetadata = new CompilerMetadata(
    {
      fileManager: self._deps.fileManager,
      pluginManager: self._deps.pluginManager,
      config: self._deps.config
    }
  )
  self._compilerMetadata.syncContractMetadata()

  self.compilerMetadata = () => { return self._compilerMetadata }
  var dragbar = yo`<div onmousedown=${mousedown} class=${css.dragbar}></div>`

  function remixdDialog () {
    return yo`
      <div class=${css.dialog}>
        <div class=${css.dialogParagraph}>Interact with your file system from Remix. Click connect and find shared folder in the Remix file explorer (under localhost).
          Before you get started, check out <a target="_blank" href="https://remix.readthedocs.io/en/latest/tutorial_remixd_filesystem.html">Tutorial_remixd_filesystem</a>
          to find out how to run Remixd.
        </div>
        <div class=${css.dialogParagraph}>Connection will start a session between <em>${window.location.href}</em> and your local file system <i>ws://127.0.0.1:65520</i>
          so please make sure your system is secured enough (port 65520 neither opened nor forwarded).
          <i class="fa fa-link"></i> will show you current connection status.
        </div>
        <div class=${css.dialogParagraph}>This feature is still in Alpha, so we recommend you to keep a copy of the shared folder.</div>
      </div>
    `
  }

  function template () {
    return yo`
      <div class=${css.container}>
        <div class=${css.fileexplorer}>
          <div class=${css.menu}>
            <span onclick=${createNewFile} class="newFile ${css.newFile}" title="Create New File in the Browser Storage Explorer">
              <i class="fa fa-plus-circle"></i>
            </span>
            ${canUpload ? yo`
              <span class=${css.uploadFile} title="Add Local file to the Browser Storage Explorer">
                <label class="fa fa-folder-open">
                  <input type="file" onchange=${uploadFile} multiple />
                </label>
              </span>
            ` : ''}
            <span class="${css.gist}" title="Publish all [browser] explorer files to a github gist" onclick=${() => publishToGist('browser')}>
              <i class="fa fa-github"></i>
            </span>
            <span class="${css.gist}" title="Update the current [gist] explorer" onclick=${() => updateGist()}>
              <i class="fa fa-github"></i>
            </span>
            <span class="${css.copyFiles}" title="Copy all files to another instance of Remix IDE" onclick=${copyFiles}>
              <i class="fa fa-files-o" aria-hidden="true"></i>
            </span>
            <span onclick=${connectToLocalhost} class="${css.connectToLocalhost}">
              <i class="websocketconn fa fa-link" title="Connect to Localhost"></i>
            </span>
          </div>
          <div class=${css.treeviews}>
            <div class=${css.treeview}>${fileExplorer.init()}</div>
            <div class="configexplorer ${css.treeview}">${configExplorer.init()}</div>
            <div class="filesystemexplorer ${css.treeview}">${fileSystemExplorer.init()}</div>
            <div class="swarmexplorer ${css.treeview}">${swarmExplorer.init()}</div>
            <div class="githubexplorer ${css.treeview}">${githubExplorer.init()}</div>
            <div class="gistexplorer ${css.treeview}">${gistExplorer.init()}</div>
            <div class="httpexplorer ${css.treeview}">${httpExplorer.init()}</div>
            <div class="httpsexplorer ${css.treeview}">${httpsExplorer.init()}</div>
          </div>
        </div>
        ${dragbar}
      </div>
    `
  }

  var event = new EventManager()
  self.event = event
  var element = template()
  fileExplorer.ensureRoot()
  configExplorer.ensureRoot()
  var websocketconn = element.querySelector('.websocketconn')
  self._deps.fileProviders['localhost'].event.register('connecting', (event) => {
    websocketconn.style.color = styles.colors.yellow
    websocketconn.setAttribute('title', 'Connecting to localhost. ' + JSON.stringify(event))
  })

  self._deps.fileProviders['localhost'].event.register('connected', (event) => {
    websocketconn.style.color = styles.colors.green
    websocketconn.setAttribute('title', 'Connected to localhost. ' + JSON.stringify(event))
    fileSystemExplorer.show()
  })

  self._deps.fileProviders['localhost'].event.register('errored', (event) => {
    websocketconn.style.color = styles.colors.red
    websocketconn.setAttribute('title', 'localhost connection errored. ' + JSON.stringify(event))
    fileSystemExplorer.hide()
  })

  self._deps.fileProviders['localhost'].event.register('closed', (event) => {
    websocketconn.style.color = styles.colors.black
    websocketconn.setAttribute('title', 'localhost connection closed. ' + JSON.stringify(event))
    fileSystemExplorer.hide()
  })

  fileExplorer.events.register('focus', function (path) {
    self._deps.fileManager.switchFile(path)
  })

  configExplorer.events.register('focus', function (path) {
    self._deps.fileManager.switchFile(path)
  })

  fileSystemExplorer.events.register('focus', function (path) {
    self._deps.fileManager.switchFile(path)
  })

  swarmExplorer.events.register('focus', function (path) {
    self._deps.fileManager.switchFile(path)
  })

  githubExplorer.events.register('focus', function (path) {
    self._deps.fileManager.switchFile(path)
  })

  gistExplorer.events.register('focus', function (path) {
    self._deps.fileManager.switchFile(path)
  })

  httpExplorer.events.register('focus', function (path) {
    self._deps.fileManager.switchFile(path)
  })

  httpsExplorer.events.register('focus', function (path) {
    self._deps.fileManager.switchFile(path)
  })

  self.render = function render () { return element }

  self.profile = function () {
    return {
      name: 'FilePanel',
      methods: [],
      events: [],
      icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0xNjk2IDM4NHE0MCAwIDY4IDI4dDI4IDY4djEyMTZxMCA0MC0yOCA2OHQtNjggMjhoLTk2MHEtNDAgMC02OC0yOHQtMjgtNjh2LTI4OGgtNTQ0cS00MCAwLTY4LTI4dC0yOC02OHYtNjcycTAtNDAgMjAtODh0NDgtNzZsNDA4LTQwOHEyOC0yOCA3Ni00OHQ4OC0yMGg0MTZxNDAgMCA2OCAyOHQyOCA2OHYzMjhxNjgtNDAgMTI4LTQwaDQxNnptLTU0NCAyMTNsLTI5OSAyOTloMjk5di0yOTl6bS02NDAtMzg0bC0yOTkgMjk5aDI5OXYtMjk5em0xOTYgNjQ3bDMxNi0zMTZ2LTQxNmgtMzg0djQxNnEwIDQwLTI4IDY4dC02OCAyOGgtNDE2djY0MGg1MTJ2LTI1NnEwLTQwIDIwLTg4dDQ4LTc2em05NTYgODA0di0xMTUyaC0zODR2NDE2cTAgNDAtMjggNjh0LTY4IDI4aC00MTZ2NjQwaDg5NnoiLz48L3N2Zz4='
    }
  }

  function uploadFile (event) {
    // TODO The file explorer is merely a view on the current state of
    // the files module. Please ask the user here if they want to overwrite
    // a file and then just use `files.add`. The file explorer will
    // pick that up via the 'fileAdded' event from the files module.

    ;[...this.files].forEach((file) => {
      var files = fileExplorer.files
      function loadFile () {
        var fileReader = new FileReader()
        fileReader.onload = function (event) {
          if (helper.checkSpecialChars(file.name)) {
            modalDialogCustom.alert('Special characters are not allowed')
            return
          }
          var success = files.set(name, event.target.result)
          if (!success) modalDialogCustom.alert('Failed to create file ' + name)
          else self.event.trigger('focus', [name])
        }
        fileReader.readAsText(file)
      }

      var name = files.type + '/' + file.name
      files.exists(name, (error, exist) => {
        if (error) console.log(error)
        if (!exist) {
          loadFile()
        } else {
          modalDialogCustom.confirm(null, `The file ${name} already exists! Would you like to overwrite it?`, () => { loadFile() })
        }
      })
    })
  }

  // ----------------- resizeable ui ---------------
  function mousedown (event) {
    event.preventDefault()
    if (event.which === 1) {
      moveGhostbar(event)
      document.body.appendChild(ghostbar)
      document.addEventListener('mousemove', moveGhostbar)
      document.addEventListener('mouseup', removeGhostbar)
      document.addEventListener('keydown', cancelGhostbar)
    }
  }
  function cancelGhostbar (event) {
    if (event.keyCode === 27) {
      document.body.removeChild(ghostbar)
      document.removeEventListener('mousemove', moveGhostbar)
      document.removeEventListener('mouseup', removeGhostbar)
      document.removeEventListener('keydown', cancelGhostbar)
    }
  }
  function getPosition (event) {
    var rhp = document.body.offsetWidth - window['righthand-panel'].offsetWidth
    var newpos = (event.pageX < limit) ? limit : event.pageX
    newpos = (newpos < (rhp - limit)) ? newpos : (rhp - limit)
    return newpos
  }
  function moveGhostbar (event) { // @NOTE VERTICAL ghostbar
    ghostbar.style.left = getPosition(event) + 'px'
  }
  function removeGhostbar (event) {
    document.body.removeChild(ghostbar)
    document.removeEventListener('mousemove', moveGhostbar)
    document.removeEventListener('mouseup', removeGhostbar)
    document.removeEventListener('keydown', cancelGhostbar)
  }

  function createNewFile () {
    modalDialogCustom.prompt(null, 'File Name', 'Untitled.sol', (input) => {
      helper.createNonClashingName(input, self._deps.fileProviders['browser'], (error, newName) => {
        if (error) return modalDialogCustom.alert('Failed to create file ' + newName + ' ' + error)
        if (!self._deps.fileProviders['browser'].set(newName, '')) {
          modalDialogCustom.alert('Failed to create file ' + newName)
        } else {
          var file = self._deps.fileProviders['browser'].type + '/' + newName
          self._deps.fileManager.switchFile(file)
          if (file.includes('_test.sol')) {
            self.event.trigger('newTestFileCreated', [file])
          }
        }
      })
    }, null, true)
  }

  /**
    * connect to localhost if no connection and render the explorer
    * disconnect from localhost if connected and remove the explorer
    *
    * @param {String} txHash    - hash of the transaction
    */
  function connectToLocalhost () {
    if (self._deps.fileProviders['localhost'].isConnected()) {
      self._deps.fileProviders['localhost'].close((error) => {
        if (error) console.log(error)
      })
    } else {
      modalDialog('Connect to localhost', remixdDialog(),
        { label: 'Connect',
          fn: () => {
            self._deps.fileProviders['localhost'].init((error) => {
              if (error) {
                console.log(error)
              } else {
                fileSystemExplorer.ensureRoot()
              }
            })
          }})
    }
  }

  // ------------------ gist publish --------------

  function updateGist () {
    var gistId = self._deps.fileProviders['gist'].id
    if (!gistId) {
      tooltip('no gist content is currently loaded.')
    } else {
      toGist('gist', gistId)
    }
  }

  function publishToGist (fileProviderName) {
    modalDialogCustom.confirm(null, 'Are you very sure you want to publish all your files anonymously as a public gist on github.com?', () => {
      toGist(fileProviderName)
    })
  }

  function toGist (fileProviderName, id) {
    packageFiles(self._deps.fileProviders[fileProviderName], (error, packaged) => {
      if (error) {
        console.log(error)
        modalDialogCustom.alert('Failed to create gist: ' + error)
      } else {
        var tokenAccess = self._deps.config.get('settings/gist-access-token')
        if (!tokenAccess) {
          modalDialogCustom.alert('Remix requires an access token (which includes gists creation permission). Please go to the settings tab for more information.')
        } else {
          var description = 'Created using remix-ide: Realtime Ethereum Contract Compiler and Runtime. \n Load this file by pasting this gists URL or ID at https://remix.ethereum.org/#version=' + queryParams.get().version + '&optimize=' + queryParams.get().optimize + '&gist='
          var gists = new Gists({
            token: tokenAccess
          })
          if (id) {
            tooltip('Saving gist (' + id + ') ...')
            gists.edit({
              description: description,
              public: true,
              files: packaged,
              id: id
            }, (error, result) => {
              cb(error, result)
            })
          } else {
            tooltip('Creating a new gist ...')
            gists.create({
              description: description,
              public: true,
              files: packaged
            }, (error, result) => {
              cb(error, result)
            })
          }
        }
      }
    })
  }

  function cb (error, data) {
    if (error) {
      modalDialogCustom.alert('Failed to manage gist: ' + error)
    } else {
      if (data.html_url) {
        modalDialogCustom.confirm(null, `The gist is at ${data.html_url}. Would you like to open it in a new window?`, () => {
          window.open(data.html_url, '_blank')
        })
      } else {
        modalDialogCustom.alert(data.message + ' ' + data.documentation_url + ' ' + JSON.stringify(data.errors, null, '\t'))
      }
    }
  }

  // ------------------ copy files --------------

  function copyFiles () {
    modalDialogCustom.prompt(null, 'To which other remix-ide instance do you want to copy over all files?', 'https://remix.ethereum.org', (target) => {
      doCopy(target)
    })
    function doCopy (target) {
      // package only files from the browser storage.
      packageFiles(self._deps.fileProviders['browser'], (error, packaged) => {
        if (error) {
          console.log(error)
        } else {
          $('<iframe/>', {
            src: target,
            style: 'display:none;',
            load: function () { this.contentWindow.postMessage(['loadFiles', packaged], '*') }
          }).appendTo('body')
        }
      })
    }
  }
}

// return all the files, except the temporary/readonly ones..
function packageFiles (filesProvider, callback) {
  var ret = {}
  filesProvider.resolveDirectory(filesProvider.type, (error, files) => {
    if (error) callback(error)
    else {
      async.eachSeries(Object.keys(files), (path, cb) => {
        filesProvider.get(path, (error, content) => {
          if (error) cb(error)
          else {
            ret[path] = { content }
            cb()
          }
        })
      }, (error) => {
        callback(error, ret)
      })
    }
  })
}

module.exports = filepanel
