# Source: agg-house.js

    /** @module agg-house */

    import AggRoom from './core/agg-room';
    import Config from './core/config';
    import {AggHouseInvalidArgumentError} from './types/error';
    import deepMerge from 'deepmerge';

    /** Class that manages aggregators. */
    class AggHouse {
        /**
         * Initialize an AggHouse.
         * @param {Object} optionalConfigs Optional configurations.
         */
        constructor(optionalConfigs) {
            /** @private {!Object} */
            this.aggregatorData_ = {};

            /** @private {!Array<AggRoom>} */
            this.aggregatorRooms_ = [];

            /** @private {!Object} */
            this.configs_ = Config.initConfigs(optionalConfigs);
        }

        /**
         * Get a configuration value.
         * @param {string} name Configuration name.
         * @return {*} Configuration value.
         */
        getConfig(name) {
            return this.configs_[name];
        }

        /**
         * Get data of an aggregator.
         * @param {string} aggregatorName Name of aggregator name to get data from.
         * @return {!Object} Aggregator data.
         * @throws {AggHouseInvalidArgumentError} Throw an error if an aggregator of
         * give name has not been loaded.
         */
        getData(aggregatorName) {
            if (this.aggregatorRooms_.findIndex((ar) =>
                ar.name === aggregatorName) < 0) {
                throw new AggHouseInvalidArgumentError('\'' + aggregatorName +
                    '\' is not loaded!');
            }
            return Object.assign({}, this.aggregatorData_[aggregatorName]);
        }

        /**
         * Get a list of names and versions of loaded aggregators.
         * @return {!Array<Object>}
         */
        getLoadedAggregators() {
            return this.aggregatorRooms_.map((ar) => {
                return {
                    'name': ar.name,
                    'version': ar.version,
                };
            });
        }

        /**
         * Create an AggRoom for an aggregator add it to control list.
         * @param {!Aggregator} aggregator Aggregator to load.
         * @throws {AggHouseInvalidArgumentError} Throw an error if an aggregator of
         * the same name is already loaded.
         */
        loadAggregator(aggregator) {
            let aggRoom = new AggRoom(aggregator, this);
            let aggName = aggRoom.name;

            if (this.aggregatorData_[aggRoom.name] !== undefined) {
                throw new AggHouseInvalidArgumentError('\'' + aggName +
                    '\' already loaded!');
            }

            this.aggregatorData_[aggName] = {};
            this.aggregatorRooms_.push(aggRoom);
        }

        /**
         * Process incoming update and deliver them to corresponding aggregators.
         * @param {!Object} update Update data.
         * @param {number} classId Class ID for data.
         */
        processUpdate(update, classId) {
            let matchedArCounts = 0;
            this.aggregatorRooms_.forEach((ar) => {
                if (ar.containsClass(classId)) {
                    ar.sendUpdate(update);
                    matchedArCounts++;
                }
            });
            console.info(`Sent update to ${matchedArCounts} aggregator(s).`);
        }

        /**
         * Unload an aggregator and dispose its AggRoom.
         * @param {!Aggregator|string} aggregator The aggregator to unload.
         */
        unloadAggregator(aggregator) {
            let aggName = typeof aggregator === 'string' ? aggName : aggName.name;
            let aggRoomIndex = this.aggregatorRooms_.findIndex(
                (ar) => ar.name === aggName);
            if (aggRoomIndex >= 0) {
                this.aggregatorRooms_[aggRoomIndex].dispose();
                this.aggregatorRooms_.slice(aggRoomIndex, 1);
                delete this.aggregatorData_[aggName];
            } else {
                throw new AggHouseInvalidArgumentError('\'' + aggName +
                    '\' not found!');
            }
        }

        /**
         * Update data produced by an aggregator.
         * @param {string} aggregatorName Name of aggregator name to get data from.
         * @param {string} newData New data from aggregator.
         * @param {boolean|null} isAppending If is appending to or replacing
         * original.
         * @throws {AggHouseInvalidArgumentError} Throw an error if an aggregator of
         * give name has not been loaded.
         */
        updateData(aggregatorName, newData, isAppending) {
            if (this.aggregatorRooms_.findIndex((ar) =>
                ar.name === aggregatorName) < 0) {
                throw new AggHouseInvalidArgumentError('\'' + aggregatorName +
                    '\' is not loaded!');
            }
            if (isAppending) {
                this.aggregatorData_[aggregatorName] = deepMerge(
                    this.aggregatorData_[aggregatorName], newData);
            } else {
                this.aggregatorData_[aggregatorName] = newData;
            }
        }
    }

    export default AggHouse;