import XEUtils from 'xe-utils/methods/xe-utils'
import GlobalConfig from '../../conf'
import VXETable, { Buttons } from '../../v-x-e-table'
import { UtilTools, DomTools, GlobalEvent } from '../../tools'

export default {
  name: 'PlxToolbar',
  props: {
    id: String,
    loading: false,
    resizable: [Boolean, Object],
    refresh: [Boolean, Object],
    import: [Boolean, Object],
    export: [Boolean, Object],
    zoom: [Boolean, Object],
    setting: [Boolean, Object],
    custom: [Boolean, Object],
    buttons: { type: Array, default: () => GlobalConfig.toolbar.buttons },
    size: String
  },
  inject: {
    $grid: {
      default: null
    }
  },
  data () {
    return {
      $table: null,
      isRefresh: false,
      tableFullColumn: [],
      customStore: {
        isAll: false,
        isIndeterminate: false,
        visible: false
      }
    }
  },
  computed: {
    vSize () {
      return this.size || this.$parent.size || this.$parent.vSize
    },
    refreshOpts () {
      return Object.assign({}, GlobalConfig.toolbar.refresh, this.refresh)
    },
    importOpts () {
      return Object.assign({}, GlobalConfig.toolbar.import, this.import)
    },
    exportOpts () {
      return Object.assign({}, GlobalConfig.toolbar.export, this.export)
    },
    resizableOpts () {
      return Object.assign({ storageKey: 'VXE_TABLE_CUSTOM_COLUMN_WIDTH' }, GlobalConfig.toolbar.resizable, this.resizable)
    },
    zoomOpts () {
      return Object.assign({}, GlobalConfig.toolbar.zoom, this.zoom)
    },
    customOpts () {
      return Object.assign({ storageKey: 'VXE_TABLE_CUSTOM_COLUMN_HIDDEN' }, GlobalConfig.toolbar.custom || GlobalConfig.toolbar.setting, this.custom || this.setting)
    }
  },
  created () {
    let { customOpts, setting, id } = this
    if (customOpts.storage && !id) {
      return UtilTools.error('plx.error.toolbarId')
    }
    if (setting) {
      // UtilTools.warn('plx.error.delProp', ['setting', 'custom'])
    }
    if (!VXETable._export && (this.export || this.import)) {
      UtilTools.error('plx.error.reqModule', ['Export'])
    }
    this.$nextTick(() => {
      this.updateConf()
      this.loadStorage()
    })
    GlobalEvent.on(this, 'keydown', this.handleGlobalKeydownEvent)
    GlobalEvent.on(this, 'mousedown', this.handleGlobalMousedownEvent)
    GlobalEvent.on(this, 'blur', this.handleGlobalBlurEvent)
  },
  destroyed () {
    GlobalEvent.off(this, 'keydown')
    GlobalEvent.off(this, 'mousedown')
    GlobalEvent.off(this, 'blur')
  },
  render (h) {
    let { _e, $scopedSlots, $grid, $table, loading, customStore, importOpts, exportOpts, refresh, refreshOpts, zoom, zoomOpts, custom, setting, customOpts, buttons = [], vSize, tableFullColumn } = this
    let customBtnOns = {}
    let customWrapperOns = {}
    let $buttons = $scopedSlots.buttons
    let $tools = $scopedSlots.tools
    if (custom || setting) {
      if (customOpts.trigger === 'manual') {
        // 手动触发
      } else if (customOpts.trigger === 'hover') {
        // hover 触发
        customBtnOns.mouseenter = this.handleMouseenterSettingEvent
        customBtnOns.mouseleave = this.handleMouseleaveSettingEvent
        customWrapperOns.mouseenter = this.handleWrapperMouseenterEvent
        customWrapperOns.mouseleave = this.handleWrapperMouseleaveEvent
      } else {
        // 点击触发
        customBtnOns.click = this.handleClickSettingEvent
      }
    }
    return h('div', {
      class: ['plx-toolbar', {
        [`size--${vSize}`]: vSize,
        'is--loading': loading
      }]
    }, [
      h('div', {
        class: 'plx-button--wrapper'
      }, $buttons ? $buttons.call(this, { $grid, $table }, h) : buttons.map(item => {
        return item.visible === false ? _e() : h('plx-button', {
          on: {
            click: evnt => this.btnEvent(evnt, item)
          },
          props: {
            icon: item.icon,
            type: item.type,
            disabled: item.disabled,
            loading: item.loading
          },
          scopedSlots: item.dropdowns && item.dropdowns.length ? {
            default: () => UtilTools.getFuncText(item.name),
            dropdowns: () => item.dropdowns.map(child => {
              return child.visible === false ? _e() : h('plx-button', {
                on: {
                  click: evnt => this.btnEvent(evnt, child)
                },
                props: {
                  icon: child.icon,
                  type: child.type,
                  disabled: child.disabled,
                  loading: child.loading
                }
              }, UtilTools.getFuncText(child.name))
            })
          } : null
        }, UtilTools.getFuncText(item.name))
      })),
      $tools ? h('div', {
        class: 'plx-tools--wrapper'
      }, $tools.call(this, { $grid, $table }, h)) : null,
      h('div', {
        class: 'plx-tools--operate'
      }, [
        this.import ? h('div', {
          class: 'plx-tools--operate-btn',
          attrs: {
            title: GlobalConfig.i18n('plx.toolbar.import')
          },
          on: {
            click: this.importEvent
          }
        }, [
          h('i', {
            class: importOpts.icon || GlobalConfig.icon.import
          })
        ]) : null,
        this.export ? h('div', {
          class: 'plx-tools--operate-btn',
          attrs: {
            title: GlobalConfig.i18n('plx.toolbar.export')
          },
          on: {
            click: this.exportEvent
          }
        }, [
          h('i', {
            class: exportOpts.icon || GlobalConfig.icon.export
          })
        ]) : null,
        refresh ? h('div', {
          class: 'plx-tools--operate-btn',
          attrs: {
            title: GlobalConfig.i18n('plx.toolbar.refresh')
          },
          on: {
            click: this.refreshEvent
          }
        }, [
          h('i', {
            class: this.isRefresh ? (refreshOpts.iconLoading || GlobalConfig.icon.refreshLoading) : (refreshOpts.icon || GlobalConfig.icon.refresh)
          })
        ]) : null,
        zoom && $grid ? h('div', {
          class: 'plx-tools--operate-btn',
          attrs: {
            title: GlobalConfig.i18n(`plx.toolbar.zoom${$grid.isMaximized() ? 'Out' : 'In'}`)
          },
          on: {
            click: () => $grid.zoom()
          }
        }, [
          h('i', {
            class: $grid.isMaximized() ? (zoomOpts.iconOut || GlobalConfig.icon.zoomOut) : (zoomOpts.iconIn || GlobalConfig.icon.zoomIn)
          })
        ]) : null,
        custom || setting ? h('div', {
          class: ['plx-custom--wrapper', {
            'is--active': customStore.visible
          }],
          ref: 'customWrapper'
        }, [
          h('div', {
            class: 'plx-tools--operate-btn',
            attrs: {
              title: GlobalConfig.i18n('plx.toolbar.custom')
            },
            on: customBtnOns
          }, [
            h('i', {
              class: customOpts.icon || GlobalConfig.icon.custom
            })
          ]),
          h('div', {
            class: 'plx-custom--option-wrapper'
          }, [
            h('div', {
              class: 'plx-custom--header'
            }, [
              h('li', {
                class: {
                  'is--checked': customStore.isAll,
                  'is--indeterminate': customStore.isIndeterminate
                },
                attrs: {
                  title: GlobalConfig.i18n('plx.table.allTitle')
                },
                on: {
                  click: this.allCustomEvent
                }
              }, GlobalConfig.i18n('plx.toolbar.customAll'))
            ]),
            h('ul', {
              class: 'plx-custom--body',
              on: customWrapperOns
            }, tableFullColumn.map(column => {
              let headerTitle = column.getTitle()
              let isDisabled = customOpts.checkMethod ? !customOpts.checkMethod({ column }) : false
              return headerTitle ? h('li', {
                class: ['plx-custom--option', {
                  'is--checked': column.visible,
                  'is--disabled': isDisabled
                }],
                attrs: {
                  title: headerTitle
                },
                on: {
                  click: () => {
                    if (!isDisabled) {
                      column.visible = !column.visible
                      if ((custom || setting) && customOpts.immediate) {
                        this.handleCustoms()
                      }
                      this.checkCustomStatus()
                    }
                  }
                }
              }, headerTitle) : null
            })),
            customOpts.isFooter === false ? null : h('div', {
              class: 'plx-custom--footer'
            }, [
              h('button', {
                class: 'btn--confirm',
                on: {
                  click: this.confirmCustomEvent
                }
              }, GlobalConfig.i18n('plx.toolbar.customConfirm')),
              h('button', {
                class: 'btn--reset',
                on: {
                  click: this.resetCustomEvent
                }
              }, GlobalConfig.i18n('plx.toolbar.customReset'))
            ])
          ])
        ]) : null
      ])
    ])
  },
  methods: {
    updateConf () {
      let { $children } = this.$parent
      let selfIndex = $children.indexOf(this)
      this.$table = XEUtils.find($children, (comp, index) => comp && comp.refreshColumn && index > selfIndex && comp.$vnode.componentOptions.tag === 'plx-table')
    },
    openCustom () {
      this.customStore.visible = true
      this.checkCustomStatus()
    },
    closeCustom () {
      let { custom, setting, customStore } = this
      if (customStore.visible) {
        customStore.visible = false
        if ((custom || setting) && !customStore.immediate) {
          this.handleCustoms()
        }
      }
    },
    loadStorage () {
      let { $grid, $table, id, refresh, resizable, custom, setting, refreshOpts, resizableOpts, customOpts } = this
      if (refresh && !$grid) {
        if (!refreshOpts.query) {
          UtilTools.warn('plx.error.notFunc', ['query'])
        }
      }
      if ($grid || $table) {
        ($grid || $table).connect({ toolbar: this })
      } else {
        if (resizable || custom || setting) {
          throw new Error(UtilTools.getLog('plx.error.barUnableLink'))
        }
      }
      if (resizable || custom || setting) {
        let customMap = {}
        if (resizableOpts.storage) {
          let columnWidthStorage = this.getStorageMap(resizableOpts.storageKey)[id]
          if (columnWidthStorage) {
            XEUtils.each(columnWidthStorage, (resizeWidth, field) => {
              customMap[field] = { field, resizeWidth }
            })
          }
        }
        if (customOpts.storage) {
          let columnHideStorage = this.getStorageMap(customOpts.storageKey)[id]
          if (columnHideStorage) {
            columnHideStorage.split(',').forEach(field => {
              if (customMap[field]) {
                customMap[field].visible = false
              } else {
                customMap[field] = { field, visible: false }
              }
            })
          }
        }
        let customList = Object.values(customMap)
        this.updateCustoms(customList.length ? customList : this.tableFullColumn)
      }
    },
    updateColumn (fullColumn) {
      this.tableFullColumn = fullColumn
    },
    updateCustoms (customs) {
      let comp = this.$grid || this.$table
      if (comp) {
        comp.reloadCustoms(customs).then(fullColumn => {
          this.tableFullColumn = fullColumn
        })
      }
    },
    getStorageMap (key) {
      let version = GlobalConfig.version
      let rest = XEUtils.toStringJSON(localStorage.getItem(key))
      return rest && rest._v === version ? rest : { _v: version }
    },
    saveColumnHide () {
      let { id, tableFullColumn, customOpts } = this
      let { checkMethod, storage, storageKey } = customOpts
      if (storage) {
        let columnHideStorageMap = this.getStorageMap(storageKey)
        let colHides = tableFullColumn.filter(column => column.property && !column.visible && (!checkMethod || checkMethod({ column })))
        columnHideStorageMap[id] = colHides.length ? colHides.map(column => column.property).join(',') : undefined
        localStorage.setItem(storageKey, XEUtils.toJSONString(columnHideStorageMap))
      }
      return this.$nextTick()
    },
    saveColumnWidth (isReset) {
      let { id, tableFullColumn, resizableOpts } = this
      if (resizableOpts.storage) {
        let columnWidthStorageMap = this.getStorageMap(resizableOpts.storageKey)
        let columnWidthStorage
        if (!isReset) {
          columnWidthStorage = XEUtils.isPlainObject(columnWidthStorageMap[id]) ? columnWidthStorageMap[id] : {}
          tableFullColumn.forEach(({ property, resizeWidth, renderWidth }) => {
            if (property && resizeWidth) {
              columnWidthStorage[property] = renderWidth
            }
          })
        }
        columnWidthStorageMap[id] = XEUtils.isEmpty(columnWidthStorage) ? undefined : columnWidthStorage
        localStorage.setItem(resizableOpts.storageKey, XEUtils.toJSONString(columnWidthStorageMap))
      }
      return this.$nextTick()
    },
    hideColumn (column) {
      UtilTools.warn('plx.error.delFunc', ['hideColumn', 'table.hideColumn'])
      column.visible = false
      return this.handleCustoms()
    },
    showColumn (column) {
      UtilTools.warn('plx.error.delFunc', ['showColumn', 'table.showColumn'])
      column.visible = true
      return this.handleCustoms()
    },
    resetCustoms () {
      return this.handleCustoms()
    },
    resetResizable () {
      this.updateResizable(this)
    },
    confirmCustomEvent () {
      this.closeCustom()
    },
    resetCustomEvent () {
      let { checkMethod } = this.customOpts
      this.tableFullColumn.forEach(column => {
        if (!checkMethod || checkMethod({ column })) {
          column.visible = true
        }
        column.resizeWidth = 0
      })
      this.resetCustoms()
      this.resetResizable()
      this.closeCustom()
    },
    updateResizable (isReset) {
      let comp = this.$grid || this.$table
      this.saveColumnWidth(isReset)
      comp.analyColumnWidth()
      return comp.recalculate(true)
    },
    handleCustoms () {
      (this.$grid || this.$table).refreshColumn()
      return this.saveColumnHide()
    },
    checkCustomStatus () {
      let { checkMethod } = this.customOpts
      let tableFullColumn = this.tableFullColumn
      this.customStore.isAll = tableFullColumn.every(column => (checkMethod ? !checkMethod({ column }) : false) || column.visible)
      this.customStore.isIndeterminate = !this.customStore.isAll && tableFullColumn.some(column => (!checkMethod || checkMethod({ column })) && column.visible)
    },
    allCustomEvent () {
      let { checkMethod } = this.customOpts
      let isAll = !this.customStore.isAll
      this.tableFullColumn.forEach(column => {
        if (!checkMethod || checkMethod({ column })) {
          column.visible = isAll
        }
      })
      this.customStore.isAll = isAll
      this.checkCustomStatus()
    },
    handleGlobalKeydownEvent (evnt) {
      let isEsc = evnt.keyCode === 27
      if (isEsc && this.$grid && this.$grid.isMaximized() && this.zoomOpts && this.zoomOpts.escRestore !== false) {
        this.$grid.zoom()
      }
    },
    handleGlobalMousedownEvent (evnt) {
      if (!DomTools.getEventTargetNode(evnt, this.$refs.customWrapper).flag) {
        this.closeCustom()
      }
    },
    handleGlobalBlurEvent (evnt) {
      this.closeCustom()
    },
    handleClickSettingEvent (evnt) {
      this.customStore.visible = !this.customStore.visible
      this.checkCustomStatus()
    },
    handleMouseenterSettingEvent (evnt) {
      this.customStore.activeBtn = true
      this.openCustom()
    },
    handleMouseleaveSettingEvent (evnt) {
      let { customStore } = this
      customStore.activeBtn = false
      setTimeout(() => {
        if (!customStore.activeBtn && !customStore.activeWrapper) {
          this.closeCustom()
        }
      }, 300)
    },
    handleWrapperMouseenterEvent (evnt) {
      this.customStore.activeWrapper = true
      this.openCustom()
    },
    handleWrapperMouseleaveEvent (evnt) {
      let { customStore } = this
      customStore.activeWrapper = false
      setTimeout(() => {
        if (!customStore.activeBtn && !customStore.activeWrapper) {
          this.closeCustom()
        }
      }, 300)
    },
    refreshEvent () {
      let { $grid, refreshOpts, isRefresh } = this
      if (!isRefresh) {
        if (refreshOpts.query) {
          this.isRefresh = true
          refreshOpts.query().catch(e => e).then(() => {
            this.isRefresh = false
          })
        } else if ($grid) {
          this.isRefresh = true
          $grid.commitProxy('reload').catch(e => e).then(() => {
            this.isRefresh = false
          })
        }
      }
    },
    btnEvent (evnt, item) {
      let { $grid, $table } = this
      let { code } = item
      if (code) {
        if ($grid) {
          $grid.triggerToolbarBtnEvent(item, evnt)
        } else {
          let btnMethod = Buttons.get(code)
          let params = { code, button: item, $grid, $table }
          if (btnMethod) {
            btnMethod.call(this, params, evnt)
          }
          UtilTools.emitEvent(this, 'button-click', [params, evnt])
        }
      }
    },
    importEvent () {
      const comp = this.$grid || this.$table
      if (comp) {
        comp.openImport()
      } else {
        throw new Error(UtilTools.getLog('plx.error.barUnableLink'))
      }
    },
    exportEvent () {
      const comp = this.$grid || this.$table
      if (comp) {
        comp.openExport(this.customOpts)
      } else {
        throw new Error(UtilTools.getLog('plx.error.barUnableLink'))
      }
    }
  }
}