'use strict';

import 'core-js/fn/object/entries';

import createStream from 'cyclejs-stream';
import mapValues from 'map-values';
import mergeObjects from 'merge-object';
import getParametersNames from 'get-parameter-names';


function _makeInjectFn(streamWithDependencies) {
    return function inject(...inputObjects) {
        let combinedInputObject = inputObjects.length === 1 ?
            inputObjects[0] :
            inputObjects.reduce(mergeObjects, {});

        for(let [ , { dependencies, stream } ] of Object.entries(streamWithDependencies)) {
            let streamDependencies = dependencies.map((dependencyName) => {
                if(!combinedInputObject.hasOwnProperty(dependencyName)) {
                    throw new Error(`Dependency "${dependencyName}" is not available!`);
                }
                
                // shareReplay is here for circural dependent streams
                // otherwise there will be endless loop of subscribing
                return combinedInputObject[dependencyName].shareReplay(1);
            });

            stream.inject(...streamDependencies);
        }

        if (inputObjects.length === 1) {
            return inputObjects[0];
        } else if (inputObjects.length > 1) {
            return inputObjects;
        } else {
            return null;
        }
    };
}


function _makeDisposeFn(group) {
    return function dispose() {
        for (let [ , stream ] of Object.entries(group)) {
            stream.dispose();
        }
    };
}

export default function createGroup(definition) {
    let streamsDefs = ('function' === typeof definition) ?
        definition() :
        definition;

    if(('object' !== typeof streamsDefs) ||
        (streamsDefs === null) ||
        Array.isArray(streamsDefs)
    ) {
        throw new TypeError('Cycle Streams Group must be an object.');
    }

    if (this instanceof createGroup) { // jshint ignore:line
        throw new Error('Cannot use `new` operator on `createStreamsGroup()`, it is not a constructor.');
    }

    let streamsWithDeps = mapValues(streamsDefs, (streamFn) => ({
        dependencies: getParametersNames(streamFn),
        stream: createStream(streamFn)
    }));

    let group = mapValues(streamsWithDeps, ({ stream }) => stream);

    // add `inject` and `dispose` as not enumerable properties to make them
    // not visible for `each` function
    Object.defineProperty(group, 'inject', {
        enumerable: false,
        value: _makeInjectFn(streamsWithDeps)
    });

    Object.defineProperty(group, 'dispose', {
        enumerable: false,
        value: _makeDisposeFn(group)
    });

    Object.freeze(group);

    return group;
}


@@ -221,16 +221,147 @@
 mes';%0A%0A%0A
+function _removeUnderscores(string) %7B%0A    let result = /%5E_*(.*%5B%5E_%5D)_*$/.exec(string);%0A%0A    return result ? result%5B1%5D : string;%0A%7D%0A%0A%0A
 function
@@ -2285,16 +2285,53 @@
 treamFn)
+%0A            .map(_removeUnderscores)
 ,%0A      

remove underscores from beginning and end of function parameters names