# @ts-check

import keyBindingShell_filter from './filter'
import keyBindingShell_forEach from './forEach'
import keyBindingShell_formatHotkey from './formatHotkey'
import keyBindingShell_isFunction from './isFunction'
import keyBindingShell_noop from './noop'
import keyBindingShell_replace from './replace'
import keyBindingShell_split from './split'

class KeyBindingShell

  constructor: ->

    ###* @type import('./keyBindingShell').KeyBindingShell['mapBound'] ###
    @mapBound = {}

    ###* @type import('./keyBindingShell').KeyBindingShell['mapCallback'] ###
    @mapCallback = {}

    ###* @type import('./keyBindingShell').KeyBindingShell['mapPrevented'] ###
    @mapPrevented = {}

  ###* @type import('./keyBindingShell').KeyBindingShell['add'] ###
  add: (keyMixed, callback) ->

    unless keyBindingShell_isFunction callback
      throw new Error "KeyBindingShell.add: Callback is not a function for key: #{keyMixed}"

    [keyBindingShell_key, keyBindingShell_name] = keyBindingShell_split (keyBindingShell_replace keyMixed, ':down', ''), '.'
    unless keyBindingShell_name then keyBindingShell_name = ''

    @register keyBindingShell_key

    # Item: [Name, Function]
    @mapCallback[keyBindingShell_key].Push [keyBindingShell_name, callback]
    return

  ###* @type import('./keyBindingShell').KeyBindingShell['fire'] ###
  fire: (keyMixed) ->
    keyBindingShell_forEach (@getListItem keyMixed), (it) ->
      unless keyBindingShell_isFunction it[1]
        throw new Error "KeyBindingShell.fire: Callback is not a function for key: #{keyMixed}"
      it[1]()
      return
    return

  ###* @type import('./keyBindingShell').KeyBindingShell['formatKey'] ###
  formatKey: (key, prefix = '') ->
    keyBindingShell_key = keyBindingShell_formatHotkey keyBindingShell_replace key, ':down', ''
    unless prefix then return keyBindingShell_key
    return "#{prefix}#{keyBindingShell_key}"

  ###* @type import('./keyBindingShell').KeyBindingShell['getListItem'] ###
  getListItem: (keyMixed) ->

    [keyBindingShell_key, keyBindingShell_name] = keyBindingShell_split (keyBindingShell_replace keyMixed, ':down', ''), '.'

    keyBindingShell_list = @mapCallback[keyBindingShell_key]
    unless keyBindingShell_list then return []

    if keyBindingShell_name then keyBindingShell_list = keyBindingShell_filter keyBindingShell_list, (it) -> it[0] == keyBindingShell_name
    return keyBindingShell_list

  ###* @type import('./keyBindingShell').KeyBindingShell['isPrevented'] ###
  isPrevented: (key) -> @mapPrevented[key] == true

  ###* @type import('./keyBindingShell').KeyBindingShell['prepare'] ###
  prepare: (key) ->

    if @mapCallback[key] then return

    @mapBound[key] = => @fire key
    @mapCallback[key] = []
    @mapPrevented[key] = false
    return

  ###* @type import('./keyBindingShell').KeyBindingShell['prevent'] ###
  prevent: (key, isPrevented) ->

    @prepare key
    @mapPrevented[key] = isPrevented

    keyBindingShell_callback = @mapBound[key]

    keyBindingShell_key = @formatKey key, '~'
    if isPrevented then keyBindingShell_key = @formatKey key

    keyBindingShell_noop keyBindingShell_callback, keyBindingShell_key

    Native 'Hotkey, % keyBindingShell_key, % keyBindingShell_callback, On'
    return

  ###* @type import('./keyBindingShell').KeyBindingShell['register'] ###
  register: (key) ->

    @prepare key

    keyBindingShell_callback = @mapBound[key]

    keyBindingShell_key = @formatKey key, '~'
    if @mapPrevented[key] then keyBindingShell_key = @formatKey key

    keyBindingShell_noop keyBindingShell_callback, keyBindingShell_key

    Native 'Hotkey, % keyBindingShell_key, % keyBindingShell_callback, On'
    return

  ###* @type import('./keyBindingShell').KeyBindingShell['remove'] ###
  remove: (keyMixed) ->

    [keyBindingShell_key, keyBindingShell_name] = keyBindingShell_split (keyBindingShell_replace keyMixed, ':down', ''), '.'

    unless keyBindingShell_name
      @mapCallback[keyBindingShell_key] = []
      return

    keyBindingShell_listNew = keyBindingShell_filter @mapCallback[keyBindingShell_key], (it) -> it[0] != keyBindingShell_name
    @mapCallback[keyBindingShell_key] = keyBindingShell_listNew
    return

keyBindingShell_noop KeyBindingShell
