 cloneArray     = require 'app/util/cloneArray'
 
 STORAGE_NAME    = 'shortcuts'
-STORAGE_VERSION = '1'
-THROTTLE_WAIT   = 300
+STORAGE_VERSION = '4'
+THROTTLE_WAIT   = 500
 
 module.exports =
 
+# Manages keyboard shortcuts.
+#
+# Wraps over a _shortcuts_ instance by default (if not passed explicitly
+# within opts.shortcuts), and makes sure we are listening and dispatching
+# correct keyboard events depending on an application's config.
+#
+# This also exposes convenience proxy methods to the underlying _keyconfig_
+# instance, and persists the state of a keyconfig#Collection to the app storage.
+#
 class ShortcutsController extends events.EventEmitter
 
   klass = this
 
   constructor: (opts={}) ->
-    # manages keyboard shortcuts.
-    #
-    # wraps over a _shortcuts_ instance by default (if not passed explicitly
-    # within opts.shortcuts), and makes sure we are listening and dispatching
-    # correct keyboard events depending on an application's config.
-    #
-    # this also exposes convenience proxy methods to get or update the underlying
-    # _keyconfig_ instance, and persist the state of key-bindings to the server.
 
     super()
 

    if opts.shortcuts instanceof Shortcuts
      @shortcuts = opts.shortcuts
    else
      raw = _.keys(defaults).reduce (acc, key) ->
        acc[key] = defaults[key].data
        return acc
      , {}
      @shortcuts = new Shortcuts raw


  addEventListeners: ->

    { appManager, appStorageController } = kd.singletons

        @shortcuts.on "key:#{key}", app.bound 'handleShortcut'


  # Renders a _kd#Modal_ immediately.
  #
  showModal: ->

    new ShortcutsModal {}, @shortcuts.config


  # Proxy to _shortcuts#get_.
  #
  # See: http://github.com/koding/shortcuts#api
  #
  get: ->

    @shortcuts.get.apply @shortcuts, Array::slice.call arguments

  # Updates a _keyconfig#Model_.
  #
  # This api is pretty-much same with _shortcuts#update_.
  #
  # One important difference is _silent_ argument is handled internally,
  # and not exposed. That's because passing _silent_ to _shortcuts#update_
  # as true makes sure _shortcuts_ doesn't add any keyboard event listeners,
  # yet it still updates the underlying _keyconfig_ instance.
  #
  # This is how we deal with shortcuts that should be handled manually.
  #
  update: (collectionName, modelName, value) ->

    throw 'value must be an object'  unless _.isObject value

    overrides = _.pick value, 'binding', 'options'

    unless Object.keys(overrides).length
      throw 'value should contain \'binding\' and/or \'options\' props'

    unless @_store.isReady
      console.warn 'changes won\'t be persisted, since storage is not ready yet'

    model = @shortcuts.get collectionName, modelName
    throw "#{modelName} not found"  unless model

    if overrides.binding
      overrides.binding = klass._insertPlatformBinding model, overrides.binding

    silent = klass._isCustomShortcut model

    res = @shortcuts.update collectionName, modelName, overrides, silent

    if silent then @_handleShortcutsChange @shortcuts.get(collectionName), res

    return res


  # Convenience method that returns a _keyconfig#Collection's_ json representation
  # for the current platform. Optionally takes a filter predicate.
  #
  getJSON: (name, predicate) ->

    set = @shortcuts.get(name)?.chain()

    if set
      if predicate then set = set.filter predicate

      set.map (model) ->
        _.extend model.toJSON(), binding: klass._getPlatformBinding model
      .value()


  # Returns the app storage key for the current platform.
  #
  # This will either be *shortcuts-mac* or *shortcuts-win*.
  #
  @getPlatformStorageKey: ->

    "#{STORAGE_NAME}-#{globals.keymapType}"


  # Returns _binding_ getter method name of a _keyconfig#Model_ for the current platform.
  #
  # This is memoized, so subsequent calls will return the cached result;
  # it still must be defined at static level since _globals_ must be ready.
  #
  # This will either be *getMacKeys* or *getWinKeys*.
  #
  # See: http://github.com/koding/keyconfig#api
  #
  @_bindingGetterMethodName: _.memoize ->

    kmt = globals.keymapType
    return "get#{kmt.charAt(0).toUpperCase()}#{kmt.slice 1, 3}Keys"


  # Given a _keyconfig#Model_, returns its binding for the current platform.
  #
  @_getPlatformBinding: (model) ->

    return model[klass._bindingGetterMethodName()]()


  # Returns _binding_ array index of a _keyconfig#Model_ for the current platform.
  #
  # This is memoized, so subsequent calls will return the cached result;
  # it still must be defined at static level since _globals_ must be ready.
  #
  # This will either be 1 for mac or 0 for windows/linux.
  #
  # See: http://github.com/koding/keyconfig#spec
  #
  @_bindingPlatformIndex: _.memoize ->

    if globals.keymapType is 'mac' then 1 else 0


  # Given a keyconfig#Model and a 1d array of bindings, validates and inserts
  # the array at the correct position for the current platform, and returns
  # a deep clone of the model's binding.
  #
  @_insertPlatformBinding: (model, platformBinding) ->

    binding = cloneArray model.binding

    if _.isArray platformBinding
      binding[klass._bindingPlatformIndex()] = platformBinding.filter _.isString

    return binding


  # Returns *true* if given _keyconfig#Model_ is a custom shortcut.
  #
  @_isCustomShortcut: (model) ->

    if model.options?.custom then yes else no
