// Learn TypeScript:
//  - https://docs.cocos.com/creator/2.4/manual/en/scripting/typescript.html
// Learn Attribute:
//  - https://docs.cocos.com/creator/2.4/manual/en/scripting/reference/attributes.html
// Learn life-cycle callbacks:
//  - https://docs.cocos.com/creator/2.4/manual/en/scripting/life-cycle-callbacks.html

import { BLOCK_STATE, CUSTOM_EVENT, Config } from "./Config/GameConfig";
import propPool from "./propPool";
import { getRandomArrayElements, waitTime } from "./utils";

const { ccclass, property } = cc._decorator;

@ccclass
export default class BlockManager extends cc.Component {
  isMove = false

  viewWidth = 0

  viewHeight = 0

  /** 方块矩阵外层容器 */
  blockMatrix: cc.Node = null

  /** 默认位置 */
  defaultPos: cc.Vec2 = null

  /** 加分事件 */
  addScoreEvent: cc.Event.EventCustom = null

  /** 放置事件 */
  setBlockEvent: cc.Event.EventCustom = null

  /** 检查方块能否放入事件 */
  checkIsSet: cc.Event.EventCustom = null

  /** 当前矩阵 */
  miniBlockKey = []

  /** 是否被禁用 */
  isDisabled = false

  /** 小方块 */
  @property(cc.Prefab)
  miniBlockPre: cc.Prefab = null

  /** 消除动画是否执行中 */
  isBlcokClearAni = false

  /** 放置音效 */
  @property(cc.AudioClip)
  setAudio:cc.AudioClip = null

  /** 清除音效 */
  @property(cc.AudioClip)
  clearAudio:cc.AudioClip = null

  start() {
    this.viewHeight = cc.view.getVisibleSize().height
    this.viewWidth = cc.view.getVisibleSize().width
    this.defaultPos = this.node.getPosition()
    this.addScoreEvent = new cc.Event.EventCustom(CUSTOM_EVENT.ADD_SCORE, true)
    this.setBlockEvent = new cc.Event.EventCustom(CUSTOM_EVENT.SET_BLOCK, true)
    this.checkIsSet = new cc.Event.EventCustom(CUSTOM_EVENT.CHECK_IS_SET, true)
    this.blockMatrix = cc.find('blockMatrix', this.node.parent)
    this.refreshMiniBlock()
    this.addNodeEvent()
  }

  /**
   * 刷新小方块的生成
   * @param isPlayAni 是否需要播放刷新动画
   * @param blocks 预设
   */
  refreshMiniBlock(isPlayAni, blocks) {
    let miniBlockMatrix = []
    if (blocks) {
      miniBlockMatrix = [...blocks]
    } else {
      miniBlockMatrix = getRandomArrayElements(Config.miniBlockMatrix, 1)[0]
    }
    this.miniBlockKey = JSON.parse(JSON.stringify(miniBlockMatrix))
    this.node.removeAllChildren()
    const { miniBlockOffset, miniBlockW } = Config
    const len = miniBlockMatrix.length, maxRowNum = 0
    const maxColNumAry = Array(len).fill(0)
    miniBlockMatrix.forEach((colBlocks, i) => {
      let maxX = 0
      colBlocks.map((miniBlock, j) => {
        if (miniBlock == 1) {
          maxColNumAry[j]++
          maxX++
          const node = propPool.isEmpyt ? cc.instantiate(this.miniBlockPre) : propPool.pop()
          const miniBlockSize = miniBlockOffset + miniBlockW
          node.x = j * miniBlockSize - this.node.width / 2 + miniBlockOffset + miniBlockW / 2
          node.y = (len - i - 1) * miniBlockSize - this.node.width / 2 + miniBlockOffset + miniBlockW / 2
          node.setParent(this.node)
        }
      })

      if (maxX > maxRowNum) {
        maxRowNum = maxX
      }
    })
    // 处理偶数方块x轴偏移
    if (maxRowNum % 2 === 0) {
      this.node.children.forEach((n) => {
        n.x -= miniBlockW / 2
      })
    }
    // 处理偶数方块y轴偏移
    if (Math.max(...maxColNumAry) % 2 === 0) {
      this.node.children.forEach((n) => {
        n.y += miniBlockW / 2
      })
    }
    if (isPlayAni) {
      cc.tween(this.node)
        .to(0.3, { opacity: 255, position: this.defaultPos })
        .start()
    }
    this.checkIsSetBlock()
  }

  /** 设置所有的方块复原 */
  setBlockMatrixAll() {
    this.isMove = false
    const allBlock = this.node.children
    if (this.isSetBlockMartix()) {
      allBlock.forEach((node: cc.Node) => {
        const scripts = node.getComponent('miniBlock')
        scripts.setBlockMatrix()
        cc.find('Canvas').dispatchEvent(this.addScoreEvent)
      })
      this.clearAllChildren()
      this.checkBlockMatrix()
      cc.audioEngine.play(this.setAudio,false,1)
    
      
      cc.find('Canvas').dispatchEvent(this.setBlockEvent)
      // this.refreshMiniBlock()
      return
    }
    this.node.setPosition(this.defaultPos)
    this.node.scale = 1
  }


  /** 清除所有自己的子元素 */
  clearAllChildren() {
    this.miniBlockKey = []
    this.node.children.forEach((node) => {
      propPool.push(node)
    })
    this.node.removeAllChildren()
    this.node.opacity = 0
    this.node.setPosition(this.defaultPos.x, this.defaultPos.y - 100)
    this.node.scale = 1
    this.setNodeState(1)

  }

  /** 检查块矩阵是否能够消除 */
  checkBlockMatrix() {
    const blockMatrix = this.blockMatrix.children.map((node) => {
      const script = node.getComponent('block')
      return {
        node,
        script,
        blockState: script.blockState,
        idx: node.getSiblingIndex()
      }
    })
    const { maxCol, maxRow } = Config
    const eliminateBlocks = []
    // 检查行
    for (let i = 0; i < maxCol; i++) {
      let temp = []
      for (let j = 0; j < maxRow; j++) {
        const block = blockMatrix[j + i * maxCol]
        if (block.blockState !== BLOCK_STATE.NON_EMPTY) break
        temp.push(block)
        if (temp.length === maxRow) {
          // console.log('最后一个方块',block.idx)
          eliminateBlocks.push(temp)
        }
      }
    }

    // 检查列
    for (let i = 0; i < maxRow; i++) {
      let temp = []
      for (let j = 0; j < maxCol; j++) {
        const block = blockMatrix[i + j * maxRow]
        if (block.blockState !== BLOCK_STATE.NON_EMPTY) break
        temp.push(block)
        if (temp.length === maxCol) {
          // console.log('最后一个方块',block.idx)
          eliminateBlocks.push(temp)
        }
      }
    }

    this.clearMatrixBlock(eliminateBlocks)
    return eliminateBlocks.length > 0
    // console.log('eliminateBlocks:', eliminateBlocks)
  }

  /**
   * 清理矩阵块
   * @param eliminateBlocks 需要清理的方块组
   */
  clearMatrixBlock(eliminateBlocks: cc.Node[]) {
    const recursionPlay = async (els) => {
      if (els.length === 0) return
      const { script } = els.pop()
      script.playClearAni(() => {
        cc.find('Canvas').dispatchEvent(this.addScoreEvent)
        if (els.length == 0) {
          cc.find('Canvas').dispatchEvent(this.checkIsSet)
        }
      })
      await waitTime(Config.blockAniDelay)
      recursionPlay(els)
    }
    if(eliminateBlocks.length >0){
      cc.audioEngine.play(this.clearAudio,false,1)
    }
    eliminateBlocks.forEach((els) => {
      recursionPlay(els)
    })
    
  }

  /** 检查矩阵能否放入 */
  checkIsSetBlock() {
    // console.log('check====', this.node.name)

    const allEmptyBlock = this.blockMatrix.children.filter((block) => {
      const block = block.getComponent('block')
      return block.blockState === BLOCK_STATE.EMPTY
    })

    const allEmptyBlockIdx = allEmptyBlock.map((node) => node.getSiblingIndex())
    // console.log(allEmptyBlockIdx, 'allEmptyBlockIdx')
    if (this.miniBlockKey.length === 0) {
      this.setNodeState(1)
      return
    }
    for (let k = 0; k < allEmptyBlockIdx.length; k++) {
      const base = allEmptyBlockIdx[k]
      let baseX = Math.trunc(base / 10), baseY = base % 10
      let mBaseX = -1, mBaseY = -1, isNext = false
      // console.log('base', base, baseX, baseY)
      for (let i = 0; i < this.miniBlockKey.length; i++) {
        if (isNext) break
        const keys: [] = this.miniBlockKey[i]
        for (let j = 0; j < keys.length; j++) {
          if (keys[j] === BLOCK_STATE.NON_EMPTY) {
            if (mBaseX == -1 && mBaseY == -1) {
              mBaseX = i
              mBaseY = j
            } else {
              const diffX = i - mBaseX, diffY = j - mBaseY
              const tempX = baseX + diffX, tempY = baseY + diffY
              // console.log(baseX, baseY, mBaseX, mBaseY, i, j,tempX,tempY)
              // console.log(this.miniBlockKey)
              const tempBase = tempY + tempX * 10
              // console.log('newBase', tempBase, tempX, tempY)
              if (tempX < 0 || tempY < 0
                || tempBase < 0 || tempX >= 10
                || tempY >= 10 || !allEmptyBlockIdx.includes(tempBase)) {
                isNext = true
              }
            }
          }
        }
      }
      if (!isNext) {
        this.setNodeState(0)
        // console.log('可以放置', this.miniBlockKey)
        return
      }
    }
    this.setNodeState(1)
  }

  /**
   * 设置当前节点状态
   * @param state 0 可用 1禁用
   */
  setNodeState(state) {
    const isDisabled = this.isDisabled = state != 0
    // console.log(this.miniBlockKey, !isDisabled ? '可用' : '禁用')
    this.node.children.forEach(node => {
      node.color = cc.color(isDisabled ? Config.miniBlockDisabledColor : Config.miniBlockColor)
    })
  }

  /**
   * 判断是否能够放入
   * @returns 
   */
  isSetBlockMartix() {
    const allBlock = this.node.children
    const filterBlock = []
    for (let i = 0; i < allBlock.length; i++) {
      const script = allBlock[i].getComponent('miniBlock')
      const { targetPos, targetNode } = script.getTargetMatrix()
      // 未获取到指定最短距离内的节点
      if (!targetNode) {
        return false
      }
      filterBlock.push({ targetPos, targetNode })
    }
    // 该目标节点是否为空
    const isEmpty = filterBlock.filter(({ targetNode }) => {
      const script = targetNode.getComponent('block')
      return script.blockState === BLOCK_STATE.NON_EMPTY
    }).length === 0
    if (!isEmpty) return false
    const allTargetNodeId = filterBlock.map(({ targetNode }) => targetNode.getSiblingIndex())
    // 索引是否重复
    const isRepeat = allTargetNodeId.find((_, i) => allTargetNodeId.includes(_, i + 1))
    const GameScene = cc.find('Canvas').getComponent('GameScene')
    // 新手引导情况下，只能放入对应位置
    if (GameScene.isGuide && !isRepeat && isEmpty) {
      const { targetNode } = filterBlock?.[0]
      if (allTargetNodeId[0] !== 90) {
        return false
      }
    }
    return !isRepeat && isEmpty
  }

  nodeReset(){
    this.node.setPosition(this.defaultPos)
    this.node.scale = 1
  }

  addNodeEvent() {
    this.node.on(cc.Node.EventType.TOUCH_START, () => {
      if (this.isDisabled == 1) return
      this.isMove = true
      this.node.scale = 1.7
    }, this)

    this.node.on(cc.Node.EventType.TOUCH_END, () => {
      if(this.isMove){
        this.setBlockMatrixAll()
      }
      
    }, this)

    cc.find('Canvas').on(cc.Node.EventType.TOUCH_END,() => {
      if(this.isMove){
        this.setBlockMatrixAll()
      }
    })
    cc.find('Canvas').on(cc.Node.EventType.TOUCH_CANCEL,() => {
      if(this.isMove){
        this.setBlockMatrixAll()
      }
    })
    cc.find('Canvas').on(cc.Node.EventType.MOUSE_LEAVE,() => {
      if(this.isMove){
        this.setBlockMatrixAll()
      }
    })

    this.node.on(cc.Node.EventType.TOUCH_MOVE, (e: cc.Event.EventTouch) => {
      if (this.isMove) {
        const pos: cc.Vec2 = e.getPreviousLocation()
        const viewW = this.viewWidth, viewH = this.viewHeight

        if (pos.x < 0 || pos.x > viewW || pos.y > (viewH - 200) || pos.y < 0) {
          this.isMove = false
          this.nodeReset()
          return
        }
        this.node.setPosition(pos.x - (viewW / 2), pos.y - (812 - (1624 - viewH) / 2) + Config.blockManagerDragOffsetY)
      }
    }, this)
  }

  // update (dt) {}
}
