import element from 'virtual-element';
import { bindActionCreators } from 'redux';
import shallowEquals  from 'is-equal-shallow';
import isPlainObject from 'is-plain-object';
import invariant from 'invariant';

const defaultMapStateToProps = () => ({});
const defaultMapDispatchToProps = dispatch => ({ dispatch });
const defaultMergeProps =  (stateProps, dispatchProps, parentProps) => ({
    ...parentProps,
    ...stateProps,
    ...dispatchProps
});

const wrapActionCreators = actionCreators => dispatch => bindActionCreators(actionCreators, dispatch);


function connect(mapStateToProps = defaultMapStateToProps, mapDispatchToProps = defaultMapDispatchToProps, mergeProps = defaultMergeProps) {
    if(isPlainObject(mapDispatchToProps)) {
        mapDispatchToProps = wrapActionCreators(mapDispatchToProps);
    }

    const mappedStateUseProps = mapStateToProps.length > 1;
    const mappedDispatchUseProps = mapDispatchToProps.length > 1;

    const computeStateProps = props => {
        const state = props.store.getState();
        const stateProps = mapStateToProps(state, props);

        invariant(isPlainObject(stateProps), '[deku-redux][connect] mapStateToProps must return an object. Instead received %s', stateProps);

        return stateProps;
    };

    const computeDispatchProps = props => {
        const dispatch = props.store.dispatch;
        const dispatchProps = mapDispatchToProps(dispatch, props);

        invariant(isPlainObject(dispatchProps), '[deku-redux][connect] mapDispatchToProps must return an object. Instead received %s', dispatchProps);

        return dispatchProps;
    };

    return function connectWrapper(Component) {
        const connectRegistry = {};
        const getStateProps = id => connectRegistry[id] ? connectRegistry[id].stateProps : null;
        const setStateProps = (id, stateProps) => connectRegistry[id].stateProps = stateProps;
        const getDispatchProps = id => connectRegistry[id] ? connectRegistry[id].dispatchProps : null;
        const setDispatchProps = (id, dispatchProps) => connectRegistry[id].dispatchProps = dispatchProps;

        const ConnectedComponent = {
            propTypes: {
                store: { source: 'store' },
                storeState: { source: 'storeState' }
            },

            beforeMount({ id, props }, elm, setState) {
                invariant(props.store, '[deku-redux][connect] Could not find store. Did you use `storePlugin` on your deku tree?');
                invariant(
                  props.store.getState && props.store.subscribe && props.store.subscribe,
                  '[deku-redux][connect] Could not recognise store. Did you use `storePlugin` with a valid redux store?'
                );

                connectRegistry[id] = {};
                setStateProps(id, computeStateProps(props));
                setDispatchProps(id, computeDispatchProps(props));
            },

            shouldUpdate({ id, props }, nextProps) {
                const storeChanged = nextProps.storeState !== props.storeState;
                const propsChanged = !shallowEquals(nextProps, props);

                const computeNewStateProps = storeChanged || (propsChanged && mappedStateUseProps);
                const computeNewDispatchProps = propsChanged && mappedDispatchUseProps;

                let statePropsChanged = false;
                let dispatchPropsChanged = false;

                if (computeNewStateProps) {
                    const stateProps = computeStateProps(props);
                    statePropsChanged = !shallowEquals(stateProps, getStateProps(id));
                    if (statePropsChanged) {
                        setStateProps(id, stateProps);
                    }
                }

                if (computeNewDispatchProps) {
                    const dispatchProps = computeDispatchProps(props);
                    dispatchPropsChanged = !shallowEquals(dispatchProps, getDispatchProps(id));
                    if (dispatchPropsChanged) {
                        setDispatchProps(id, stateProps);
                    }
                }

                return propsChanged || statePropsChanged || dispatchPropsChanged;
            },

            beforeUnmount({ id }) {
                connectRegistry[id] = undefined;
            },

            render({ id, props }) {
                // TODO: use _.omit or similar
                const parentProps = Object.keys(props)
                    .filter(prop => prop !== 'store' || prop !== 'storeState')
                    .reduce((acc, prop) => {
                        acc[prop] = props[prop];
                        return acc;
                    }, {});

                const componentProps = mergeProps(parentProps, getStateProps(id), getDispatchProps(id));

                invariant(isPlainObject(componentProps), '[deku-redux][connect] `mergeProps` function didn\'t return a plain object.');

                return element(Component, componentProps);
            }
        };

        return ConnectedComponent;
    };
}

export default connect;

@@ -1,43 +1,4 @@
-import element from 'virtual-element';%0A
 impo
@@ -4897,33 +4897,133 @@
     
-return element(Component,
+// %3CComponent %7B...componentProps%7D /%3E%0A                return %7B type: Component, children: componentProps.children, attributes:
  com
@@ -5029,25 +5029,26 @@
 mponentProps
-)
+ %7D
 ;%0A          

Remove dependency on virtual-element