'use babel'

import _ from 'underscore-plus'
import { CompositeDisposable, Disposable } from 'atom'
import SelectListView from 'atom-select-list'
import StatusBarItem from './status-bar-item'
import helpers from './helpers'

const LineEndingRegExp = /\r\n|\n/g

// the following regular expression is executed natively via the `substring` package,
// where `\A` corresponds to the beginning of the string.
// More info: https://github.com/atom/line-ending-selector/pull/56
// eslint-disable-next-line no-useless-escape
const LFRegExp = /(\A|[^\r])\n/g
const CRLFRegExp = /\r\n/g

let disposables = null
let modalPanel = null
let lineEndingListView = null

export function activate () {
  disposables = new CompositeDisposable()

  disposables.add(
    atom.commands.add('atom-text-editor', {
      'line-ending-selector:show': event => {
        if (!modalPanel) {
          lineEndingListView = new SelectListView({
            items: [
              { name: 'LF', value: '\n' },
              { name: 'CRLF', value: '\r\n' }
            ],
            filterKeyForItem: lineEnding => lineEnding.name,
            didConfirmSelection: lineEnding => {
              setLineEnding(
                atom.workspace.getActiveTextEditor(),
                lineEnding.value
              )
              modalPanel.hide()
            },
            didCancelSelection: () => {
              modalPanel.hide()
            },
            elementForItem: lineEnding => {
              const element = document.createElement('li')
              element.textContent = lineEnding.name
              return element
            }
          })
          modalPanel = atom.workspace.addModalPanel({
            item: lineEndingListView
          })
          disposables.add(
            new Disposable(() => {
              lineEndingListView.destroy()
              modalPanel.destroy()
              modalPanel = null
            })
          )
        }

        lineEndingListView.reset()
        modalPanel.show()
        lineEndingListView.focus()
      },

      'line-ending-selector:convert-to-LF': event => {
        const editorElement = event.target.closest('atom-text-editor')
        setLineEnding(editorElement.getModel(), '\n')
      },

      'line-ending-selector:convert-to-CRLF': event => {
        const editorElement = event.target.closest('atom-text-editor')
        setLineEnding(editorElement.getModel(), '\r\n')
      }
    })
  )
}

export function deactivate () {
  disposables.dispose()
}

export function consumeStatusBar (statusBar) {
  let statusBarItem = new StatusBarItem()
  let currentBufferDisposable = null
  let tooltipDisposable = null

  const updateTile = _.debounce(buffer => {
    getLineEndings(buffer).then(lineEndings => {
      if (lineEndings.size === 0) {
        let defaultLineEnding = getDefaultLineEnding()
        buffer.setPreferredLineEnding(defaultLineEnding)
        lineEndings = new Set().add(defaultLineEnding)
      }
      statusBarItem.setLineEndings(lineEndings)
    })
  }, 0)

  disposables.add(
    atom.workspace.observeActiveTextEditor(editor => {
      if (currentBufferDisposable) currentBufferDisposable.dispose()

      if (editor && editor.getBuffer) {
        let buffer = editor.getBuffer()
        updateTile(buffer)
        currentBufferDisposable = buffer.onDidChange(({ oldText, newText }) => {
          if (!statusBarItem.hasLineEnding('\n')) {
            if (newText.indexOf('\n') >= 0) {
              updateTile(buffer)
            }
          } else if (!statusBarItem.hasLineEnding('\r\n')) {
            if (newText.indexOf('\r\n') >= 0) {
              updateTile(buffer)
            }
          } else if (oldText.indexOf('\n')) {
            updateTile(buffer)
          }
        })
      } else {
        statusBarItem.setLineEndings(new Set())
        currentBufferDisposable = null
      }

      if (tooltipDisposable) {
        disposables.remove(tooltipDisposable)
        tooltipDisposable.dispose()
      }
      tooltipDisposable = atom.tooltips.add(statusBarItem.element, {
        title () {
          return `File uses ${statusBarItem.description()} line endings`
        }
      })
      disposables.add(tooltipDisposable)
    })
  )

  disposables.add(
    new Disposable(() => {
      if (currentBufferDisposable) currentBufferDisposable.dispose()
    })
  )

  statusBarItem.onClick(() => {
    const editor = atom.workspace.getActiveTextEditor()
    atom.commands.dispatch(
      atom.views.getView(editor),
      'line-ending-selector:show'
    )
  })

  let tile = statusBar.addRightTile({ item: statusBarItem, priority: 200 })
  disposables.add(new Disposable(() => tile.destroy()))
}

function getDefaultLineEnding () {
  switch (atom.config.get('line-ending-selector.defaultLineEnding')) {
    case 'LF':
      return '\n'
    case 'CRLF':
      return '\r\n'
    case 'OS Default':
    default:
      return helpers.getProcessPlatform() === 'win32' ? '\r\n' : '\n'
  }
}

function getLineEndings (buffer) {
  if (typeof buffer.find === 'function') {
    return Promise.all([buffer.find(LFRegExp), buffer.find(CRLFRegExp)]).then(
      ([hasLF, hasCRLF]) => {
        const result = new Set()
        if (hasLF) result.add('\n')
        if (hasCRLF) result.add('\r\n')
        return result
      }
    )
  } else {
    return new Promise(resolve => {
      const result = new Set()
      for (let i = 0; i < buffer.getLineCount() - 1; i++) {
        result.add(buffer.lineEndingForRow(i))
      }
      resolve(result)
    })
  }
}

function setLineEnding (item, lineEnding) {
  if (item && item.getBuffer) {
    let buffer = item.getBuffer()
    buffer.setPreferredLineEnding(lineEnding)
    buffer.setText(buffer.getText().replace(LineEndingRegExp, lineEnding))
  }
}
