###
Bongo.js
Unfancy models for MongoDB

(c) 2011 Koding, Inc.

@class: Subcollection
@author: Christopher Thorn
###
Base = require './base'
module.exports = class Subcollection extends Base
  ###
  @vars.
  ###
  {defineProperty} = Object
  ###
  @snippet.
  @description: import the non-buggy methods from the Array
    prototype i.e., the destructive ones that modify the
    instance itself.  Other ones (e.g. slice, concat) will 
    actually return instances of Array, not "this.constructor", 
    which would take into account inheritance.  I think this is
    going to be fixed in Harmony. By not importing them, we
    prevent astonishment, and spread awareness of the 
    shortcomings of some Array prototype methods.
  ###
  for method in 'forEach,indexOf,join,pop,reverse,shift,sort,splice,unshift'.split ','
    @::[method] = Array::[method]
  ###
  @method: property of the constructor.
  @signature: Subcollection.of(annotation)
  @return: the annotation wrapped in a new array.
  @description: this is a sort of anti-sugar for those who
    prefer verbosity (and extra function call overhead. :))
    Basically, instead of saying [String], one can say
    Subcollection.of(String)
  ###
  @of =(annotation)-> [annotation]
  ###
  @constructor.
  @signature: new Subcollection(parent, children, annotation)
  @param: parent - the containing model
  @param: children - an array containing the new members of
    this subcollection.
  @param: annotation - the constructor/cast of the arrayed instances/primitives.
  @description: create a new subcollection, and push all the
    items from the supplied array onto it.  Subcollections are 
    psuedo-typed.
  ###
  constructor:(parent, children, annotation)->
    defineProperty @, 'parent',             value: parent
    # avoid a(n explicit) circular dependency here:
    defineProperty @, 'parentConstructor',  value: parent.constructor
    defineProperty @, 'annotation',         value: annotation
    @push children...
  ###
  @method.
  @signature: Subcollection::findById(id);
  @param: id - an id matching the _id property of the sought-
    after document.
  @description: find a child element by its _id property
  ###
  findById:(id, indexOnly=no)->
    for doc, index in @ when doc._id?.equals id
      return if indexOnly then index else doc
  ###
  @method.
  @signature: Subcollection::get()
  ###
  ###
  @method.
  @signature: Subcollection::push(children...)
  @param: children... - rest params containing the objects to push to the end of the array.
  @description: override native Array::push to add value casting
  ###
  push:(children...)->
    subcollection = @
    [].push.apply subcollection,
      for child, index in children
        child = @parentConstructor.castValue child, @annotation
        if child instanceof @parentConstructor
          child.inSubcollection = yes
          unless child._id?
            child._id = new ObjectId
        child
  ###
  @method.
  @signature: Subcollection::remove(id);
  @param: id - an id matching the _id property of the sought-
    after document.
  @description: remove a child element by its _id property
  ###
  remove:(id)->
    @splice @indexOf @findById(id), 1
  ###
  @method.
  @signature: Subcollection::toJSON()
  @return: an array slice of the entire subcollection. (This
    is ((perhaps a hack)) so we can have array notation in the JSON.
    Anyone know of a better trick?)
  @todo: reimplement?
  ###
  toJSON:->
    [].call @