import isObservable from '../internal/isObservable'


/**
 * Extracts the properties that are observables
 * @param {state} stateOrProps
 * @returns {Array} of observables
 * @private
 */
export function observables(stateOrProps) {

  return Object.keys(stateOrProps)
      .filter(prop => isObservable(stateOrProps[prop]))
      .map(prop => ({name: prop, observable: stateOrProps[prop]})) || []
}

/**
 * Extracts the properties that are not observables
 * @param {state} stateOrProps
 * @returns {Map<string,*>} map of non-observables
 * @private
 */
export function nonObservables(stateOrProps) {

  return Object.keys(stateOrProps)
    .filter(prop => !isObservable(stateOrProps[prop]) && prop !== 'children')
    .reduce((total, prop) => Object.assign(total, {[prop]: stateOrProps[prop]}), {})
}

export function setupNonObservables(component, stateOrProps, setState = true) {

  component._nonObservables = nonObservables(stateOrProps)

  setState && component.setState(component._nonObservables)
}

/**
 * a callback maps a "val" to the obj property on the Component state
 *
 * @param {React.Component} component
 * @param {*} prop
 * @returns {Function} callback
 * @private
 */
const _callback = (component, prop) => val => component.setState({[prop.name]: val})
const _subscribe = (observableList, callbacks) =>
  observableList.forEach((obj, i) => obj.observable.onValue(callbacks[i]))
const _pluck = (obj, keys) =>
  keys.reduce((culledObj, key) => Object.assign(culledObj, {[key]: obj[key]}), {})


function _setupObservableState(component, observableList, initialState) {

  const callbacks = observableList.map(prop => _callback(component, prop))

set default state first before setting up listeners

  component.setState(
    _pluck(initialState, observableList.map(x => x.name)), // set observables only
    () => _subscribe(observableList, callbacks)
  )

  return callbacks
}

export function setupObservables(component, stateOrProps, initialState) {

  initialState = initialState || component.state || {}

  component._observables = observables(stateOrProps)
  component._callbacks = _setupObservableState(component, component._observables, initialState)
}

export function removeObservables(component) {

  const observables = component._observables
  const callbacks = component._callbacks

  observables.forEach((obj, i) => obj.observable.offValue(callbacks[i]))
}
h