import {
  _decorator,
  UITransform,
  Node,
  Label,
  Camera,
  director,
  math,
  Prefab,
  PhysicsSystem2D,
} from "cc";
import { Joystick } from "./Joystick";
import { Snake } from "./Snake";
import { FastBtn } from "./FastBtn";
import { Animal } from "./Animal";
import { FondManger } from "./FondManger";
import { Config } from "./Config";
import { Global } from "./Global";
import { GameState, Events } from "./Enums";
import { PoolManager } from "./PoolManager";
import { showToast } from "../../../Module/UIFast";
import Scene from "../../../Module/Scene";

const { ccclass, property } = _decorator;

@ccclass("MainGame")
export class MainGame extends Scene {

  static bundle: string = "MainGame";
  static skin: string = "MainGame";

  @property(Prefab)
  private animalPrefab: Prefab = null;

  @property({
    displayName: "最多食物",
    tooltip: "地图上随机产生食物，如果超过当前值不在产生。"
  })
  maxFood: number = 200;

  @property({
    displayName: "NPC数量",
    tooltip: "当前游戏最大NPC角色"
  })
  private maxAnimal: number = 20;

  @property({ type: Joystick, displayName: "摇杆" }) joystick: Joystick = null;

  @property({
    type: FastBtn,
    displayName: "快进按钮"
  })
  private fashBtn: FastBtn = null;

  @property(UITransform)
  private uiBg: UITransform = null;

  @property(Snake)
  private snake: Snake = null;

  @property(FondManger)
  fondManger: FondManger = null;

  @property(Node)
  private animalNode: Node = null;

  @property(Label)
  private LTips: Label = null;

  @property(Camera)
  camera: Camera = null;

  private state: GameState = GameState.READY;
  private rebirthSum: number = 0;

  private static _ins: MainGame = null;
  static get ins(): MainGame {
    return MainGame._ins;
  }

  onLoad() {

    MainGame._ins = this;
    PhysicsSystem2D.instance.enable = true;

    Global.MAP_WIDTH = this.uiBg.contentSize.x;
    Global.MAP_HIGHT = this.uiBg.contentSize.y;

    // 初始化墙壁
    const wallTop = this.uiBg.node.getChildByName("WALL_TOP");
    wallTop.setPosition(0, Global.MAP_HIGHT / 2);
    wallTop.getComponent(UITransform).width = Global.MAP_WIDTH;

    const wallBottom = this.uiBg.node.getChildByName("WALL_BOTTOM");
    wallBottom.setPosition(0, -Global.MAP_HIGHT / 2);
    wallBottom.getComponent(UITransform).width = Global.MAP_WIDTH;

    const wallLeft = this.uiBg.node.getChildByName("WALL_LEFT");
    wallLeft.setPosition(-Global.MAP_WIDTH / 2, 0);
    wallLeft.getComponent(UITransform).height = Global.MAP_HIGHT;

    const wallRight = this.uiBg.node.getChildByName("WALL_RIGHT");
    wallRight.setPosition(Global.MAP_WIDTH / 2, 0);
    wallRight.getComponent(UITransform).height = Global.MAP_HIGHT;

    // 初始化蛇
    const skinId = Global.skinId;
    this.snake.init(0, 0, 0, skinId, 0.2,
      Config.SKIN_STYLE[skinId].len,
      Config.SKIN_STYLE[skinId].taitLen, 0);

    // 初始化食物和NPC
    this.fondManger.init(this.maxFood);
    this.initAnimal(this.maxAnimal);

    // 设置游戏状态
    this.setGameState(GameState.PLAY);

    // 注册事件
    director.on(Events.showGOver, this.showGOver, this);
    director.on(Events.setGameState, this.setGameState, this);
  }

  onDestroy() {
    MainGame._ins = null;

  }

  update(dt: number) {
    if (this.state == GameState.READY) return;

    // 更新UI提示
    this.LTips.string = `长度：${this.snake.getSnakeLen()}  金币：${this.snake.getMoneySum()}`;

    // 更新蛇的移动
    this.snake.setAngle((360 - this.joystick.angle) % 360);
    if (this.fashBtn.isFash) {
      this.snake.running(dt);
    } else {
      this.snake.move(dt);
    }

    // 更新NPC移动
    this.animalNode.children.forEach(child => {
      child.getComponent(Animal)?.autoMove(dt);
    });
  }

  onPause() {
    this.setGameState(GameState.PAUSE);
    console.log("pause");
  }

  setGameState(state: GameState) {
    this.state = Number(state);
    switch (this.state) {
      case GameState.READY:
        break;
      case GameState.PLAY:
        director.resume();
        break;
      case GameState.PAUSE:
        director.pause();
        break;
      case GameState.OVER:
        this.rebirthSum++;
        if (this.rebirthSum > 2) return this.showGOver();
        showToast("你已死亡！");
        break;
      case GameState.WIN:
        director.pause();
        console.log("win", this.snake.getSnakeLen(), this.snake.getMoneySum());
        break;
      case GameState.QUIT:
        director.resume();
        director.loadScene("GameMain");
        break;
      default:
        console.log("err");
    }
  }

  play() {
    if (this.state == GameState.OVER) {
      showToast("你已复活！");
      this.state = GameState.PLAY;
      this.snake.reInit(
        math.randomRangeInt(-(Global.MAP_WIDTH / 2 - 50), Global.MAP_WIDTH / 2 - 50),
        math.randomRangeInt(-(Global.MAP_HIGHT / 2 - 50), Global.MAP_HIGHT / 2 - 50)
      );
      // this.showGOver();
    }
  }

  showGOver() {
    console.log("showGOver", this.snake.getSnakeLen(), this.snake.getMoneySum());
  }

  private* _getItemGenerator(count: number) {
    for (let i = 0; i < count; i++) {
      yield this._initItem(i);
    }
  }

  private _initItem(index: number) {
    const node = PoolManager.instance.getNode(this.animalPrefab, this.animalNode);
    const x = math.randomRangeInt(-(Global.MAP_WIDTH / 2 - 50), Global.MAP_WIDTH / 2 - 50);
    const y = math.randomRangeInt(-(Global.MAP_HIGHT / 2 - 50), Global.MAP_HIGHT / 2 - 50);
    // const skinId = math.randomRangeInt(0, Config.SKIN_STYLE.length);
    const skinId = 0;

    node.getComponent(Animal)?.init(
      x, y,
      math.randomRangeInt(0, 360),
      skinId, 0.2,
      Config.SKIN_STYLE[skinId].len,
      Config.SKIN_STYLE[skinId].taitLen,
      index + 1
    );
  }

  async initAnimal(count: number) {
    await this.executePreFrame(this._getItemGenerator(count), 1);
  }

  executePreFrame(generator: Generator, frameTime: number): Promise<boolean> {
    return new Promise((resolve, reject) => {
      const iter = generator;

      const next = () => {
        const startTime = new Date().getTime();
        for (let result = iter.next(); ; result = iter.next()) {
          if (!result || result.done) {
            resolve(true);
            return;
          }
          if (new Date().getTime() - startTime > frameTime) {
            this.scheduleOnce(() => {
              next();
            });
            return;
          }
        }
      };

      next();
    });
  }
}