###
Bongo.js
Unfancy models for MongoDB

@class: Base
@author: Christopher Thorn
###
{EventEmitter2} = require 'eventemitter2'
module.exports = class Base extends EventEmitter2
  ###
  @method: property of the constructor.
  @signature: Base.extend(constructor, proto)
  @param: static - members to be mixed into the constructor.
  @param: proto - an optional object (or a function that returns
    an object) that provides the prototype properties for the
    model.  You can use this for even more sugar to assign
    instance methods to your models. It should contain a "constructor"
    property:
      @property - constructor - a named function that will be used as 
        the constructor for the new klass.  If you don't name your
        function some features may break.  There is no way to change 
        the name of a function runtime, since the Function::name 
        property is read-only.  So, do give it name. :)
  @description: This is a sugar method intended to simplify
    inheritance for those who are using Bongo with JS.  Writing
    your application in CoffeeScript is recommended, but in that
    case you shouldn't use Model.extend—use classes instead.
  ###
  @extend = (static, proto)->
    props = [static, proto]
    for prop, index in props
      if 'function' is typeof prop
        props[index] = new prop
    [proto, static] = props unless proto
    parent = @
    child = proto.constructor
    for own prop of parent
      child[prop] = parent[prop]
    ctor =->
      @constructor = child
      return
    ctor:: = parent::
    child:: = new ctor
    for own prop of proto
      child::[prop] = proto[prop]
    child.__super__ = parent::
    child.set static
    child
  ###
  @method.
  @signature: Base::Uber(arguments...)
  @description: this is sugar to simplify inheritance for
    those who are using Bongo and writing their application
    with JS.  Avoid Uber() when you are using CoffeeScript—
    prefer super().
  ###
  Uber:->
    @constructor.__super__.constructor.apply @, arguments
  ###
  @method.
  @signature: Base::uber(methodName, arguments)
  @description: Call a method on the superclass.
  @description: this is sugar to simplify inheritance for
    those who are using Bongo and writing their application
    with JS.  Avoid uber() when you are using CoffeeScript—
    prefer super().
  ###
  uber:(methodName, args...)->
    @constructor.__super__[methodName].apply @, args
  ###
  @method: property of the constructor
  @signature: Base.set(options)
  @description: this is sugar for setting constructor properties.
  ###
  @set =(options)->
    for own optionName, option of options
      @[optionName] = option
    @
  
  @inheritanceChain =(Klass=@, glue)->
    unless chain = @inheritanceChain_
      proto = Klass.prototype
      chain = [Klass]
      while proto = proto.__proto__
        chain.push proto.constructor
      # memoize the inheritance chain because it ought not to change
      # and it's a bit expensive to have to travese the inheritance
      # chain every time.
      Object.defineProperty @, 'inheritanceChain_', value: chain
    if glue
      (constructor.name for constructor in chain).join glue
    else
      chain

  inheritanceChain:(glue)->
    Base.inheritanceChain @constructor, glue

  @addWaitingInstance =(instance)->
    unless @waiting
      Object.defineProperty @, 'waiting', value: []
    @waiting.push instance

  @whenReady =(instance, callback)->
    if instance.isReady
      callback null, instance
    else
      @addWaitingInstance instance
      instance.on 'ready', =>
        callback null, instance

  whenReady:(instance, callback)->
    [callback, instance] = [instance, callback] unless callback
    instance or= @
    if instance.isReady
      callback null, instance
    else
      instance.constructor.addWaitingInstance instance
      instance.on 'ready', =>
        callback null, instance