// src/store/index.js
import Vue from 'vue'
// import Vuex from 'vuex'
// import logger from 'vuex/dist/logger'
// import VuexPersistence from 'vuex-persist'
import Vuex from '@/vuex'

class VuexPersistence {
  constructor(storage) {
    this.storage = storage.storage
    this.localName = 'local'
  }
  plugin = (store) => {
    const localState = JSON.parse(this.storage.getItem(this.localName))
    localState && store.replaceState(localState)
    store.subscribe((mutation, state) => {
      this.storage.setItem(this.localName, JSON.stringify(state))
    })
  }
}

const logger = () => (store) => {
  let prevState = JSON.stringify(store.state)
  store.subscribe((mutation, state) => { // 监听变化,每次数据变化都会执行此方法
    console.log('prev', prevState)
    console.log(mutation)
    prevState = JSON.stringify(state)
    console.log('next', prevState)
  })
}

const vuexLocal = new VuexPersistence({
  storage: window.localStorage
})

// 会默认调用 Vuex 的 install 方法
// Vue.use方法的简单实现
// Vue.use = function(plugin, options) {
//   plugin.install(Vue, options)
// }
Vue.use(Vuex)

// Vuex是一个对象,里面有一个大写的Store和install方法
export default new Vuex.Store({
  strict: true,
  plugins:[
    vuexLocal.plugin,
    logger()
  ],
  state: {
    count: 1,
    // 注意,如果state中的属性和模块同名,后面的会覆盖前面的,导致无法取到这个值
    a: 'a'
  },
  getters: {
    getCount(state) {
      return state.count + 1
    }
  },
  mutations: {
    changeCount(state, payload) {
      // state.count += payload 
      // 设置 strict: true 后不能在 mutations 中异步修改 state
      setTimeout(() => {
        state.count += payload
      }, 1000);
    }
  },
  actions: {
    changeCount({ commit }, payload) {
      setTimeout(() => {
        commit('changeCount', payload)
      }, 1000)
    }
  },
  modules: {
    a: {
      namespaced: true,
      state: {
        name: 'f',
        count: 18
      },
      mutations: {
        changeCount(state, payload) {
          state.count += payload 
        }
      },
      modules: {
        c: {
          namespaced: true,
          state: {
            name: 'c',
            count: 16
          },
          mutations: {
            changeCount(state, payload) {
              state.count += payload 
            }
          }
        }
      }
    },
    b: {
      namespaced: true,
      state: {
        name: 'g',
        count: 17
      },
      mutations: {
        changeCount(state, payload) {
          state.count += payload 
        }
      }
    }
  }
})
























 
 
 
 


































 
 
 
 
 
 











 
 
 
 
 
 
 
 



















 
 
 





 
 
 












// src/vuex/store.js
import { applyMixin } from './install'
import ModuleCollection from './module/module-collection'
import { forEachValue } from './utils'

export let Vue
// 动态获取最新的state
const getState = (store, path) => { // store.state 获取最新状态
  return path.reduce((rootState, current) => {
    return rootState[current]
  }, store.state)
}
const installModule = (store, path, module, rootState) => {  
  let namespaced = store._modules.getNamespace(path)
  console.log('namespaced', namespaced)

  // 将子模块的状态定义到根模块
  if (path.length > 0) {
    const parent = path.slice(0, -1).reduce((memo, current) => {
      return memo[current]
    }, rootState)
    // 这样添加的状态不是响应式的
    // parent[path[path.length - 1]] = module.state

    store._withCommiting(() => {
      // 新增不存在的属性属性使用set,将其设置为响应式的
      Vue.set(parent, path[path.length - 1], module.state)
    })
  }

  module.forEachMutation((mutation, key) => {
    store.mutations[namespaced + key] = store.mutations[namespaced + key] || []
    store.mutations[namespaced + key].push((payload) => mutation.call(store, getState(store, path), payload))
  })
  module.forEachAction((action, key) => {
    store.actions[namespaced + key] = store.actions[namespaced + key] || []
    store.actions[namespaced + key].push((payload) => action.call(store, store, payload))
  })
  module.forEachChildren((childModule, key) => {
    installModule(store, path.concat(key), childModule, rootState)
  })
  module.forEachGetters((gettersFn, key) => {
    store.wrapGetters[namespaced + key] = () => {
      return gettersFn.call(store, getState(store, path))
    }
  })
}

function resetStoreVM(store, state) {
  const computed = {}
  forEachValue(store.wrapGetters, (fn, key) => {
    computed[key] = fn
    Object.defineProperty(store.getters, key, {
      get: () => store._vm[key]
    })
  })
  store._vm = new Vue({
    data: {
      $$state: state
    },
    computed
  })
  if (store.strict) {
    store._vm.$watch(() => store._vm._data.$$state, () => {
      // 如果断言为false,则将一个错误消息写入控制台。如果断言是 true,没有任何反应
      console.assert(store._commiting, '在mutation之外修改了状态')
    }, { sync: true, deep: true }) // watcher执行是异步的,同步执行需要配置sync属性
  }
}

export class Store {
  constructor(options) {
    // 可能用户会有嵌套的module
    this._modules = new ModuleCollection(options)
    console.log('ddd', this._modules)
    this.mutations = {} // 将用户所有模块的mutation都放到这个对象中
    this.actions = {} // 将用户所有模块的action放到这个对象中
    this.getters = {}
    this._subscribes = []
    this.strict = options.strict
    this._commiting = false
    this._withCommiting = function(fn) {
      let commiting = this.commiting
      this._commiting = true
      fn() // 修改状态的逻辑
      this._commiting = commiting
    }
    
    this.wrapGetters = {} // 用于临时存放所有的getters
    const state = options.state // 获取用户状态

    // 将用户所有模块的 mutation、action、state、getters 都放到对象中
    installModule(this, [], this._modules.root, state)
    resetStoreVM(this, state)
    console.log('this.state', this.state)
    console.log('this.getters', this.getters)
    console.log('this.mutations', this.mutations)
    console.log('this.actions', this.actions)

    // 逐个执行plugin并传入store,默认插件就会执行
    options.plugins.forEach(fn => fn(this))
  }
  subscribe(fn) {
    this._subscribes.push(fn)
  }
  replaceState(newState) {
    this._withCommiting(() => {
      this._vm._data.$$state = newState
    })
  }
  get state() {
    return this._vm._data.$$state
  }
  commit = (type, payload) => {
    this._withCommiting(() => {
      this.mutations[type] && this.mutations[type].forEach(fn => fn(payload)) // commit后,mutation执行完毕,状态就更新了
    })
    this._subscribes.forEach(fn => fn({ type, payload }, this.state))
  }
  dispatch = (type, payload) => {
    this.actions[type] && this.actions[type].forEach(fn => fn(payload))
  }
}

export const install = (_Vue) => {
  Vue = _Vue
  applyMixin(Vue)
}