/* global ReactRedux, Redux, ReactDOM */

// "Getting Started with Redux" (by Dan Abramov)
// https://egghead.io/series/getting-started-with-redux

// This file on JSBin (by Jesse Buchanan):
// http://jsbin.com/wuwezo/74/edit?js,console,output

////////////////////////////////////////////////
//
// Imports
//

const { connect, Provider } = ReactRedux;
const { combineReducers, createStore } = Redux;

////////////////////////////////////////////////
//
// Reducers (except root, see bottom)
//

// 2nd level reducer
// Here, `state` refers to the array of todo objects
const todos = (state = [], action) => {
  console.log("todos reducer called");
  switch (action.type) {
    // Returns a new array of todos, containing the added todo as described by `action`.
    // The new todo is constructed by delegating to the `todo` reducer.
    case 'ADD_TODO':
      console.log('todos ADD_TODO');
      // ... below is ES6 "spread operator" (arrays only)
      return [
        ...state,
        todo(undefined, action)
      ];
    // Returns a new array of todos, with an individual todo's completed status
    // toggled as identified by `action.id`.
    // Must operate on entire list (seems wrong somehow).
    case 'TOGGLE_TODO':
      console.log('todos TOGGLE_TODO');
      return state.map(t =>
        todo(t, action)
      );
    default:
      return state;
  }
};

// 2nd level reducer
// Here, `state` refers to a simple configuration string (enum/atom)
// Remember, we are only returning the state we are concerned with
const visibilityFilter = (state = 'SHOW_ALL', action) => {
  console.log("visibilityFilter reducer called");
  switch (action.type) {
    case 'SET_VISIBILITY_FILTER':
      return action.filter;
    default:
      return state;
  }
};

// 3rd level reducer.
// Here, `state` refers to a single todo object.
const todo = (state, action) => {
  console.log("todo reducer called");
  // Remember, no mutation.
  // Initial state is considered (or not, in the ADD_TODO case),
  // and used to construct a new state object, always.

  switch (action.type) {
    case 'ADD_TODO':
      console.log('todo ADD_TODO');
      return {
        id: action.id,
        text: action.text,
        completed: false
      };
    case 'TOGGLE_TODO':
      console.log('todo TOGGLE_TODO');
      // Bail out if the action is for a different todo than the one passed in.
      if (state.id !== action.id) {
        return state;
      }

      // ... below is ES7 "Object Rest Destructuring"
      // https://github.com/sebmarkbage/ecmascript-rest-spread/blob/master/Spread.md
      // Think of it as "and the rest", or "override these object properties"
      return {
        ...state,
        completed: !state.completed
      };
    default:
      return state;
  }
};

////////////////////////////////////////////////
//
// React components
// (not including generated or root component/s)
//

// This is a "stateless component". It gets `props` passed in.
// https://facebook.github.io/react/blog/2015/09/10/react-v0.14-rc1.html#stateless-function-components
// Notice the curlybraces in the parameter list. This is destructuring
// `props` (e.g. filter = props.filter, children = props.children)
const TodoList = ({ todos, onTodoClick }) => (
  <ul>
    {todos.map(todo =>
      <Todo
        key={todo.id}
        {...todo}
        onClick={() => onTodoClick(todo.id)}
      />
    )}
  </ul>
);


const Todo = ({ onClick, completed, text }) => {
  return (
    <li onClick={onClick}
      style={{textDecoration: completed ? 'line-through' : 'none'}}
    >
      {text}
    </li>
  );
};

const Link = ({ active, children, onClick }) => {
  if (active)
    return <span>{children}</span>;
  return (
    <a href='#'
       onClick={e => {
         e.preventDefault();
         onClick();
       }}
    >
      {children}
    </a>
  );
};

const Footer = () => {
  return (
    <p>
      Show:
      {' '}
      <FilterLink
        filter='SHOW_ALL'>All</FilterLink>
      {' '}
      <FilterLink
        filter='SHOW_ACTIVE'>Active</FilterLink>
      {' '}
      <FilterLink filter='SHOW_COMPLETED'>Completed</FilterLink>
    </p>
  );
};

// Intentionally not `const` here.
// We will override AddTodo later with a connected component.
let AddTodo = ({ dispatch }) => {
  let input;

  return (
    <div>
      <input ref={node => {
        // This is a newer 0.14 syntax where ref can be a callback.

        // n.b. no `this` available in a functional/stateless component.
        // Here, we're making a closure over `input`, defined above.
        input = node;
      }} />
      <button onClick={() => {
        dispatch(addTodo(input.value));
        input.value = '';
      }}>
        Add todo
      </button>
    </div>
  );
};

////////////////////////////////////////////////
//
// react-redux mapping functions
//

// This takes Redux state, and returns the props needed
// for the presentational component.
const mapStateToTodoListProps = (state) => {
  console.log(`mapStateToTodoListProps(): state.todos = ${state.todos}`);
  return {
    todos: getVisibleTodos(
      state.todos,
      state.visibilityFilter
    )
  };
};

// This takes the store's dispatch method, and returns the
// callbacks props needed for the presentational component.
const mapDispatchToTodoListProps = (dispatch) => {
  return {
    onTodoClick: (id) => {
      dispatch(toggleTodo(id));
    }
  };
};

const mapStateToFilterLinkProps = (state, ownProps) => {
  return {
    active: ownProps.filter === state.visibilityFilter
  };
};

const mapDispatchToFilterLinkProps = (dispatch, ownProps) => {
  return {
    onClick: () => {
      console.log('FilterLink onClick');
      dispatch(setVisibilityFilter(ownProps.filter));
    }
  };
};

////////////////////////////////////////////////
//
// react-redux container components generated by `connect`
//


// To create a container component, we use react-redux `connect`.
// We pass the mapStateToProps and mapDispatchToProps functions
// defined above.
//
// `connect` returns a curried function, which you call on the
// presentational component. *This* function returns a "connected"
// or "container" component.
//
// What is a connected component anyway?
//
// In the mapStateToProps function, we pluck any necessary state
// from the Redux store, and react-redux wires it to the React props.
//
// In the mapDispatchToProps function, we accept a callback to store.dispatch.
// We return an object, whose keys are callback attributes (`onClick` etc)
// and whose values are functions that actually perform dispatch.
//
// This allows us to take a stateless, dumb, presentation only component,
// and transform it into a component that knows how to get state from the store,
// and knows how to dispatch events to the store.
//
// The egghead redux video tutorials walk through how to implement `connect`
// by hand.

const VisibleTodoList = connect(
  mapStateToTodoListProps, mapDispatchToTodoListProps
)(TodoList);

const FilterLink = connect(
  mapStateToFilterLinkProps,
  mapDispatchToFilterLinkProps
)(Link);

// Default behavior of `connect` (with 0 parameters):
//   Do not subscribe to any stores, inject `dispatch` as prop.
// This shorthand is equivalent to AddTodoHardWay, demonstrated below.
AddTodo = connect()(AddTodo);

// AddTodoTheHardWay = connect(
//   state => {
//     // AddToDo doesn't have any state to be mapped to props.
//     // Could have just specified null here instead of an anon fn.
//     return {};
//   },
//   dispatch => {
//     // A bit hard to follow here.
//     // The 2nd arg of `connect` is the mapDispatchToProps fn.
//     // This function takes a dispatch function, returns callbacks as needed.
//     // In other cases, we might return onSomethingClick callbacks here, calling
//     //  store.dispatch inside those callbacks as necessary.
//     // In this case, we're just taking dispatch, and returning
//     //  {dispatch: dispatch}
//
//     return { dispatch };
//   }
// )(AddTodo);


////////////////////////////////////////////////
//
// Redux action creators
//

// You could just litter the action object boilerplate all throughout
// the code. This is just DRY/shorthand...
//
// ...but it's actually more than that.
// (?) Closures mean I can keep state
// (?) Async operations are handled here!

let nextToDoId = 0; // global :shrug:
const addTodo = (text) => {
  return {
    type: 'ADD_TODO',
    id: nextToDoId++,
    text
  };
};

const setVisibilityFilter = (filter) => {
  return {
    type: 'SET_VISIBILITY_FILTER',
    filter
  };
};

const toggleTodo = (id) => {
  return {
    type: 'TOGGLE_TODO',
    id
  };
};

////////////////////////////////////////////////
//
// Helpers (just a filter, really)
//

const getVisibleTodos = (todos, filter) => {
  console.log(`getVisibleTodos: todos=${todos} filter=${filter}`);
  switch (filter) {
    case 'SHOW_ALL':
      return todos;
    case 'SHOW_COMPLETED':
      return todos.filter(t => t.completed);
    case 'SHOW_ACTIVE':
      return todos.filter(t => !t.completed);
  }
};


//////////////////////////////////////////////////
//
// Root reducer, root component, initial render
//


// Top level reducer.
const todoApp = combineReducers({
  // n.b. following syntax is ES6 Object Initializer (shorthand property names)
  todos,              // todos: todos,
  visibilityFilter    // visibilityFilter: visibilityFilter
});

// Create the Redux store from the root reducer.
const store = createStore(todoApp);

// Look at how simple the application is now!
// The top level React component needs no props.
const TodoApp = () => {
  return (
    <div>
      <AddTodo />
      <VisibleTodoList />
      <Footer />
    </div>
  );
};

// react-redux <Provider> uses the React Context feature (`getChildContext`,
// `childContextTypes`) to inject the store, automatically subscribe and
// unsubscribe to it at the correct React lifecycle hooks (e.g. `componentDidMount`).
//
// See how <Provider> works here:
// https://github.com/rackt/react-redux/blob/master/src/components/Provider.js

ReactDOM.render(
  <Provider store={store}>
    <TodoApp />
  </Provider>,
  document.getElementById('root')
);
