import React, { PropTypes, Component } from 'react';
import { withRouter as withRouterLegacy, Link as LinkLegacy, NavLink as NavLinkLegacy } from 'react-router-dom';
export { Route, Switch, Redirect } from 'react-router-dom';
import createHistory from 'history/createBrowserHistory';
import { Router } from 'react-router';
import { connect } from 'react-redux';
import { CALL_HISTORY_METHOD } from 'react-router-redux/actions';

export const routerQueryMiddleware = () => {
  return function (next) {
    return function (action) {
      if (action.type !== CALL_HISTORY_METHOD) {
        return next(action);
      }
      console.log(action.payload);
      return next();
    };
  };
}

export const withRouter = (WrappedComponent) => {
  @connect(
    ({ router }) => ({ location: router.location })
  )
  class WithRouter extends Component {
    static contextTypes = {
      router: PropTypes.shape({
        history: PropTypes.shape({
          push: PropTypes.func.isRequired,
          replace: PropTypes.func.isRequired,
          createHref: PropTypes.func.isRequired
        }).isRequired
      }).isRequired,
    };
    push = (propsTo) => {
      const to = { ...propsTo };
      if (to.query) {
        to.search = stringifyQuery(to.query);
        delete to.query;
      }
      this.context.router.history.push(to);
    }
    replace = (propsTo) => {
      const to = { ...propsTo };
      if (to.query) {
        to.search = stringifyQuery(to.query);
        delete to.query;
      }
      this.context.router.history.replace(to);
    }
    render() {
      return (
        <WrappedComponent {...this.props} router={{ ...this.context.router, push: this.push, replace: this.replace }} />
      );
    }
  }
  return WithRouter;
};

export const Link = (props) => {
  if (props.to && props.to.query) {
    props.to.search = stringifyQuery(props.to.query);
    delete props.to.query;
  }
  return <LinkLegacy {...props} />;
};

export const NavLink = (props) => {
  if (props.to && props.to.query) {
    props.to.search = stringifyQuery(props.to.query);
    delete props.to.query;
  }
  return <NavLinkLegacy {...props} />;
};

function memoize(func) {
  const cache = {};
  return function() {
    const key = JSON.stringify(arguments);
    if (cache[key]) {
      return cache[key];
    }
    const val = func.apply(this, arguments);
    cache[key] = val;
    return val;
  };
}

export const parseQuery = memoize((str) => {
  const ret = Object.create(null);
  if (typeof str !== 'string') return ret;
  str = str.trim().replace(/^(\?|#|&)/, '');
  if (!str) return ret;
  str.split('&').forEach((param) => {
    const parts = param.replace(/\+/g, ' ').split('=');
    const key = parts.shift();
    let val = parts.length > 0 ? parts.join('=') : undefined;
    // key = decodeURIComponent(key);
    val = val === undefined ? null : decodeURIComponent(val);
    if (ret[key] === undefined) {
      ret[key] = val;
    } else if (Array.isArray(ret[key])) {
      ret[key].push(val);
    } else {
      ret[key] = [ret[key], val];
    }
  });
  return ret;
});

export const stringifyQuery = memoize((obj) => {
  return obj ? Object.keys(obj).sort().map((key) => {
    const val = obj[key];
    if (val === undefined) {
      return '';
    }
    if (val === null) {
      return key;
      // return encodeURIComponent(key);
    }
    if (Array.isArray(val)) {
      const result = [];

      val.slice().forEach((val2) => {
        if (val2 === undefined) {
          return;
        }

        if (val2 === null) {
          result.push(key);
          // result.push(encodeURIComponent(key));
        } else {
          result.push(`${key}=${encodeURIComponent(val2)}`);
          // result.push(encodeURIComponent(key) + '=' + encodeURIComponent(val2));
        }
      });

      return result.join('&');
    }
    return `${key}=${encodeURIComponent(val)}`;
    // return encodeURIComponent(key, opts) + '=' + encodeURIComponent(val, opts);
  }).filter((x) => {
    return x.length > 0;
  }).join('&') : '';
});

@@ -364,35 +364,31 @@
 mport %7B 
-CALL_HISTORY_METHOD
+LOCATION_CHANGE
  %7D from 
@@ -459,177 +459,274 @@
 e = 
-() =%3E %7B%0A  return function (next) %7B%0A    return function (action) %7B%0A      if (action.type !== CALL_HISTORY_METHOD) %7B%0A        return next(action);%0A      %7D%0A      console.log
+store =%3E next =%3E action =%3E  %7B%0A  console.log(action.type, action.type === LOCATION_CHANGE, LOCATION_CHANGE, action.payload);%0A  if (action.type !== LOCATION_CHANGE) %7B%0A    return next(action);%0A  %7D%0A  if (action.payload.query) %7B%0A    action.payload.search = stringifyQuery
 (act
@@ -740,45 +740,79 @@
 load
+.query
 );%0A    
-  return next();%0A    %7D;%0A  %7D;%0A%7D
+delete action.payload.query;%0A  %7D%0A  return next(action);%0A%7D;
 %0A%0Aex
@@ -895,39 +895,150 @@
  =%3E 
-(%7B location: router.location %7D)
+%7B%0A      return %7B%0A        location: %7B%0A          ...router.location,%0A          query: parseQuery(router.location.search)%0A        %7D%0A      %7D%0A    %7D
 %0A  )

fix small bug