import Vue from 'vue'

import {
  VD_SYNC,
  PAGE_CREATE,
  MOUNTED_DATA,
  UPDATED_DATA,
  PAGE_CREATED,
  VD_SYNC_CALLBACK
} from '../../../constants'

import {
  ON_PAGE_CREATE
} from '../../constants'

import {
  VDomSync
} from './vdom-sync'

import {
  setCurrentPage
} from '../page'

import {
  getPageVueComponent
} from '../../../page-factory'

export let vd

let PageVueComponent

const handleData = {
  [PAGE_CREATE]: function onPageCreate (data) {
    const [pageId, pagePath, pageOptions] = data
    document.title = `${pagePath}[${pageId}]`

    // 设置当前页面伪对象，方便其他地方使用 getCurrentPages 获取当前页面 id，route
    setCurrentPage(pageId, pagePath)
    // 通知页面创建，根据当前页面配置信息，初始化部分事件
    UniViewJSBridge.subscribeHandler(ON_PAGE_CREATE, pageOptions, pageId)
    // 初始化当前页面 VueComponent（生成页面样式代码）
    PageVueComponent = getPageVueComponent(pagePath)
    // 生成当前页面 vd
    vd = new VDomSync(pageId, {
      version: pageOptions.version
    })
  },
  [MOUNTED_DATA]: function onMounted (data) {
    vd.addVData.apply(vd, data)
  },
  [UPDATED_DATA]: function onUpdated (data) {
    vd.updateVData.apply(vd, data)
  },
  [PAGE_CREATED]: function onPageCreated (data) {
    const [pageId, pagePath, pageQuery] = data
    const page = getCurrentPages()[0]
    page.options = pageQuery || {}
    page.$vm = new PageVueComponent({
      mpType: 'page',
      pageId,
      pagePath,
      pageQuery
    }).$mount('#app')
  }
}

function broadcast (vm, componentName, eventName, ...params) {
  vm.$children.forEach(child => {
    const name = child.$options.name && child.$options.name.replace(/^VUni/, '')
    if (~componentName.indexOf(name)) {
      child.$emit(eventName, ...params)
    }
    broadcast(child, componentName, eventName, ...params)
  })
}

const NATIVE_COMPONENTS = ['Camera', 'LivePlayer', 'LivePusher', 'Map', 'Video', 'CoverView', 'CoverImage', 'Ad']

function updateView () {
  const pages = getCurrentPages()
  const pageVm = pages[0] && pages[0].$vm
  pageVm && broadcast(
    pageVm,
    NATIVE_COMPONENTS,
    'uni-view-update'
  )
}

window.addEventListener('resize', () => {
  updateView()
})

window.addEventListener('updateview', updateView)

function vdSync ({
  data,
  options
}) {
  let isVdCallback = true
  data.forEach(data => {
    if (data[0] === PAGE_CREATE) { // 页面创建无需触发 callback
      isVdCallback = false
    }
    handleData[data[0]](data[1])
  })
  vd.flush()
  Vue.nextTick(() => {
    // 清空本次 addBatchData
    vd.clearAddBatchVData()
    updateView()
    isVdCallback && UniViewJSBridge.publishHandler(VD_SYNC_CALLBACK)
  })
}

function getData (id, name) {
  try {
    const data = this.$r[id][name]
    if (name === 'is' && typeof data === 'object') {
      const components = this.$options.components || {}
      for (const key in components) {
        const value = components[key]
        const options = typeof value === 'function' ? value.options : value
        if (options.__file === data.__file) {
          return options
        }
      }
    }
    return data
  } catch (e) {
    // console.error(this.$options.__file + `:[${this._$id}]$r[${id}][${name}] is undefined`)
  }
}
/**
 * wxs change:prop
 * @param {Object} id
 * @param {Object} name
 */
function getChangeData (id, name) {
  try {
    const value = this.$r[id][name]
    const wxsPropName = name.replace('change:', '')
    this[wxsPropName] = value
    this.$set(this.wxsProps, wxsPropName, value)
    return value
  } catch (e) {
    // console.error(this.$options.__file + `:[${this._$id}]$r[${id}][${name}] is undefined`)
  }
}

export function initData (Vue) {
  Vue.prototype._$g = getData
  Vue.prototype._$gc = getChangeData

  UniViewJSBridge.subscribe(VD_SYNC, vdSync)

  Object.defineProperty(Vue.prototype, '_$vd', {
    get () {
      return !this.$options.isReserved && vd
    }
  })

  Vue.mixin({
    beforeCreate () {
      if (this.$options.mpType) {
        this.mpType = this.$options.mpType
      }
      if (this._$vd) {
        this._$vd.initVm(this)
      }
    }
  })
}
