/// class PD.Context.ContextCollection

PD.Context.ContextCollection =
Class.extend({

    //===================================
    // FIELDS
    //===================================

    _collection: null,
    _options: null,
    _childContextMaps: null,

    //===================================
    // CONSTRUCTOR
    //===================================

    init: function (options) {

        this._collection = {};

        this._childContextMaps = {};

        this._options = _.extend({
            add: function () { },
            remove: function () { }
        });

    },

    //===================================
    // PROPERTIES
    //===================================

    get_options: function () {
        return this._options;
    },

    //===================================
    // PUBLIC METHODS
    //===================================

    add: function (context) {

        var contextId = context.get_id(),
            addFn = this.get_options().add;

        if (!this.exists(contextId)) {;

            this._collection[contextId] = context;

            this._createChildContextMap(context);

            addFn({
                contextId: contextId
            });

        }

    },

    remove: function (context, destroyChildContexts) {

        var targetContextId = this._getContextId(context),
            targetContext = this.getById(targetContextId),
            removeFn = this.get_options().remove;

        if (targetContext != null) {

            delete this._collection[targetContextId];

            if (destroyChildContexts !== false) {
                this._destroyChildContexts(targetContext);
            }

            removeFn({
                contextId: targetContextId
            });

        }

    },

    each: function (fn) {

        fn = fn || function () { };

        _.each(this._collection, function (value, key, list) {
            fn(key, value);
        });

    },

    exists: function (context) {
    
        var contextId = this._getContextId(context);

        return this._collection[contextId] != null;

    },

    getById: function (contextId) {

        var result = null;

        contextId = this._getContextId(contextId);

        if (this.exists(contextId)) {
            result = this._collection[contextId];
        }
 
        return result;

    },

    //===================================
    // PRIVATE METHODS
    //===================================

    _getContextId: function (context) {

        var result = null;

        if (context != null) {

            if (_.isString(context)) {
                result = context.toLowerCase();
            }
            else {
                result = context.get_id();
            }

        }

        return result;

    },

    _createChildContextMap: function (context) {

        var mapArray = null,
            focusedContext = context,
            focusedContextId = null;

        do {
            
            focusedContext = focusedContext.get_parent();

            if (focusedContext != null) {

                focusedContextId = focusedContext.get_id();

                if (this._childContextMaps[focusedContextId] == null) {
                    this._childContextMaps[focusedContextId] = [];
                }

                mapArray = this._childContextMaps[focusedContextId];

                mapArray[mapArray.length] = context.get_id();

            }

        } while (focusedContext != null)

    },

    _destroyChildContexts: function (context, map) {

        var that = this,
            focusedContext = context,
            focusedContextId = context.get_id(),
            mapArray = this._childContextMaps[focusedContextId] || [],
            oldArray = null,
            newArray = null;

        if (mapArray != null) {
        
            mapArray.unshift(focusedContextId);

            _.each(mapArray, function (contextId) {
                that.remove(contextId, false);
                delete that._childContextMaps[contextId];
            });

        }

        //remove references to children from parent maps 

        do {

            focusedContext = focusedContext.get_parent();

            if (focusedContext != null) {
                oldArray = this._childContextMaps[focusedContext.get_id()];
                this._childContextMaps[focusedContext.get_id()] = _.difference(oldArray, mapArray);
            }

        } while (focusedContext != null)


    }

});