import invariant from 'invariant';
import React, { Component, PropTypes } from 'react';
import { connect, Provider } from 'react-redux';

export function isolateDispatch(isolateState: Function, isolateAction: Function) {
	invariant(typeof(isolateState) !== 'function',
		'The first argument of isolateDispatch must be a function that selects an isolated subtree'
	);
	invariant(typeof(isolateAction) !== 'function',
		'The second argument of isolateDispatch must be a function that creates a wrapping action for the isolation reducer'
	);

	return (dispatch: Function, ownProps: ?Object) => {
		invariant(typeof(dispatch) !== 'function',
			'dispatch must be a function. Are you trying to execute a thunk without redux-thunk?'
		);
		const isolatedDispatch = action => {
			if(typeof(action) === 'function') {
				return dispatch((realDispatch, realGetState, ...extra) => {
					return action(isolatedDispatch, () => isolateState(realGetState(), ownProps), ...extra);
				});
			} else {
				return dispatch(isolateAction(action, ownProps));
			}
		};
		return isolatedDispatch;
	};
}

export function isolate(isolateState: Function, isolateAction: Function) {
	invariant(typeof(isolateState) !== 'function',
		'The first argument of isolate must be a function that selects an isolated subtree'
	);
	invariant(typeof(isolateAction) !== 'function',
		'The second argument of isolate must be a function that creates a wrapping action for the isolation reducer'
	);

	const createDispatch = isolateDispatch(isolateState, isolateAction);
	return ComponentToIsolate => {
		class IsolatedComponent extends Component {
			static propTypes = {
				state: PropTypes.object.isRequired,
				dispatch: PropTypes.func.isRequired
			};

			constructor(props) {
				super(props);

				const { state, dispatch, ...ownProps } = props;

				this.listeners = [];
				this.lastState = state;
				this.currentIsolatedState = isolateState(state, ownProps);
				this.currentisolatedDispatch = createDispatch(dispatch, ownProps);

				const getIsolatedState = () => this.currentIsolatedState;
				const isolatedDispatch = (...args) => this.currentisolatedDispatch(...args);

				const localSubscribe = (listener: Function): Function => {
					let isSubscribed = true;
					const listeners = this.listeners;
					listeners.push(listener);
					return function unsubscribe() {
						if (!isSubscribed) {
							return;
						}
						isSubscribed = false;
						const index = listeners.indexOf(listener);
						listeners.splice(index, 1);
					};
				};

				// Our store proxy - matches a redux store minus
				// replaceReducers, which we will not need anyway
				this.proxyStore = {
					getState: getIsolatedState,
					dispatch: isolatedDispatch,
					subscribe: localSubscribe
				};
			}

			componentWillReceiveProps(props) {
				if(props.state !== this.lastState) {
					const { state, dispatch, ...ownProps } = props;
					this.lastState = props.state;
					this.currentIsolatedState = isolateState(state, ownProps);
					this.currentisolatedDispatch = createDispatch(dispatch, ownProps);
					this.updateListeners = true;
				}
			}

			componentDidUpdate() {
				if(this.updateListeners) {
					this.listeners.map(l => l());
					this.updateListeners = false;
				}
			}

			render() {
				const { state: _state, dispatch: _dispatch, ...ownProps } = this.props;
				return (
					<Provider store={this.proxyStore}>
						<ComponentToIsolate {...ownProps} />
					</Provider>
				);
			}
		}

		const displayName = ComponentToIsolate.displayName || ComponentToIsolate.name || 'Component';
		IsolatedComponent.displayName = `IsolatedComponent(${displayName})`;
		return connect(state => ({ state }))(IsolatedComponent);
	};
}

@@ -239,33 +239,33 @@
 f(isolateState) 
-!
+=
 == 'function',%0A%09
@@ -387,33 +387,33 @@
 (isolateAction) 
-!
+=
 == 'function',%0A%09
@@ -620,17 +620,17 @@
 spatch) 
-!
+=
 == 'func
@@ -644,91 +644,119 @@
 %09%09%09'
-dispatch must be a function. Are you trying to execute a thunk without redux-thunk?
+the isolated dispatch creator must be called with the dispatch function to create an isolated dispatch function
 '%0A%09%09
@@ -900,24 +900,230 @@
 xtra) =%3E %7B%0D%0A
+%09%09%09%09%09invariant(typeof(realDispatch) === 'function' && typeof(realGetState) === 'function',%0D%0A%09%09%09%09%09%09'dispatch and getState must be functions. Are you trying to execute a thunk without redux-thunk?'%0D%0A%09%09%09%09%09);%0D%0A
 %09%09%09%09%09return 
@@ -1439,17 +1439,17 @@
 eState) 
-!
+=
 == 'func
@@ -1581,17 +1581,17 @@
 Action) 
-!
+=
 == 'func

Fix inverted invariant logic and some invariant messages