bindable   = require "bindable"
Model      = require "./model"
decor      = require "./decor/factory"
factories  = require "factories"
type       = require "type-component"
protoclass = require "protoclass"

class ModelCollection extends bindable.Collection

  ###
  ###

  __isModel: true

  ###
  ###

  constructor: (source) ->
    @_removeModelListeners()
    @modelFactory = factories.factory.create (data) => @createModel data

    if Object.prototype.toString.call(source) is "[object Array]"
      source = source.map (data) => 
        return data if data.__isBindable 
        @modelFactory.create data

    super source
    decor.decorate @

  ###
  ###

  createModel: (data) -> new Model data

  ###
  ###

  model: (data, push = true) ->
    @_listenToModel model = @modelFactory.create data

    # TODO - need to throw _load in a queue
    # behind the created model incase the _id hasn't been
    # set on the model - might generate duplicates

    if model.isNew or not model.get("_id")
      model.once "saved", () =>
        unless push
          @push model
        @_updateInfo()
        @_relisten()

    if push
      @push model
    model

  ###
  ###

  decorate: (options) -> decor.decorate @, options

  ###
  ###

  update: (data) =>
    @reset data

  ###
  ###

  reset: (src) =>

    src  = @map(src)
    src2 = src.concat()
    esrc = @source().concat()

    # update existing
    for existingItem in esrc
      for newItem, i in src
        if @_compare existingItem, newItem
          existingItem.update newItem
          src.splice i, 1
          break


    `if(this.removeItems !== false) for(var i = esrc.length; i--;) { var existingItem = esrc[i]`
    found = false
    for newItem in src2
      if @_compare existingItem, newItem
        found = true
        break

    unless found
      @splice i, 1
    `}`

    # insert the reset
    @push(@_map(item)) for item in src

    @_updateInfo()
    @_relisten()


  ###
  ###

  _removeModelListeners: () ->
    if @_modelListeners
      listener.dispose() for listener in @_modelListeners

    @_modelListeners = []

  ###
  ###

  _relisten: () ->
    @_removeModelListeners()
    @_listenToModel(model) for model in @source()

  ###
  ###

  _listenToModel: (model) ->
    @_modelListeners.push model.once? "dispose", () => @_deferReload()
    model

  ###
  ###

  map: (src) -> @deserialize src

  ###
  ###

  deserialize: (src) -> src

  ###
  ###

  _map: (item) -> @modelFactory.create item

  ###
  ###

  _deferReload: () ->
    clearTimeout @_reloadTimeout
    @_reloadTimeout = setTimeout @_reload, 100

  ###
  ###

  _reload: () => @reload()

  ###
  ###

  _compare: (a, b) ->
    aid = a.get("_id")
    return (aid is b._id) or (aid is b)


module.exports = protoclass.setup ModelCollection
