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

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

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

  /** 舞台道具  */
  @property({ type: [cc.Prefab], displayName: '舞台道具' })
  propItem: cc.Prefab[] = []

  /** 加分道具 */
  @property({ type: cc.Prefab, displayName: '加分道具' })
  bsetPropItem: cc.Prefab = null

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

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

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

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

  /** 倒计时 */
  countDown = 0

  /** 游戏是否结束 */
  isGameOver = false
  /** 游戏是否开始 */
  isStartGame = false

  clip: cc.Node = null

  /** 爪子是否执行动画中 */
  clipAni = false

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

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

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

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

  /** 游戏配置 */
  gameConfig = { ...Config }

  /** 加速倍率 */
  speedMultiplier = 1

  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.addNodeEvent()
  }

  /** 添加节点的事件 */
  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)

    exportEvent.on(CUSTOM_EVENT.NEXT_LEVEL, this.nextLevel, this)
    exportEvent.on(CUSTOM_EVENT.GAME_START, this.startGame, this)
    exportEvent.on(CUSTOM_EVENT.GAME_INIT, ({ detail = {} }) => {
      this.resetConfig(detail)
      this.refreshLevelInfo()
      this.refreshStageProps()
    }, this)
    exportEvent.on(CUSTOM_EVENT.GAME_DESTROY, () => {
      this.node.destroy()
    }, this)
    // TODO 测试用
    const href = window?.location?.href
    const isTest = getUrlParam('testPanel')
    const testNode = cc.find('test', this.node)
    testNode.active = isTest
    if (isTest && (href?.includes('duibadev') || href?.includes('duibatest')) || href.includes('localhost')) {
      const testEventMap = {
        next: 'nextLevel',
        refreshProp: 'refreshStageProps',
        start: 'testStartGame',
        init: 'testInit',
        addSpeed: 'addSpeeds'
      }
      testNode.children.forEach((n) => {
        const event = this?.[testEventMap[n.name]]
        n.on(cc.Node.EventType.TOUCH_END, event, this)
      })
    }
  }

  testStartGame() {
    console.log('测试')
    exportEvent.fire(CUSTOM_EVENT.GAME_START)
  }

  testInit() {
    console.log('测试初始化')
    exportEvent.fire(CUSTOM_EVENT.GAME_INIT, {
      LevelInfo: [
        {
          level: 1,
          /** 目标数量 */
          num: 1,
          /** 倒计时 */
          cds: 50,
        },
        {
          level: 1,
          /** 目标数量 */
          num: 5,
          /** 倒计时 */
          cds: 30,
        },
        // {
        //   level: 1,
        //   /** 目标数量 */
        //   num: 8,
        //   /** 倒计时 */
        //   cds: 30,
        // }
      ]
    })
  }

  addSpeeds() {
    this.speedMultiplier += 1
    const rotationAni = cc.find('clipMask/clipWrp', this.node)
      .getComponent(cc.Animation)
      .getAnimationState('clipRation')
    rotationAni.speed += 1
  }

  /**
   * 开始游戏
   * @param config config
   */
  startGame({ detail = {} }) {
    this.startGameCountAni(() => {
      this.isStartGame = true
      cc.find('clipMask/clipWrp', this.node).getComponent(cc.Animation).play()
      this.schedule(this.startCd, 1)
    })
  }

  /** 开始游戏倒计时动画 */
  startGameCountAni(cb) {
    const cd = cc.find('countDown/cd', this.node),
      cdBg = cc.find('countDown/cdBg', this.node)
    cd.parent.active = true
    cdBg.opacity = 150
    cd.on(SvgaEvent.END_FRAME, () => {
      cb?.()
      cd.parent.active = false
    })
    cd.getComponent(Svga).play(0)
  }

  /**
   * 重置游戏配置
   * @param con 游戏配置
   */
  resetConfig(con = {}) {
    this.gameConfig = {
      ...Config,
      ...con
    }
    this.clipSpeed = this.gameConfig.normalPropSpeed
    this.surplusStar = this.gameConfig.bestPropsNum
    this.clip.getChildByName('line').height = this.gameConfig.maxLong
    const rotationAni = cc.find('clipMask/clipWrp', this.node)
      .getComponent(cc.Animation)
      .getAnimationState('clipRation')
    rotationAni.speed = this.gameConfig.rotationSpeed
    this.isGameOver = false
    this.isStartGame = false
    this.speedMultiplier = 1
    this.clipTarget = null
    this.setClipState(CLIP_STATE.DEFAULT, () => {
      cc.find('clipMask/clipWrp', this.node).angle = 0
      cc.find('clipMask/clipWrp', this.node).getComponent(cc.Animation).stop()
    })
    this.clip.y = -41
  }

  /** 爪子碰撞事件 */
  onCollEnter({ detail }) {
    const other: cc.Collider = detail.other

    this.clipTarget = other.node
    this.clipSpeed = other.tag == 1 ? this.gameConfig.bestPropSpeed : this.gameConfig.normalPropSpeed
    this.setClipState(CLIP_STATE.STOP)
  }

  /**
   * 设置爪子状态
   * @param state CLIP_STATE
   */
  setClipState(state, cb) {
    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(() => {
            // 加分道具
            if (this.clipTarget.getComponent(cc.Collider).tag == 1) {
              this.setStarProc()
            }
            this.clipTarget = null
            this.clipAni = false
            // 场面上没有剩余加分道具，刷新道具舞台
            if (this.surplusStar == 0) {
              this.refreshStageProps()
            }
          })
        } else {
          this.clipAni = false
        }
        break;
      case CLIP_STATE.PLAY:
      case CLIP_STATE.STOP:
        ani.pause()
        this.clip.active = true
        def.active = false
        this.clipAni = true
        // 播放爪子动效
        clip.play(state)
        break;
      case CLIP_STATE.GAME_OVER:
        ani.stop()
        clip.stop()
        break;
    }
    cb?.()
  }

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

  /** 游戏结束 */
  gameOver() {
    this.isGameOver = true
    this.setClipState(CLIP_STATE.GAME_OVER)
    this.unschedule(this.startCd)
    exportEvent.fire(CUSTOM_EVENT.GAME_OVER, {
      ...this.gameConfig.LevelInfo[this.actLevel],
      score: this.starNum,
      level: this.actLevel
    })
  }

  /**
   * 设置节点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`)
      this.gameOver()
      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.actLevelInfo = this.gameConfig.LevelInfo[level]

    this.levelObjectives = this.actLevelInfo.num
    this.countDown = this.actLevelInfo.cds || 0
    const levelMap = ['font/level1', 'font/level2', 'font/level3']
    cc.resources.load(levelMap[level] || 'font/midIcon', (err, ex) => {
      cc.find('midIcon', this.node).getComponent(cc.Sprite).spriteFrame = new cc.SpriteFrame(ex)
    })
    // 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`)
  }

  /** 设置星星进度 */
  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), colors = GameColors?.[this.actLevel] || []
    node.color = cc.color(colors[key] || this.gameConfig.defaultColor)
    node.setParent(proc)
    this.starNum++

    this.setLable('starIcon/starProc', `${this.starNum}/${this.levelObjectives}`)
    // 达到目标
    if (this.starNum >= this.levelObjectives) {
      this.isStartGame = false
      this.unschedule(this.startCd)
      exportEvent.fire(CUSTOM_EVENT.PASS_LEVEL, {
        ...this.gameConfig.LevelInfo[this.actLevel],
        score: this.starNum,
        level: this.actLevel
      })
      // this.nextLevel()
    }
  }

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

  /** 更新游戏进度条信息 */
  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 = this.gameConfig.bestPropsNum
    const gameStage = cc.find('gameStage', this.node)
    const stageProps: cc.Node[] = gameStage.children
    const mapIdx = Math.floor(randomNum(0, this.gameConfig.propsMap.length))

    const stageMap: number[] = [...this.gameConfig.propsMap[mapIdx]]
    // 生成加分道具
    for (let i = 0; i < this.gameConfig.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 < this.gameConfig.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 || !this.isStartGame) return

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

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

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

      // 抓到物品，道具的移动
      if (this.clipTarget && this.clipTarget?.parent) {
        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)
      }
    }
  }
}
