#= require ./base

class Ultimate.Backbone.App
  @App: null

  name: null
  warnOnMultibind: true
  preventMultibind: false
  performanceReport: true
  groupBindingLog: true

  Models: {}
  Collections: {}
  Routers: {}
  ViewMixins: {}
  ProtoViews: {}
  Views: {}
  viewInstances: []

  constructor: (name = null) ->
    if @constructor.App
      throw new Error('Can\'t create new Ultimate.Backbone.App because the single instance has already been created')
    else
      cout 'info', 'Ultimate.Backbone.App.constructor', name, @
      @constructor.App = @
      @name = name

  start: ->
    @performanceReport = DEBUG_MODE if @performanceReport
    if @performanceReport
      if _.isFunction(performance?.now)
        performanceStart = performance.now()
      else
        cout 'warn', 'performance.now() isnt available'
        @performanceReport = null
    bindedViewsCount = @bindViews().length
    if @performanceReport
      performanceViews = performance.now()
      cout 'info', "Binded #{bindedViewsCount} view#{if bindedViewsCount is 1 then '' else 's'} in #{Math.round((performanceViews - performanceStart) * 1000)}\u00B5s"
    @bindCustomElements(null, true)
    if @performanceReport
      performanceBinders = performance.now()
      bindersCount = @customElementBinders.length
      cout 'info', "Processed #{bindersCount} custom element binder#{if bindersCount is 1 then '' else 's'} in #{Math.round((performanceBinders - performanceViews) * 1000)}\u00B5s"

  bindViews: (jRoot = $('html')) ->
    if @groupBindingLog
      console?.groupCollapsed? 'bindViews on', jRoot
    bindedViews = []
    sortedViews = _.sortBy(_.pairs(@Views), (p) -> p[1].priority)
    for [viewName, viewClass] in sortedViews when viewClass::el
      #cout 'info', "Try bind #{viewName} [#{viewClass::el}]"
      jRoot.find(viewClass::el).each (index, el) =>
        if @canBind(el, viewClass)
          view = new viewClass(el: el)
          cout 'info', "Binded view #{viewName}:", view
          @viewInstances.push view
          bindedViews.push view
    if @groupBindingLog
      console?.groupEnd?()
    bindedViews

  canBind: (element, viewClass) ->
    if @warnOnMultibind or @preventMultibind
      views = @getViewsOnElement(element)
      l = views.length
      if l > 0
        if @warnOnMultibind
          cout 'warn', "Element already has binded #{l} view#{if l > 1 then 's' else ''}", element, viewClass, views
        not @preventMultibind
      else
        true
    else
      true

  getViewsOnElement: (element) ->
    element = if element instanceof jQuery then element[0] else element
    _.where @viewInstances, el: element

  unbindViews: (views) ->
    for view in views
      view.undelegateEvents()
      view.leave?()
    @viewInstances = _.without(@viewInstances, views...)

  getFirstView: (viewClass) ->
    for view in @viewInstances
      if view.constructor is viewClass
        return view
    null

  getAllViews: (viewClass) ->
    _.filter(@viewInstances, (view) -> view.constructor is viewClass)



  customElementBinders: []

  registerCustomElementBinder: (binder) ->
    @customElementBinders.push binder

  bindCustomElements: (jRoot = $('body'), initial = false) ->
    for binder in @customElementBinders
      binder arguments...



_.extend Ultimate.Backbone.App::, Backbone.Events