
const specialKeyMap = {
  'backspace': 8,
  'tab': 9,
  'enter': 13,
  'shift': 16,
  'ctrl': 17,
  'alt': 18,
  'pausebreak': 19,
  'capslock': 20,
  'escape': 27,
  'pgup': 33,
  'pgdown': 34,
  'end': 35,
  'home': 36,
  'left': 37,
  'up': 38,
  'right': 39,
  'down': 40,
  'insert': 45,
  'delete': 46,
  'left-window': 91,
  'right-window': 92,
  'select': 93,
  'numpad0': 96,
  'numpad1': 97,
  'numpad2': 98,
  'numpad3': 99,
  'numpad4': 100,
  'numpad5': 101,
  'numpad6': 102,
  'numpad7': 103,
  'numpad8': 104,
  'numpad9': 105,
  'multiply': 106,
  'add': 107,
  'subtract': 109,
  'decimal': 110,
  'divide': 111,
  'f1': 112,
  'f2': 113,
  'f3': 114,
  'f4': 115,
  'f5': 116,
  'f6': 117,
  'f7': 118,
  'f8': 119,
  'f9': 120,
  'f10': 121,
  'f11': 122,
  'f12': 123,
  'numlock': 144,
  'scrolllock': 145,
  'semi-colon': 186,
  'equals': 187,
  'comma': 188,
  'dash': 189,
  'period': 190,
  'fwdslash': 191,
  'grave': 192,
  'open-bracket': 219,
  'bkslash': 220,
  'close-braket': 221,
  'single-quote': 222
}

class Input {
  constructor (canvas) {
    this.keyList = {}
    this.previousKeyList = {}
    this.canvas = canvas

    this.mouse = {
      pos: { x: 0, y: 0 },
      frame: { x: 0, y: 0 },
      previous: { x: 0, y: 0 }
    }

    window.addEventListener('keydown', (e) => this.keyDown(e), false)
    window.addEventListener('keyup', (e) => this.keyUp(e), false)

    canvas.addEventListener('mousemove', (e) => {
      let x
      let y

      if (e.changedTouches) {
        let touch = e.changedTouches[0]
        if (touch) {
          e.pageX = touch.pageX
          e.pageY = touch.pageY
        }
      }

      if (e.pageX || e.pageY) {
        x = e.pageX
        y = e.pageY
      } else {
        x = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft
        y = e.clientY + document.body.scrollTop + document.documentElement.scrollTop
      }

      x -= canvas.offsetLeft
      y -= canvas.offsetTop

      this.mouse.frame.x = x
      this.mouse.frame.y = y
    }, false)

    canvas.addEventListener('mousedown', (e) => {
      e.preventDefault()
      this.mouse['btn' + e.button] = true
    })

    canvas.addEventListener('mouseup', (e) => {
      e.preventDefault()
      this.mouse['btn' + e.button] = false
    })

    canvas.addEventListener('contextmenu', (e) => {
      e.preventDefault()
    })
  }

  get mousePos () {
    return this.mouse.pos
  }

  get mouseMoved () {
    return (this.mouse.pos.x !== this.mouse.previous.x ||
      this.mouse.pos.y !== this.mouse.previous.y)
  }

  get mouseOffset () {
    return {
      x: this.mouse.previous.x - this.mouse.pos.x,
      y: this.mouse.previous.y - this.mouse.pos.y
    }
  }

  toggleKey (keyCode, on) {
    // Find key in special key list
    let key = null
    for (let k in specialKeyMap) {
      let val = specialKeyMap[k]
      if (keyCode === val) {
        key = k
        break
      }
    }

    // Use fromCharCode
    if (!key) {
      key = String.fromCharCode(keyCode).toLowerCase()
    }

    this.keyList[key] = (on === true)
  }

  keyDown (e) {
    let keycode

    if (window.event) {
      keycode = window.event.keyCode
    } else if (e) {
      keycode = e.which
    }

    this.toggleKey(keycode, true)
  }

  keyUp (e) {
    let keycode

    if (window.event) {
      keycode = window.event.keyCode
    } else if (e) {
      keycode = e.which
    }

    this.toggleKey(keycode, false)
  }

  down (key) {
    return this.keyList[key] != null ? this.keyList[key] : false
  }

  downLast (key) {
    return this.previousKeyList[key] != null ? this.previousKeyList[key] : false
  }

  isDown (key) {
    return this.down(key) && this.downLast(key)
  }

  isUp (key) {
    return !this.isDown(key)
  }

  isPressed (key) {
    return this.down(key) === true && this.downLast(key) === false
  }

  update () {
    this.previousKeyList = {}
    for (let k in this.keyList) {
      if (this.keyList[k] === true) {
        this.previousKeyList[k] = true
      }
    }

    // Mouse positions in the previous frame
    this.mouse.previous.x = this.mouse.pos.x
    this.mouse.previous.y = this.mouse.pos.y

    // Mouse positions in the current frame
    // Convert to OpenGL coordinate system
    this.mouse.pos.x = this.mouse.frame.x / this.canvas.width * 2 - 1
    this.mouse.pos.y = this.mouse.frame.y / this.canvas.height * 2 - 1
  }
}

export default Input
