import React from 'react'
import i18n from 'i18next'
import newEventActions from 'actions/newEvent'
import newEventStores from 'stores/newEvent'
import treeStores from 'stores/tree'
import {
  getEventBlockByBid,
  emptyCon,
  genConObj,
  consAstToArray
} from 'stores/funcs/newEvent'
import { cpJson, shallowEqualImmutable } from 'utils/utils'
import { consFormulaShow } from 'utils/commons/ctrlHide'
import { BLOCK_OP_TYPE } from 'const/const'
import BlockLoopConItem from './BlockLoopConItem'
import FormulaEditor from 'src/components/common/formulaEditor/EventFormulaEditor'

export default class BlockLoopCustom extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      option: this.getLoopOption(props.bid),
      custom: this.getLoopCustom(this.getLoopOption(props.bid), props.bid)
    }

    this.onEventChange = this.onEventChange.bind(this)
    this.onChangeTimes = this.onChangeTimes.bind(this)
    this.getLoopOption = this.getLoopOption.bind(this)
    this.getLoopCustom = this.getLoopCustom.bind(this)
    this.getCustomCom = this.getCustomCom.bind(this)
    this.changeConsAst = this.changeConsAst.bind(this)
    this.addBlockCon = this.addBlockCon.bind(this)
    this.delBlockCon = this.delBlockCon.bind(this)
  }

  shouldComponentUpdate(nextProps, nextState) {
    return (
      !shallowEqualImmutable(this.props, nextProps) ||
      !shallowEqualImmutable(this.state, nextState)
    )
  }

  componentDidMount() {
    this.unsubscribe = newEventStores.listen(this.onEventChange)
  }

  componentWillUnmount() {
    this.unsubscribe()
  }

  componentDidUpdate(preProps) {
    if (preProps.bid !== this.props.bid) {
      this.setState({
        option: this.getLoopOption(),
        custom: this.getLoopCustom(this.getLoopOption())
      })
    }
  }

  onEventChange(status) {
    let obj = {}
    if (status && status.types) {
      if (
        status.types.includes('updateBlockProp') &&
        this.props.bid === status.data.affectBlockId &&
        status.data.prop === 'op'
      ) {
        let option = this.getLoopOption()
        if (this.state.option !== option) {
          obj.option = option
          obj.custom = this.getLoopCustom(option)
        }
      }
      if (
        status.types.includes('updateBlockProp') &&
        this.props.bid === status.data.affectBlockId &&
        status.data.prop === 'cons'
      ) {
        this.forceUpdate()
      }
    }
    if (Object.keys(obj).length > 0) {
      this.setState(obj)
    }
  }

  genForCons(blockId, value) {
    return {
      op: 'let',
      val: [blockId, 'int'],
      args: [{ op: 'step', args: [{ op: 'var', expVal: value, args: [] }] }]
    }
  }

  onChangeTimes(value) {
    this.setState(
      {
        custom: value
      },
      () => {
        let forCons = this.genForCons(this.props.bid, value)
        newEventActions.updateBlockProp({
          blockId: this.props.bid,
          prop: 'cons',
          value: forCons
        })
      }
    )
  }

  getLoopOption(bid) {
    let _bid = bid || this.props.bid
    let nodeId = this.props.eventNodeId || newEventStores.curEventNodeId
    let block = getEventBlockByBid(_bid, nodeId)
    if (block) {
      return block.op || BLOCK_OP_TYPE.FOR
    }
    return BLOCK_OP_TYPE.FOR
  }

  getLoopCustom(option, bid) {
    let _bid = bid || this.props.bid
    let nodeId = this.props.eventNodeId || newEventStores.curEventNodeId
    let block = getEventBlockByBid(_bid, nodeId)
    if (block) {
      switch (option) {
        case BLOCK_OP_TYPE.FOR:
          return (
            block.args &&
            block.args[0] &&
            block.args[0].args &&
            block.args[0].args[0] === 'step' &&
            block.args[0].args[0].args &&
            block.args[0].args[0].args[0].expVar
          )
        case BLOCK_OP_TYPE.WHILE:
          return consAstToArray(block.args[0])
      }
    }
    return null
  }

  changeConsAst() {
    // 将cons数组转为ast格式
    let cons = cpJson(this.state.custom)
    let ors = []
    let ands = []
    cons.forEach((con, index) => {
      if (con.flag !== 'or') {
        ands.push(con)
      } else {
        ors.push(ands)
        ands = []
        ands.push(con)
      }
      if (index === cons.length - 1) {
        ors.push(ands)
      }
    })

    let consAst = {}
    if (ors.length > 1) {
      // 有or
      consAst.op = 'or'
      consAst.args = []
      ors.forEach(orItem => {
        if (orItem.length > 1) {
          // 多个条件
          let andObj = {
            op: 'and',
            args: []
          }
          orItem.forEach(conItem => {
            let conObj = genConObj(conItem)
            andObj.args.push(conObj)
          })
          consAst.args.push(andObj)
        } else {
          // 单个条件
          let conObj = genConObj(orItem[0])
          consAst.args.push(conObj)
        }
      })
    } else {
      // 没有or
      consAst.op = 'and'
      consAst.args = []
      let andArray = ors[0]
      if (andArray.length > 1) {
        // 多个条件
        andArray.forEach(conItem => {
          let conObj = genConObj(conItem)
          consAst.args.push(conObj)
        })
      } else {
        // 单个条件
        consAst = genConObj(andArray[0])
      }
    }

    let nodeId = this.props.eventNodeId || treeStores.curEventNodeId
    newEventActions.updateBlockProp({
      nodeId,
      blockId: this.props.bid,
      prop: 'cons',
      value: consAst,
      trigger: true,
      addRecord: true
    })
  }

  addBlockCon() {
    let cons = this.state.custom
    let newEmptyCon = emptyCon()
    cons.push(newEmptyCon)
  }

  delBlockCon({ index, resetLast }) {
    let cons = this.state.custom
    if (resetLast && cons.length === 1) {
      cons[0] = emptyCon()
    } else {
      cons.splice(index, 1)
    }
    if (cons.length === 1) {
      cons[0].flag = 'and'
    }
  }

  getCustomCom() {
    let forCom = () => {
      return (
        <div className="loop-for-wrap f--hlc">
          <div className="for-tag f--hcc">
            {i18n.t('EventView.jenga_loop_block.forTag')}
          </div>
          <div className="for-times-wrap f--h">
            <FormulaEditor
              className="loop-formula-editor loop-for-formula-editor"
              uid={this.props.bid + BLOCK_OP_TYPE.FOR}
              bid={this.props.bid}
              value={this.state.custom}
              hasCons={consFormulaShow()}
              onChangeCode={this.onChangeTimes}
            />
          </div>
        </div>
      )
    }

    let whileCom = () => {
      let cons = (
        <div className="con-items-wrap">
          {this.state.custom.map((con, index) => {
            return (
              <BlockLoopConItem
                index={index}
                con={con}
                key={index}
                layer={this.props.layer}
                groupLayer={this.props.groupLayer}
                nestGroupLayer={this.props.nestGroupLayer}
                extraGapGroupLayer={this.props.extraGapGroupLayer}
                bid={this.props.bid}
                eventNodeId={this.props.eventNodeId}
                onConsChange={this.changeConsAst}
                onAddBlockCon={this.addBlockCon}
                onDelBlockCon={this.delBlockCon}
              />
            )
          })}
        </div>
      )
      return (
        <div className="loop-while-wrap f--hlc">
          {!this.state.custom || this.state.custom.length === 0 ? null : cons}
        </div>
      )
    }

    switch (this.state.option) {
      case BLOCK_OP_TYPE.FOR:
        return forCom()
      case BLOCK_OP_TYPE.WHILE:
        return whileCom()
    }
  }

  render() {
    return <div className="loop-custom flex-1">{this.getCustomCom()}</div>
  }
}
