<template>
  <div class="demo-split">
    <Split v-model="split3" min="600" max="200">
      <div slot="left" class="demo-split-pane no-padding">
        <Split v-model="split4" mode="vertical" min="150px" max="150px">
          <div slot="top" class="demo-split-pane">
            <Row>
              <i-col span="24">
                <FormulaEdit
                  ref="formulaEdit"
                  :height="split4"
                  @TokenChange="OnTokenChange"
                  :functions="funcData"
                  :constants="constData"
                  :value="value"
                  @error='OnFormulaError'
                />
              </i-col>
            </Row>
            <Row>
              <i-col span="12">
                <ButtonGroup size="small">
                  <Button @click="clear">清空</Button>
                  <!-- <Button @click="newFormula">新增</Button> -->
                  <Button @click="checkFormula">检查</Button>
                </ButtonGroup>
              </i-col>
              <i-col id="error-message" span="12" style="text-align: right;">
                <Poptip v-show="formulaError" :content="String(formulaError)" word-wrap placement="bottom">
                  <Alert type="error" show-icon>'检查语法有误'</Alert>
                </Poptip>
              </i-col>
            </Row>
          </div>
          <div slot="bottom" class="demo-split-pane">
              <Row>
                <i-col span="10">
                  <div id="function-list" :style="{'--bottom-height': bottomHeight}">
                    <Tree
                      ref="functionTree"
                      :data="funcData"
                      :render="renderContent"
                      @on-select-change="OnFunctionChange"
                    ></Tree>
                  </div>
                </i-col>
                <i-col span="14">
                  <textarea id="description" :style="{'--bottom-height': bottomHeight}" aria-multiline="true" readonly v-model="desc"></textarea>
                </i-col>
            </Row>
          </div>
        </Split>
      </div>
      <div slot="right" class="demo-split-pane">
        <ParamForm ref="paramForm" id="formula_params" :fData='selectedFunction.params' v-show="paramFormShow"
                      :editor="editor"  @ParamFormulaClick="OnParamFormulaClick"
                      :start="start" :length="length" @paramChange="OnParamChange"></ParamForm>
      </div>
    </Split>
  </div>
</template>
<script>
import create from '../utils/create'
import { functionList } from './data/function'
import FormulaEdit from './formula/FormulaEdit.vue'
import { macro } from './data/constant'
import {
  constantjs,
  //   string
  //   formatFormulaParam
  //   isArray
  //   executeFormula
  getParamFromStr,
  resolution,
  isSplitejs,
  string
} from './formula/formulaAnalysis'
// import { resolution, funcs } from './formula/formulaAnalysis'
import { util } from './formula/formulaUtil'
import ParamForm from './formula/paramForm'
import { Tree } from 'view-design'

const _arrayJoinToStr = (array) => {
  let list = []
  for (let i = 0; i < array.length; i++) {
    if (array[i] instanceof Array) {
      list.push(_arrayJoinToStr(array[i]))
    } else if (typeof array[i] === 'string' && !isSplitejs(array[i])) {
      list.push('"' + array[i] + '"')
    } else {
      list.push(array[i])
    }
  }
  return list
}

export default create({
  name: 'formula',
  components: {
    FormulaEdit,
    Tree,
    ParamForm
  },
  props: {
    value: {
      type: String,
      default: ''
    }
  },
  data () {
    return {
      split3: 0.65,
      split4: '150px',
      desc: '',
      modal6: false,
      loading: true,
      funcData: functionList,
      constData: macro,
      ResultText: '',
      selectedFunction: { params: [] },
      editor: undefined,
      start: 0,
      length: 0,
      formulaName: '',
      formulaError: undefined,
      paramFormShow: true
    }
  },
  computed: {
    bottomHeight () {
      return (465 - parseInt(this.split4)) + 'px'
    }
  },
  methods: {
    // 切换编辑器中函数高亮显示
    OnTokenChange (event) {
      // 切换函数树中选中节点
      let tokenString = event.selectedText
      if (tokenString) {
        let _tree = util.TreeFilter(this.funcData, [tokenString])
        this.funcData = _tree.nodes
        if (_tree.selectedNodes) {
          this.OnFunctionChange(util.funcData, _tree.selectedNodes[0])
        }
      } else {
        this.funcData = util.TreeFilter(this.funcData, ['']).nodes
        this.selectedFunction.params = []
        return
      }

      // 更新参数控件
      if (event.params && event.params.groups) {
        this.paramFormShow = true
        let tokenParams = event.params.groups[0]
        this.formulaName = tokenString
        this.start = event.params.start
        this.length = event.params.length
        if (this.selectedFunction && tokenParams[tokenString]) {
          this.selectedFunction.params = this.selectedFunction.params.map(
            (data, index) => {
              let value = tokenParams[tokenString][index]
              if (value instanceof Array) {
                if (String(value).indexOf('[object Object]') < 0) {
                  if (constantjs(JSON.stringify(value))) {
                    value = _arrayJoinToStr(value)
                    if (typeof value === 'object') {
                      value = value.join('')
                      if (/Array(<?>)?/.test(data.dataType)) value = '[' + value + ']'
                    }
                  } else if (typeof value[0] === 'string') {
                    value[0] = '"' + value[0] + '"'
                  }
                }
              }
              data.value = value
              return data
            }
          )
        }
      } else {
        this.paramFormShow = false
      }
    },
    // 增加表达式
    newFormula () {
      this.selectedFunction.params.forEach((param, index) => {
        let obj = util.deepClone(param)
        obj.value = ''
        this.$set(this.selectedFunction.params, index, obj)
      })

      this.$refs.formulaEdit.clearMarks()
      if (this.selectedFunction.name) {
        this.formulaName = this.selectedFunction.name
      }
    },
    clear () {
      this.$refs.formulaEdit.getEditor().setValue('')
      this.$refs.formulaEdit.getEditor().refresh()
    },
    // 树控件自定义渲染样式
    renderContent (h, { root, node, data }) {
      return h(
        'span',
        {
          style: {
            display: 'inline-block',
            width: '100%'
          }
        },
        [
          h(
            'span', {
              on: {
                dblclick: () => {
                  this.newFormula()
                }
              }
            },
            [
              h('Icon', {
                props: {
                  type: 'ios-paper-outline'
                },
                style: {
                  marginRight: '8px'
                }
              }),
              h('span', data.title + (data.name ? '(' + data.name + ')' : ''))
            ]
          ),
          h('span', {
            style: {
              display: 'inline-block',
              float: 'right'
            }
          })
        ]
      )
    },
    // 点击函数树出发的事件
    OnFunctionChange (nodes, data) {
      if (!data) return
      this.desc = ''
      data.selected = true
      if (data.children) {
        data.expand = !data.expand
      } else {
        this.desc = data.desription
        data.params = data.params.map((currentValue, index, arr) => {
          currentValue = Object.assign({}, currentValue, { value: '', status: true })
          if (currentValue.control && currentValue.control.selectList) {
            currentValue.control.selectList = currentValue.control.selectList.map((item, index, arr) => {
              if (item instanceof Object) {
                return item
              } else {
                return { value: item, label: item }
              }
            })
          }
          return currentValue
        })
        this.selectedFunction = util.deepClone(data)
      }
    },
    // 在参数界面中点击参数中的函数链接以便快速切换编辑器中的函数
    OnParamFormulaClick (event) {
      let pos = 0
      let line = 0
      let cm = this.editor
      this.formulaName = event.formulaName
      this.start = event.start
      for (let i = 0; i < cm.lineCount(); i++) {
        let lineStr = cm.getLine(i)
        if (pos + lineStr.length + 1 <= this.start) {
          line++
          pos += lineStr.length + 1
        } else {
          break
        }
      }
      let postion = {
        line: line,
        ch: this.start - pos + this.formulaName.length
      }
      cm.setCursor(postion)
      cm.focus()
    },
    // 检查公式
    checkFormula () {
      let checkStr = 'check(' + this.editor.getValue() + ')'
      let result = resolution(checkStr, 0)
      if (!result.error) {
        this.$Message.success('检查语法成功')
      } else {
        this.$Message.error('检查语法有误')
      }
    },
    // 参数值改变
    OnParamChange ({ paramIndex, value, params, funcName }) {
      let cm = this.editor
      let c = cm.getCursor()
      let funcStr = cm.getValue().substr(this.start, this.length)
      let currentParams = getParamFromStr(funcStr)
      let docStr = cm.getValue()
      let curStart = 0
      let curEnd = 0
      let startPos = 0
      let endPos = 0
      let val = value ? String(value) : ''
      if (params[paramIndex].dataType === 'string' && !string('' + val, 0)) {
        val = '"' + val + '"'
      }
      if (currentParams[paramIndex]) {
        let str = currentParams[paramIndex].str
        curStart = currentParams[paramIndex].start
        curEnd = currentParams[paramIndex].end
        let pos = 0
        while (pos < str.length) {
          if (/\s/.test(str.substr(pos, 1))) {
            curStart++; pos++
          } else {
            break
          }
        }
        startPos = this.start + curStart
        endPos = this.start + curEnd
        docStr = docStr.substring(0, startPos) + val + docStr.substr(endPos)
      } else if (currentParams[paramIndex - 1]) {
        curStart = currentParams[paramIndex - 1].end
        startPos = this.start + curStart
        endPos = this.start + curStart
        docStr = docStr.substring(0, startPos) + ',' + val + docStr.substr(endPos)
      } else {
        startPos = this.start + curStart
        endPos = this.start + curEnd
        docStr = docStr.substring(0, startPos) + ',' + val + docStr.substr(endPos)
      }
      cm.setValue(docStr)
      cm.setCursor(c)
      if (this.formulaError) cm.focus()
    },
    OnFormulaError (error) {
      // if (error) console.log(error)
      this.formulaError = error
      this.$emit('on-error', error)
    },
    getValue () {
      return this.$refs.formulaEdit.getValue()
    },
    setValue (val) {
      this.$refs.formulaEdit.setValue(val)
    }
  },
  mounted () {
    this.$nextTick(() => {
      this.editor = this.$refs.formulaEdit.getEditor()
    })
  }
})
</script>
<style scoped lang="scss">
.demo-split {
  height: 480px;
  border: 1px solid #dcdee2;
}
.demo-split-pane {
  padding: 10px;
}
.demo-split-pane.no-padding {
  height: 480px;
  padding: 0;
}
.demo-split-pane .ivu-btn-group {
  margin-top: 5px;
}

#function-list {
  ::v-deep .ivu-tree span {
    -moz-user-select: none;
    -ms-user-select: none;
    -webkit-user-select: none;
    user-select: none;
  }
  ::v-deep .ivu-tree ul li {
      margin: 5px;
  }
  height: var(--bottom-height);
  border: solid 1px #dcdee2;
  overflow-y: auto;
  margin-right: 4px;
  text-align: left;
}
#formula-main {
  height: var(--bottom-height);
  width: 100%;
  resize: none;
  margin-bottom: 5px;
}
#description {
  height: var(--bottom-height);
  width: 100%;
  resize: none;
  border: solid 1px #dcdee2;
}
#formula_params {
  width: 100%;
  height: 100%;
}
#error-message {
  ::v-deep .ivu-alert {
    position: relative;
    border-radius: 4px;
    padding: 4px 8px 4px 8px;
    color: #515a6e;
    font-size: 14px;
    line-height: 16px;
    margin: 5px;
  }

  ::v-deep .ivu-alert.ivu-alert-with-icon {
    padding: 4px 8px 4px 8px;
  }

  ::v-deep .ivu-alert-icon {
    font-size: 16px;
    top: 4px;
    left: 12px;
    position: absolute;
  }

  ::v-deep .ivu-alert-message {
    margin-left: 30px;
  }

  ::v-deep .ivu-poptip-body-content-inner {
    text-align: left;
  }
}
</style>
