import {
  _decorator,
  assetManager,
  Collider2D,
  Component,
  Sprite, SpriteAtlas,
  SpriteFrame,
  v2,
  v3,
  Vec3,
  Node, Contact2DType, tween, Prefab, PhysicsGroup, director,
} from "cc";
import { Events, FoodType, GameState } from "./Enums";
import { Global } from "./Global";
import { PoolManager } from "./PoolManager";
import { isIntersect } from "./uitl";
import { MainGame } from "./MainGame";
const { ccclass, property } = _decorator;

@ccclass("Snake")
export class Snake extends Component {
  // 属性装饰器
  @property({
    type: Node,
    displayName: "头部"
  })
  private Head: Node = null;

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

  // 私有成员变量
  private eye: Collider2D = null;
  private Bodys: Node[] = [];
  private imgHead: SpriteFrame = null;
  private imgBody1: SpriteFrame = null;
  private imgBody2: SpriteFrame = null;
  private imgTait1: SpriteFrame = null;
  private imgTait2: SpriteFrame = null;

  // 蛇的状态
  private isLife: boolean = false;
  private scale: number = 0.2;
  private speed: number = 600;
  private taitLen: number = 2;
  private energy: number = 0;
  private tag: number = 0;
  private moneySum: number = 0;

  // 位置相关
  private h: Vec3 = null;
  private vw: number = Global.visibleSize.width / 2 + 100;
  private vh: number = Global.visibleSize.height / 2 + 100;
  private ready: boolean = false;

  // 初始化方法
  public init(x: number, y: number, angle: number, skinId: number = 1, scale: number = 0.2, bodyCount: number = 3, taitLen: number = 1, tag: number = 0) {
    this.ready = false;
    const bundlePath = `skin/s${skinId}`;

    this.moneySum = 0;

    // 加载皮肤资源
    assetManager.loadBundle("resources", (err, bundle) => {
      if (err) {
        console.warn(err);
        return;
      }

      bundle.load(bundlePath, SpriteAtlas, (err, atlas) => {
        if (err) {
          console.warn(err);
          return;
        }

        // 初始化蛇的图片资源
        this.imgHead = atlas.getSpriteFrame("head");
        this.imgBody1 = atlas.getSpriteFrame("body1");
        this.imgBody2 = atlas.getSpriteFrame("body2");
        this.imgTait1 = atlas.getSpriteFrame("tait1");
        this.imgTait2 = atlas.getSpriteFrame("tait2");

        // 初始化属性
        this.Bodys = [];
        this.scale = scale;
        this.speed = this.speed * scale;
        this.energy = 0;
        this.taitLen = taitLen;
        this.tag = tag;

        // 设置头部
        this.Head.angle = 0;
        this.Head.setPosition(x, y);
        this.Head.setScale(scale, scale);
        this.Head.getComponent(Sprite).spriteFrame = this.imgHead;

        // 创建身体节点
        for (let i = 0; i < bodyCount; i++) {
          const body = PoolManager.instance.getNode(this.bodyPrefab, this.node);
          const collider = body.getComponent(Collider2D);
          if (collider) {
            collider.tag = this.tag;
          }

          body.angle = angle;
          body.setPosition(-9999, -9999);
          body.setScale(scale, scale);

          // 设置身体部分的贴图
          if (i < bodyCount - taitLen) {
            body.getComponent(Sprite).spriteFrame = i % 2 == 0 ? this.imgBody2 : this.imgBody1;
          } else {
            body.getComponent(Sprite).spriteFrame = i == bodyCount - 1 ? this.imgTait2 : this.imgTait1;
          }

          body.active = false;
          this.Bodys.push(body);
        }

        this.isLife = true;
      });
      this.ready = true;
    });
  }

  onEnable() {
    const collider = this.Head.getComponent(Collider2D);
    collider && collider.on(Contact2DType.BEGIN_CONTACT, this.onBeginHead, this);
    this.eye = this.Head.getComponent(Collider2D);
    this.eye && this.eye.on(Contact2DType.BEGIN_CONTACT, this.onBeginEye, this);
  }

  onDisable() {
    const collider = this.Head.getComponent(Collider2D);
    collider && collider.off(Contact2DType.BEGIN_CONTACT, this.onBeginHead, this);
    this.eye && this.eye.off(Contact2DType.BEGIN_CONTACT, this.onBeginEye, this);
  }

  // 碰撞检测
  private onBeginHead(selfCollider: Collider2D, otherCollider: Collider2D) {
    if (otherCollider.group === PhysicsGroup["Body"] && otherCollider.tag != this.tag) {
      this.death();
    }
  }

  private onBeginEye(selfCollider: Collider2D, otherCollider: Collider2D) {
    if (otherCollider.group === PhysicsGroup["Prop"]) {
      const foodType = otherCollider.tag;

      // 食物吃掉的动画
      tween(otherCollider.node)
        .to(0.3, {
          position: this.Head.getPosition(),
          scale: v3(0, 0)
        })
        .call(() => {
          PoolManager.instance.putNode(otherCollider.node);

          if (foodType == FoodType.FOOD) {
            this.addEnergy(1);
          } else if (foodType == FoodType.MONEY) {
            this.moneySum += 1;
          }

          // 生成新的食物
          if (MainGame.ins.fondManger.getFoodSum() < MainGame.ins.maxFood) {
            MainGame.ins.fondManger.setFood();
          }
        })
        .start();
    }
  }

  // 能量与成长
  private addEnergy(value: number) {
    this.energy += value;
    const growthThreshold = Math.floor(10 * this.scale);

    if (this.energy >= growthThreshold) {
      this.grow();
      this.energy -= growthThreshold;

      if (this.scale < 0.8) {
        this.scale += 0.005;
      }
      this.speed = 600 * this.scale;
    }
  }

  // 蛇身体生长
  private grow() {
    const len = this.Bodys.length;
    const newBody = PoolManager.instance.getNode(this.bodyPrefab, this.node);
    newBody.angle = this.Bodys[len - this.taitLen - 1].angle;
    newBody.setPosition(this.Bodys[len - this.taitLen - 1].getPosition());
    newBody.setScale(this.scale, this.scale);

    const isEvenBody = (len - this.taitLen) % 2 == 0;
    newBody.getComponent(Sprite).spriteFrame = isEvenBody ? this.imgBody2 : this.imgBody1;
    newBody.getComponent(Collider2D).tag = this.tag;

    newBody.active = isIntersect(
      newBody.getPosition(),
      this.Head.getPosition(),
      this.vw,
      this.vh
    );

    this.Bodys.splice(len - this.taitLen, 0, newBody);
  }

  setAngle(e) {
    this.isLife && (this.Head.angle = e);
  }

  // 移动相关方法
  public running(dt: number) {
    if (!this.isLife) return;

    // 检查边界
    this.h = this.Head.getPosition();
    const mapHalfWidth = Global.MAP_WIDTH / 2;
    const mapHalfHeight = Global.MAP_HIGHT / 2;

    if (
      this.h.x <= -mapHalfWidth ||
      this.h.x >= mapHalfWidth ||
      this.h.y <= -mapHalfHeight ||
      this.h.y >= mapHalfHeight
    ) {
      this.death();
    }

    // 更新身体节点位置
    const lastIndex = this.Bodys.length - 1;
    for (let i = lastIndex; i > 1; i--) {
      this.Bodys[i].angle = this.Bodys[i - 2].angle;
      this.Bodys[i].setPosition(this.Bodys[i - 2].getPosition());
      this.Bodys[i].setScale(this.scale, this.scale);
      this.Bodys[i].setSiblingIndex(lastIndex - i);

      // 只显示在视野范围内的身体节点
      this.Bodys[i].active = isIntersect(
        this.Bodys[i].getPosition(),
        this.Head.getPosition(),
        this.vw,
        this.vh
      );
    }

    // 更新头部位置
    this.Head.setPosition(this.getNewPos(
      this.Head.angle,
      dt,
      this.Head.getPosition(),
      2 * this.speed
    ));
    this.Head.setScale(this.scale, this.scale);
    this.Head.setSiblingIndex(lastIndex + 1);

    // 更新相机位置
    MainGame.ins.camera.node.setPosition(this.Head.getPosition());
  }

  move(dt: number) {
    // 检查是否准备就绪且存活
    if (!this.ready || !this.isLife) {
      return;
    }

    // 获取头部位置并检查边界
    this.h = this.Head.getPosition();
    const mapHalfWidth = Global.MAP_WIDTH / 2;
    const mapHalfHeight = Global.MAP_HIGHT / 2;

    // 如果超出地图边界则死亡
    if (
      this.h.x <= -mapHalfWidth ||
      this.h.x >= mapHalfWidth ||
      this.h.y <= -mapHalfHeight ||
      this.h.y >= mapHalfHeight
    ) {
      this.death();
    }

    // 更新身体节点位置
    const lastIndex = this.Bodys.length - 1;

    // 从尾部开始更新,每个节点跟随前一个节点的位置
    for (let i = lastIndex; i > 0; i--) {
      // 更新角度
      this.Bodys[i].angle = this.Bodys[i - 1].angle;

      // 更新位置
      this.Bodys[i].setPosition(this.Bodys[i - 1].getPosition());

      // 更新缩放
      this.Bodys[i].setScale(this.scale, this.scale);

      // 更新渲染顺序
      this.Bodys[i].setSiblingIndex(lastIndex - i);

      // 只显示在视野范围内的身体节点
      this.Bodys[i].active = isIntersect(
        this.Bodys[i].getPosition(),
        this.Head.getPosition(),
        this.vw,  // 视野宽度
        this.vh   // 视野高度
      );
    }

    // 更新第一个身体节点,跟随头部
    this.Bodys[0].angle = this.Head.angle;
    this.Bodys[0].setPosition(this.Head.getPosition());
    this.Bodys[0].setScale(this.scale, this.scale);
    this.Bodys[0].setSiblingIndex(lastIndex);
    this.Bodys[0].active = true;

    // 更新头部位置
    this.Head.setPosition(
      this.getNewPos(
        this.Head.angle,
        dt,
        this.Head.getPosition()
      )
    );
    this.Head.setScale(this.scale, this.scale);
    this.Head.setSiblingIndex(lastIndex + 1);

    // 更新相机位置,跟随头部
    MainGame.ins.camera.node.setPosition(this.Head.getPosition());
  }

  getSnakeLen() {
    return this.Bodys.length;
  }

  getMoneySum() {
    return this.moneySum;
  }

  reInit(e, t) {
    this.Head.angle = 0;
    this.Head.setPosition(e, t);
    this.node.active = true;
    this.isLife = true;
  }

  // 死亡处理
  public death() {
    if (!this.isLife) return;

    // 隐藏所有身体节点
    for (let i = 0; i < this.Bodys.length; i++) {
      this.Bodys[i].setPosition(-9999, -9999);
      this.Bodys[i].active = false;
    }

    this.isLife = false;
    this.node.active = false;

    // 发送游戏结束事件
    director.emit(Events.setGameState, GameState.OVER);
  }

  // 工具方法 - 计算新位置
  private getNewPos(angle: number, dt: number, currentPos: Vec3, speed: number = this.speed): Vec3 {
    const radian = angle / 180 * Math.PI;
    const direction = v2(Math.cos(radian), Math.sin(radian));

    return v3(
      currentPos.x + dt * direction.x * speed,
      currentPos.y + dt * direction.y * speed,
      0
    );
  }
}