// 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 { CUSTOM_EVENT, Config, LevelInfo } from "./Config/GameConfig";
import { getProbability, getRandomArrayElements, loadGameResources, numToChinese, randomNum, set16ToRgb } from "./utils";
const { ccclass, property } = cc._decorator;

// 爪子状态
const CLIP_STATE = {
  /** 默认 */
  DEFAULT: 'clipAni',
  /** 出钩 */
  PLAY: 'clipPlay',
  /** 收回 */
  STOP: 'clipStop'
}

@ccclass
export default class GameScene extends cc.Component {
  /** 单格进度条 */
  @property(cc.Prefab)
  procItem: cc.Prefab = null

  /** 舞台道具  */
  @property([cc.Prefab])
  propItem: cc.Prefab[] = []

  /** 加分道具 */
  @property(cc.Prefab)
  bsetPropItem: cc.Prefab = null

  /** 当前关卡 0、1、2... */
  actLevel = 0,

  /** 关卡目标数量 */
  levelObjectives = 0

  /** 当前关卡信息 */
  actLevelInfo = null

  /** 已获得星星数量 */
  starNum = 0

  /** 倒计时 */
  countDown = 0

  /** 游戏是否结束 */
  isGameOver = false

  clip: cc.Node = null

  /** 爪子状态 */
  clipState = CLIP_STATE.STOP

  /** 速度 */
  clipSpeed = Config.normalPropSpeed

  /** 是否在执行动画中 */
  isAni = false

  /** 抓取的目标 */
  clipTarget: cc.Node = null

  /** 场面上剩余加分道具数量 */
  surplusStar = Config.bestPropsNum

  protected onLoad(): void {
    // loadGameResources()
    /** 开启碰撞检测 */
    const cm = cc.director.getCollisionManager()
    cm.enabled = true
    cm.enabledDebugDraw = true;
  }

  start() {
    this.clip = cc.find('clipMask/clipWrp/clip', this.node)

    this.resetConfig()
    this.refreshLevelInfo()
    this.refreshStageProps()
    this.addNodeEvent()
  }

  /** 重置游戏设置 */
  resetConfig() {
    this.clipSpeed = Config.normalPropSpeed
    this.surplusStar = Config.bestPropsNum
    this.clip.getChildByName('line').height = Config.maxLong
    const rotationAni = cc.find('clipMask/clipWrp', this.node)
      .getComponent(cc.Animation)
      .getAnimationState('clipRation')
    rotationAni.speed = Config.rotationSpeed
  }

  /** 爪子碰撞事件 */
  onCollEnter({ detail }) {
    const other: cc.Collider = detail.other
    // 抓到加分道具
    if (other.tag == 1) {
      this.setStarProc()
    }
    this.clipTarget = other.node
    this.clipSpeed = other.tag == 1 ? Config.bestPropSpeed : Config.normalPropSpeed
    other.tag = 0
    this.setClipState(CLIP_STATE.STOP)

  }

  /** 添加节点的事件 */
  addNodeEvent() {
    const gameBtn = cc.find('gameBtn', this.node)
    gameBtn.on(cc.Node.EventType.TOUCH_END, this.playGame, this)
    this.node.on(CUSTOM_EVENT.CLIP_ANI_END, this.onAnimCompleted, this)
    this.node.on(CUSTOM_EVENT.CLIP_COLLISION, this.onCollEnter, this)
  }

  /** 设置爪子状态 */
  setClipState(state) {
    const ani = cc.find('clipMask/clipWrp', this.node).getComponent(cc.Animation)
    const node = cc.find('clipMask/clipWrp/clip', this.node)
    const clip = node.getComponent(cc.Animation)

    const def = cc.find('clipMask/clipWrp/default', this.node)
    this.clipState = state
    switch (state) {
      case CLIP_STATE.DEFAULT:
        // 恢复旋转
        ani.resume()
        // 显示默认动效
        def.active = true
        // 隐藏爪子动效
        this.clip.active = false
        // 有道具销毁道具
        if (this.clipTarget) {
          this.clipTarget.getComponent('propMove').die(() => {
            this.clipTarget = null
          })
        }
        // 场面上没有剩余加分道具，刷新道具舞台
        if (this.surplusStar == 0) {
          this.refreshStageProps()
        }
        break;
      case CLIP_STATE.PLAY:
      case CLIP_STATE.STOP:
        ani.pause()
        this.clip.active = true
        def.active = false
        // 播放爪子动效
        clip.play(state)
        break;
    }
  }

  /** 出钩 */
  playGame() {
    if (this.isGameOver) return
    this.setClipState(CLIP_STATE.PLAY)
  }

  /** 游戏结束 */
  gameOver() {
    this.isGameOver = true
    this.unschedule(this.startCd)
  }

  /**
   * 设置节点lable
   * @param value 
   */
  setLable(key, value) {
    cc.find(key, this.node).getComponent(cc.Label).string = value + ''
  }

  /** 开始倒计时 */
  startCd() {
    const cd = this.countDown - 1 || 0
    if (!cd || cd <= 0) {
      this.setLable('cdIcon/cd', `0s`)
      console.log('倒计时结束')
      this.unschedule(this.startCd)
      return
    }
    this.countDown = cd
    this.setLable('cdIcon/cd', `${this.countDown}s`)
  }

  protected onDestroy(): void {
    this.unschedule(this.startCd)
  }

  /** 刷新关卡信息 */
  refreshLevelInfo(level = 0) {
    this.isGameOver = false
    this.actLevel = level
    this.starNum = 0
    this.levelObjectives = LevelInfo[level].colors.length
    this.actLevelInfo = LevelInfo[level]
    this.countDown = this.actLevelInfo.countDown

    this.setLable('levelName', `第${numToChinese(level + 1)}关`)
    this.setLable('starIcon/starProc', `${this.starNum}/${this.levelObjectives}`)

    this.updateGameProc()

    this.unschedule(this.startCd)
    this.setLable('cdIcon/cd', `${this.countDown}s`)
    this.schedule(this.startCd, 1)
  }

  /** 设置星星进度 */
  setStarProc() {
    const proc = cc.find('procBg/proc', this.node)
    const key = this.starNum
    this.surplusStar--
    if (key >= this.levelObjectives) return

    const node = cc.instantiate(this.procItem)
    const rgbAry = set16ToRgb(this.actLevelInfo.colors[key])
    node.color = new cc.Color(...rgbAry)
    node.setParent(proc)
    this.starNum++

    this.setLable('starIcon/starProc', `${this.starNum}/${this.levelObjectives}`)
    // 达到目标
    if (this.starNum >= this.levelObjectives) {
      this.nextLevel()
    }
  }

  /** 下一关 */
  nextLevel() {
    const nextLevel = this.actLevel + 1
    this.unschedule(this.startCd)
    if (nextLevel >= LevelInfo.length) { // 已通关
      this.gameOver()
      return
    }
    this.refreshLevelInfo(nextLevel)
  }

  /** 更新游戏进度条信息 */
  updateGameProc() {
    const procBg = this.node.getChildByName('procBg'), proc = procBg.getChildByName('proc')
    const actIdx = this.levelObjectives, procItemWidth = 39, procOffset = 1
    proc.removeAllChildren()
    procBg.width = procItemWidth * actIdx + 12 + (actIdx - 1) * procOffset
    proc.width = procItemWidth * actIdx + (actIdx - 1) * procOffset
    procBg.x = -(procBg.width / 2)
  }

  /** 刷新舞台道具 */
  refreshStageProps() {
    this.surplusStar = Config.bestPropsNum
    const gameStage = cc.find('gameStage', this.node)
    const stageProps: cc.Node[] = gameStage.children
    const mapIdx = Math.floor(randomNum(0, Config.propsMap.length))

    const stageMap: number[] = [...Config.propsMap[mapIdx]]
    // 生成加分道具
    for (let i = 0; i < Config.bestPropsNum; i++) {

      // 随机取
      let idx = Math.floor(randomNum(0, stageMap.length))
      // 找到第一个非空的位置
      while (stageMap[idx % stageMap.length] !== 1) idx++
      // 放入加分道具
      stageMap[idx % stageMap.length] = 2
    }
    // 随机道具池
    let propsRandom = []
    for (let i = 0; i < Config.propsNum; i++) {
      propsRandom.push(...this.propItem)
    }
    // 剩余格子数量
    const emptyNum = stageMap.filter(_ => _ == 1).length
    propsRandom = getRandomArrayElements(propsRandom, emptyNum)

    for (let i = 0; i < stageProps.length; i++) {
      const node = stageProps[i]
      const k = stageMap[i]
      // 随机旋转角度
      const angle = randomNum(0, 90).toFixed(2)
      let propNode: cc.Node = null
      node.removeAllChildren()
      if (k == 1) {
        const pre = cc.instantiate(propsRandom.pop())
        propNode = cc.instantiate(pre)
      } else if (k == 2) { // 加分道具
        propNode = cc.instantiate(this.bsetPropItem)
      } else {
        continue;
      }
      if (propNode) {
        propNode.angle = angle > 45 ? angle - 90 : angle

        propNode.setParent(node)
      }

    }

    /** 随机翻转地图 */
    const layout = gameStage.getComponent(cc.Layout)
    layout.horizontalDirection = getProbability(50) ?
      cc.Layout.HorizontalDirection.LEFT_TO_RIGHT : cc.Layout.HorizontalDirection.RIGHT_TO_LEFT
  }

  update(dt: number): void {
    if (this.isGameOver) return

    // 锚点跟爪子之间的大致距离
    const offset = 41
    const maxLong = Config.maxLong - offset

    // 爪子出手
    if (this.clipState == CLIP_STATE.PLAY) {
      if (this.clip.y <= -maxLong) {
        this.setClipState(CLIP_STATE.STOP)
        return
      }
      this.clip.y -= dt * this.clipSpeed
    }

    // 爪子回收
    if (this.clipState == CLIP_STATE.STOP) {
      if (this.clip.y >= -offset) {
        // 恢复初始速度
        this.clipSpeed = Config.normalPropSpeed
        this.setClipState(CLIP_STATE.DEFAULT)
        return
      }
      this.clip.y += dt * this.clipSpeed

      // 抓到物品，道具的移动
      if (this.clipTarget) {
        const pos = this.clip.convertToWorldSpaceAR(cc.v2(0, -133))
        const p = this.clipTarget.parent.convertToWorldSpaceAR(cc.v2(0, 0))
        this.clipTarget.setPosition(pos.x - p.x, pos.y - p.y)
      }
    }
  }
}
