var cledit = require('./cleditCore')

function Keystroke(handler, priority) {
  this.handler = handler
  this.priority = priority || 100
}

cledit.Keystroke = Keystroke

var clearNewline
var charTypes = Object.create(null)

// Word separators, as in Sublime Text
'./\\()"\'-:,.;<>~!@#$%^&*|+=[]{}`~?'.split('').cl_each(function (wordSeparator) {
  charTypes[wordSeparator] = 'wordSeparator'
})
charTypes[' '] = 'space'
charTypes['\t'] = 'space'
charTypes['\n'] = 'newLine'

function getNextWordOffset(text, offset, isBackward) {
  var previousType
  while ((isBackward && offset > 0) || (!isBackward && offset < text.length)) {
    var currentType = charTypes[isBackward ? text[offset - 1] : text[offset]] || 'word'
    if (previousType && currentType !== previousType) {
      if (previousType === 'word' || currentType === 'space' || previousType === 'newLine' || currentType === 'newLine') {
        break
      }
    }
    previousType = currentType
    isBackward ? offset-- : offset++
  }
  return offset
}

cledit.defaultKeystrokes = [

  new Keystroke(function (evt, state, editor) {
    if ((!evt.ctrlKey && !evt.metaKey) || evt.altKey) {
      return
    }
    var keyCode = evt.charCode || evt.keyCode
    var keyCodeChar = String.fromCharCode(keyCode).toLowerCase()
    var action
    switch (keyCodeChar) {
      case 'y':
        action = 'redo'
        break
      case 'z':
        action = evt.shiftKey ? 'redo' : 'undo'
        break
    }
    if (action) {
      evt.preventDefault()
      setTimeout(function () {
        editor.undoMgr[action]()
      }, 10)
      return true
    }
  }),

  new Keystroke(function (evt, state) {
    if (evt.which !== 9 /* tab */ || evt.metaKey || evt.ctrlKey) {
      return
    }

    function strSplice(str, i, remove, add) {
      remove = +remove || 0
      add = add || ''
      return str.slice(0, i) + add + str.slice(i + remove)
    }

    evt.preventDefault()
    var isInverse = evt.shiftKey
    var lf = state.before.lastIndexOf('\n') + 1
    if (isInverse) {
      if (/\s/.test(state.before.charAt(lf))) {
        state.before = strSplice(state.before, lf, 1)
      }
      state.selection = state.selection.replace(/^[ \t]/gm, '')
    } else {
      if (state.selection) {
        state.before = strSplice(state.before, lf, 0, '\t')
        state.selection = state.selection.replace(/\n(?=[\s\S])/g, '\n\t')
      } else {
        state.before += '\t'
      }
    }
    return true
  }),

  new Keystroke(function (evt, state, editor) {
    if (evt.which !== 13 /* enter */) {
      clearNewline = false
      return
    }

    evt.preventDefault()
    var lf = state.before.lastIndexOf('\n') + 1
    if (clearNewline) {
      state.before = state.before.substring(0, lf)
      state.selection = ''
      clearNewline = false
      return true
    }
    clearNewline = false
    var previousLine = state.before.slice(lf)
    var indent = previousLine.match(/^\s*/)[0]
    if (indent.length) {
      clearNewline = true
    }

    editor.undoMgr.setCurrentMode('single')
    state.before += '\n' + indent
    state.selection = ''
    return true
  }),

  new Keystroke(function (evt, state, editor) {
    if (evt.which !== 8 /* backspace */ && evt.which !== 46 /* delete */) {
      return
    }

    editor.undoMgr.setCurrentMode('delete')
    if (!state.selection) {
      var isJump = (cledit.Utils.isMac && evt.altKey) || (!cledit.Utils.isMac && evt.ctrlKey)
      if (isJump) {
        // Custom kill word behavior
        var text = state.before + state.after
        var offset = getNextWordOffset(text, state.before.length, evt.which === 8)
        if (evt.which === 8) {
          state.before = state.before.slice(0, offset)
        } else {
          state.after = state.after.slice(offset - text.length)
        }
        evt.preventDefault()
        return true
      } else if (evt.which === 8 && state.before.slice(-1) === '\n') {
        // Special treatment for end of lines
        state.before = state.before.slice(0, -1)
        evt.preventDefault()
        return true
      } else if (evt.which === 46 && state.after.slice(0, 1) === '\n') {
        state.after = state.after.slice(1)
        evt.preventDefault()
        return true
      }
    } else {
      state.selection = ''
      evt.preventDefault()
      return true
    }
  }),

  new Keystroke(function (evt, state, editor) {
    if (evt.which !== 37 /* left arrow */ && evt.which !== 39 /* right arrow */) {
      return
    }
    var isJump = (cledit.Utils.isMac && evt.altKey) || (!cledit.Utils.isMac && evt.ctrlKey)
    if (!isJump) {
      return
    }

    // Custom jump behavior
    var textContent = editor.getContent()
    var offset = getNextWordOffset(textContent, editor.selectionMgr.selectionEnd, evt.which === 37)
    if (evt.shiftKey) {
      // rebuild the state completely
      var min = Math.min(editor.selectionMgr.selectionStart, offset)
      var max = Math.max(editor.selectionMgr.selectionStart, offset)
      state.before = textContent.slice(0, min)
      state.after = textContent.slice(max)
      state.selection = textContent.slice(min, max)
      state.isBackwardSelection = editor.selectionMgr.selectionStart > offset
    } else {
      state.before = textContent.slice(0, offset)
      state.after = textContent.slice(offset)
      state.selection = ''
    }
    evt.preventDefault()
    return true
  })
]