import { GDispatcher } from "../..";
import { DreamContainer } from "../../components/DreamContainer/DreamContainer";
import { DreamShape } from "../../components/DreamShape/DreamShape";
import { DreamSprite } from "../../components/DreamSprite/DreamSprite";
import Dream from "../../Dream";
import { layers } from "../../modules/layers";
import { RES } from "../../modules/RES";
import { UsePreload } from "../../modules/UseDecorator/usePreload";
import { PromiseAwait, PromiseAwaitCacheMap, Tools } from "../../tools/Tools";
import { GameConfig } from "./GameConfig";
import { G_Events } from "./GameEvents";
import { Platform } from "./Platform";
import { PlayerIns, RabbitActionMap, RabbitPlayer } from "./Player";
import { ScoreBoard } from "./ScoreBoard";
import { SpClock } from "./SpClock";

type Pos = {
  x: number,
  y: number
}

const {
  rowNum, offsetYLen, offsetXLen
} = GameConfig
const mapHeight = rowNum * offsetYLen

function randomPro(n: number) {
  return Math.random() < n
}

@UsePreload({
  preAction: async function() {
    await RES.loadGroup('GameScene')
    await RES.loadGroup('spine')
    await RES.loadGroup('svga')
  }
})
export class GameScene extends Dream.RenderContainer {
  GameMapContainer: FYGE.Container
  GameReelContainer: FYGE.Container // 当前卷轴Container 用作 卷轴
  SceneContainer: FYGE.Container

  _mapCount: number = 0 // 当前渲染地址个数
  renderMapOffsetY: number = 0 // 当卷轴移动到当前 判定值就去渲染地图
  get mapCount() {
    return this._mapCount
  }
  set mapCount(v) {
    this._mapCount = v;
    this.renderMapOffsetY = layers.stageOffsetY - GameConfig.renderMapScreenNum + (v - 1) * mapHeight
  }

  isPause: boolean = true
  rabbitPos: Pos
  rabbitContainer: FYGE.Container // 兔子Container
  rabbitCloneContainer: FYGE.Container // 兔子的克隆体 做创屏幕动画的
  rabbitBoxMap: Platform[][] = [] // 兔子地图map
  ScoreBoardIns: ScoreBoard // 当前分数面板
  SpClockIns: SpClock // 道具倒计时
  double: number = 1 // 是否双倍

  rabbitIns: PlayerIns
  cloneRabbitIns: PlayerIns

  GuidanceCont: FYGE.Container

  set dir(v: number) {
    this.rabbitContainer.scaleX = v
    this.rabbitCloneContainer.scaleX = v
  }

  didRendered(): void {
    this.renderMap(true, GameConfig.sptMap)

    this.initEvents()

    this.onGameStart()
  }

  unMount(): void {
    this.removeEvents()
    PromiseAwaitCacheMap.clear()
  }

  initEvents() {
    // 测试地图
    // document.body.addEventListener('keydown', (e) => {
    //   const keyCode = e.code
    //   console.log(keyCode)
    //   if (keyCode == 'ArrowDown') {
    //     this.onGameStart()
    //   } else {
    //     this.onGameOver()
    //   }
    // })
  }

  removeEvents() {

  }

  onGameStart() {
    GameConfig.speed = 100
    GameConfig.DecisionLine = [
      180,
      layers.stageHeight - 10
    ]
    if (GameConfig.debugger) {
      this.SceneContainer.addChild(
        <DreamContainer inlineProps={{
          y: layers.stageOffsetY
        }}>
          <DreamShape drawData={[0, GameConfig.DecisionLine[0], 750, 4]} />
          <DreamShape drawData={[0, GameConfig.DecisionLine[1], 750, 4]} />
        </DreamContainer>
      )
    }
    this.isPause = false
  }

  /**
   * 游戏结束
   */
  onGameOver() {
    this.onShowRabbitAni(RabbitActionMap.Fail)

    FYGE.Tween.removeTweens(this.rabbitContainer)
    FYGE.Tween.removeTweens(this.rabbitCloneContainer)
    this.isPause = true

    GDispatcher.dispatchEvent(G_Events.GameOver, this.ScoreBoardIns.score)
  }

  /**
   * 显示动画
   */
  onShowRabbitAni(aniName: RabbitActionMap, endAni?: RabbitActionMap) {
    this.rabbitIns.showAni(aniName, endAni)
    this.cloneRabbitIns.showAni(aniName, endAni)
  }

  /**
   * 开始渲染地图
   * @param init 是否是初始化
   */
  renderMap(init: boolean = false, mapConfig?: any) {
    if (!mapConfig) {
      const index = Math.random() * GameConfig.mapList.length >> 0
      console.info('=========render map ===========:', index)
      mapConfig = GameConfig.mapList[index]
    } 
    
    const {
      rowNum,
      offsetYLen
    } = GameConfig
    const mapBoxes = mapConfig.boxMap
    if (init) {
      this.renderRabbit(mapConfig.initPos)
    } else {

      this.rabbitPos.y += rowNum
    }
    // 地图平台数组
    const mapPlatform:Platform[][] = []
    this.GameMapContainer.addChildAt(
      <DreamContainer className="mapContainer" onRemoveFromStage={() => {
        console.log(this.rabbitBoxMap)
        console.log('remove!!!!')
      }} inlineProps={{
        y: this.mapCount * rowNum * offsetYLen * -1
      }}>
        {
          mapBoxes.map((rowItem, rowIndex) => {
            const rowArr: any[] = mapPlatform[rowIndex] = []
            let boomflag = false // 一行就让出一个炸弹吧
            let clockflag = false // 一行最多一个 道具吧
            return (
              <DreamContainer inlineProps={{
                y: rowIndex * GameConfig.offsetYLen
              }}>
                {
                  rowItem.map((colItem, colIndex) => {
                    if (colItem == 4) {
                      colItem = randomPro(GameConfig.sp_p) ? 2 : 1
                    } else if (colItem == 5) {
                      colItem = randomPro(GameConfig.sp_b) ? 3 : 1
                    }
                    // 一行中还是 随机到 炸弹就变成平台
                    if (colItem == 3) {
                      if (boomflag) {
                        colItem = 1
                      } else {
                        boomflag = true
                      }
                    }

                    if (colItem == 2) {
                      if (clockflag) {
                        colItem = 1
                      } else {
                        clockflag = true
                      }
                    }

                    if (
                      colItem
                    ) {
                      return <Platform ref={(el: Platform) => {
                        rowArr[colIndex] = el
                      }}
                      pos={[rowIndex, colIndex]} type={colItem % 4} x={colIndex * offsetXLen} />
                    } else {
                      rowArr[colIndex] = undefined
                      return undefined
                    }
                  })
                }
              </DreamContainer>
            )
          })
        }
      </DreamContainer>
    , 0)
    this.mapCount += 1

    this.rabbitBoxMap.unshift(...mapPlatform)

    // 第三章图片的话，清楚掉第一张地图
    if (this.mapCount >= 3) {
      this.rabbitBoxMap.splice(60)
      this.GameMapContainer.removeChildAt(2)
    }
  }

  /**
   * 渲染兔子，会再来一个clone rabbit
   * @param pos 
   * @returns 
   */
  renderRabbit(pos: Pos) {
    if (this.rabbitContainer) return
    this.rabbitContainer = this.GameMapContainer.addChild(<RabbitPlayer getPlayer={p => {
      this.rabbitIns = p
    }} />)
    this.rabbitCloneContainer = this.GameMapContainer.addChild(<RabbitPlayer getPlayer={p => {
      this.cloneRabbitIns = p
    }} inlineProps={{
      visible: false,
      className: 'clone'
    }} />)
    this.rabbitContainer.position.set(
      pos.x * offsetXLen,
      pos.y * offsetYLen
    )
    this.rabbitPos = pos
  }

  lastTime: number = 0
  onEnterFrame() {
    if (this.isPause) return
    const now = Date.now()
    const st = (now - this.lastTime) / 1000
    this.lastTime = now
    if (st > 0.2) {
      return
    }
    this.GameReelContainer.y += GameConfig.speed * st
    if (this.GameReelContainer.y >= this.renderMapOffsetY) {
      this.renderMap()
    }
  }

  /**
   * 获取当前兔子相对于 屏幕的Y
   * @returns 
   */
  getCurrRabbitYFromScreen() {
    return this.GameReelContainer.y + this.rabbitContainer.y - layers.stageOffsetY
  }

  /**
   * 游戏场景点击， 用作逻辑处理
   */
  @PromiseAwait
  onGameSceneClick(e: FYGE.MouseEvent) {
    if (this.isPause) return
    return new Promise<void>(resolve => {
      const screenRY = this.getCurrRabbitYFromScreen()
      const {
        stageX
      } = e
      const {
        x: rax,
        y: ray
      } = this.rabbitPos
      const {
        rabbitBoxMap,
        rabbitContainer,
        rabbitCloneContainer
      } = this
      const {
        x: rx,
        y: ry
      } = rabbitContainer
      const dir = this.dir = (stageX > 375 ? 1 : -1)
      const _willX = rax + dir
      const willX = (_willX + GameConfig.boxColNum) % GameConfig.boxColNum
      let shouldY = ray - (screenRY < GameConfig.DecisionLine[0] ? 0 : 1)
      // console.log('========',shouldY, '\n===stageX===\n', stageX)
      let landPlatform: Platform
      while(!(landPlatform = rabbitBoxMap[shouldY][willX])) {
        shouldY += 1
        if (shouldY >= rabbitBoxMap.length) {
          break
        }
      }

      // TODO 如果找不到 下去的平台 直接不让跳吧
      if (!landPlatform) {
        resolve()
        return
      }

      // 这里之前提前改，否则会有时序问题
      this.rabbitPos = {
        x: willX,
        y: shouldY
      }

      const afterCb = this.onPlatformLand(landPlatform)

      // FYGE.Tween.removeTweens(rabbitContainer)
      if (rax == 4) {
        rabbitCloneContainer.visible = true
        rabbitCloneContainer.position.set(rabbitContainer.x - 750, rabbitContainer.y)
      } else if (rax == 0) {
        rabbitCloneContainer.visible = true
        rabbitCloneContainer.position.set(rabbitContainer.x + 750, rabbitContainer.y)
      }
      const sp = shouldY - ray
      const downTime = 200 // 200 * (sp > 0 ? sp : 1)
      FYGE.Tween.get(rabbitContainer, {
        onChange: () => {
          // rabbitContainer.x = (rabbitContainer.x + 750) % 750
          rabbitCloneContainer.x = rabbitContainer.x - 750 * (rax == 0 ? -1 : 1)
        }
      })
        .to({
          x: _willX * offsetXLen
        }, 400 + downTime)
      
      FYGE.Tween.get(rabbitContainer, {
        onChange: () => {
          rabbitCloneContainer.y = rabbitContainer.y
        }
      })
        .to({
          y: Math.min(ry + sp * offsetYLen, ry) - 50
        }, 400, FYGE.Ease.quadOut)
        .to({
          y: ry + sp * offsetYLen
        }, downTime, FYGE.Ease.quadIn)
        // .wait(300)
        .call(() => {
          // TODO 这里算的有点问题，是不是上面onChang 事件的问题呢？
          if (rabbitCloneContainer.x >= -10 && rabbitCloneContainer.x <= 750) {
            this.rabbitContainer = rabbitCloneContainer
            this.rabbitCloneContainer = rabbitContainer
          }

          afterCb()
          resolve()
        })
        
    })
  }

  /**
   * 平台判断
   * @param platform 
   */
  onPlatformLand(platform: Platform) {
    let noop = () => {}

    platform.onLand((type: number, s: number, cb: Function) => {
      const extraConfig = Tools.PageData.extraFunc
        ? Tools.PageData.extraFunc(type)
        : {
          extraScore: 0,
          done: false,
          extraTime: 0,
          landCb: () => {}
        }
      
      const isDone = type == 3 && extraConfig.done

      // pre 去做动作吧
      if (isDone) {
        this.onShowRabbitAni(RabbitActionMap.Jump, RabbitActionMap.Fail)
      } else if (type == 2) {
        this.onShowRabbitAni(RabbitActionMap.Jump, RabbitActionMap.Smile)
      } else {
        this.onShowRabbitAni(RabbitActionMap.Jump, RabbitActionMap.Normal)
      }

      // 落地的回调
      noop = () => {
        cb()
        extraConfig.landCb(this.rabbitContainer, this.rabbitCloneContainer)
        if (isDone) {
          return this.onGameOver()
        } else if (type == 2) {
          this.double = 2
          this.SpClockIns.onShowCountDown(() => {
            this.double = 1
          }, extraConfig.extraTime)
        }
        this.onAddScore(s + extraConfig.extraScore || 0)
      }
    })

    return noop
  }

  /**
   * 添加分数
   * @param s 
   */
  onAddScore(s: number) {
    this.ScoreBoardIns.score += s * this.double
    const currScore = this.ScoreBoardIns.score
    const speedScoreMap = Tools.PageData.gameDiff

    const currConfig = speedScoreMap.find(it => currScore < it.score) || {speed: 200}
    GameConfig.speed = currConfig.speed
    console.info('游戏速度:', GameConfig.speed, currConfig)
  }

  GameJudgeFrame() {
    if (this.isPause) return
    // 当前兔子在屏幕上的 y
    const currRabbitOffsetY = this.GameReelContainer.y + this.rabbitContainer.y - layers.stageOffsetY
    if (currRabbitOffsetY >= GameConfig.DecisionLine[1]) {
      this.onGameOver()
    }
  }

  render() {
    const initReelOffsetY = (mapHeight - layers.stageOffsetY - layers.stageHeight) * -1
    console.log('初始化卷轴 offsetY:', initReelOffsetY)
    return (
      <DreamContainer ref={el => this.SceneContainer = el} onEnterFrame={this.GameJudgeFrame.bind(this)} onClick={this.onGameSceneClick.bind(this)}>
        <DreamSprite src={RES.getRes('GSB.png')} />
        <DreamContainer onEnterFrame={this.onEnterFrame.bind(this)} className="GameReelContainer" ref={el => this.GameReelContainer = el}
          inlineProps={{
            y: initReelOffsetY
          }}
        >
          <DreamContainer className="GameMapContainer" ref={el => {
            this.GameMapContainer = el
          }}>
          </DreamContainer>
        </DreamContainer>

        
        <ScoreBoard ref={(el: ScoreBoard) => this.ScoreBoardIns = el} />

        <SpClock ref={(el: SpClock) => this.SpClockIns = el} />

        <DreamContainer ref={el => {
          this.GuidanceCont = el
        }}></DreamContainer>
      </DreamContainer>
    )
  }
}