/// class ProspectDirector.Web.UI.FeatureSets.FeatureSet

/**
@constructor
@class Web.UI.FeatureSets.FeatureSet
**/
ProspectDirector.Web.UI.FeatureSets.FeatureSet = Class.extend({

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

    _options: null,
    _groups: null,
    _editors: null,
    _editorDefinitions: null,

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

    init: function (options) {
        this._options = $.extend({
            eventBus: new ProspectDirector.Web.UI.EventBus()
        }, options);
        this._fill(options);
    },

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

    /**
    @public
    @property get_eventBus 
    @type {Object}
    **/
    get_options: function () {
        return this._options;
    },

    /**
    @public
    @property get_data 
    @type {Object}
    **/
    get_data: function () {
        return this.get_options();
    },

    /**
    @public
    @property get_groups 
    @type {Web.UI.FeatureSets.FeatureGroupCollection}
    **/
    get_groups: function () {
        return this._groups;
    },

    /**
    @public
    @property get_editorDefinitions 
    @type {Web.UI.FeatureSets.EditorDefinitionCollection}
    **/
    get_editorDefinitions: function () {
        return this._editorDefinitions;
    },

    /**
    @public
    @property get_editors 
    @type {Web.UI.FeatureSets.EditorCollection}
    **/
    get_editors: function () {
        return this._editors;
    },

    /**
    @public
    @property get_eventBus 
    @type {Web.UI.EventBus}
    **/
    get_eventBus: function () {
        return this.get_options().eventBus;
    },

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

    /**
    @public
    @method on
    **/
    on: function (eventType, handler) {

        var featureSet = $(this);

        if ($.isFunction(handler)) {
            featureSet.on(eventType, handler);
        }

    },

    /**
    @public
    @method trigger
    **/
    trigger: function (eventType, args) {

        var featureSet = $(this);

        featureSet.trigger(eventType, args);

    },

    /**
    @public
    @method commit
    **/
    commit: function () {

        this.get_editors().each(function (index, editor) {
            if ($.isFunction(editor.commit)) {
                editor.commit();
            }
        });

    },

    refresh: function (args) {
        $(this).trigger("refresh", args);
    },

    /**
    @public
    @method getFeatures
    **/
    getFeatures: function (typeName) {

        var result = new ProspectDirector.Web.UI.FeatureSets.FeatureCollection();

        this.get_groups().each(function (index, group) {
            group.get_features().each(function (index2, feature) {
                result.add(feature);
            });
        });

        return result;

    },

    /**
    @public
    @method getFeature
    **/
    getFeature: function (typeName) {

        var result = null;

        this.get_groups().each(function (index, group) {
            result = group.get_features().getByTypeName(typeName);
            if (result) {
                return false;
            }
        });

        return result;

    },

    /**
    @public
    @method getEditorInitializer
    **/
    getEditorInitializer: function (typeName) {

        var id = typeName.substr(typeName.lastIndexOf(".") + 1);

        id = id.substr(0, 1).toLowerCase() + id.substr(1);

        return this.get_options().initializers[id];

    },

    /**
    @public
    @method getValue
    @param key
    @return {Object} The retrieved value or `undefined` if the provided `key` is not found.
    **/
    getValue: function (key) {

        var result = undefined;

        this.getFeatures().each(function (index, feature) {
            if (feature.getDataKeys().contains(key)) {
                result = feature.getValue(key);
                return false;
            }
        });

        return result;

    },

    /**
    @public
    @method setValue
    @param key {String} The key to use to store the value supplied.
    @param value {Object} The value to store.
    **/
    setValue: function (key, value) {

        var result = undefined;

        this.getFeatures().each(function (index, feature) {
            if (feature.getDataKeys().contains(key)) {
                feature.setValue(key, value);
                result = true;
                return false;
            }
        });

        return result;

    },

    /**
    @public
    @method getValues
    @param key
    @return {Object} A dictionary of key/value pairs. If the value cannot be retrieved using a key provided the value will be set to `undefined`.
    **/
    getValues: function (keys) {

        var result = {},
            features = this.getFeatures(),
            keysList = new ProspectDirector.Web.List(keys);

        keysList.each(function (index, key) {
            result[key] = undefined;
        });

        keysList.each(function (index1, key) {

            features.each(function (index2, feature) {
                if (feature.getDataKeys().contains(key)) {
                    result[key] = feature.getValue(key);
                    return false;
                }
            });

        });

        return result;

    },

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

    /**
    @private
    @method _fill
    **/
    _fill: function (data) {

        this._fillFeatureGroups(data);

        this._fillEditorDefinitions(data);

        this._fillEditors();

    },

    /**
    @private
    @method _fillEditorDefinitions
    **/
    _fillEditorDefinitions: function (data) {

        var that = this,
            group = null,
            definitions = null;

        this._editorDefinitions = new ProspectDirector.Web.UI.FeatureSets.EditorDefinitionCollection();

        for (key in data.editors) {

            if (data.editors.hasOwnProperty(key)) {

                definitions = new ProspectDirector.Web.List(data.editors[key]);

                definitions.each(function (index, definition) {

                    that.get_editorDefinitions().add(new ProspectDirector.Web.UI.FeatureSets.EditorDefinition({
                        parent: that,
                        data: definition,
                        initializer: that.getEditorInitializer(definition.type),
                        feature: that.getFeature(definition.type),
                        eventBus: that.get_eventBus(),
                        groupId: key
                    }));

                });

            }

        }

    },

    /**
    @private
    @method _fillFeatureGroups
    **/
    _fillFeatureGroups: function (data) {

        var that = this,
            groups = new ProspectDirector.Web.List(data.context.featureSet.groups);

        this._groups = new ProspectDirector.Web.UI.FeatureSets.FeatureGroupCollection({
            parent: this
        });

        groups.each(function (index, group) {

            that.get_groups().add(new ProspectDirector.Web.UI.FeatureSets.FeatureGroup({
                parent: that,
                data: group
            }));

        });

    },

    /**
    @private
    @method _fillEditors
    **/
    _fillEditors: function () {

        var that = this;

        this._editors = new ProspectDirector.Web.UI.FeatureSets.EditorCollection({
            parent: this
        });

        this.get_editorDefinitions().each(function (index, definition) {

            var editor = definition.activate();

            if (editor) {
                that.get_editors().add(editor);
            }
            else {
                throw "Editor " + definition.get_script() + " could not be resolved.";
            }

        });

    }

});