import { Assets, Container, Point, PointData, Sprite } from "pixi.js";
import { GameConfig, mapSize } from "@/pages/GamePage/config/Config.ts";

import { Circle } from "detect-collisions";
import { collisionSys } from "@/pages/GamePage/GamePage.tsx";
import { Tween } from "@/pages/GamePage/tween";
import { IReactionDisposer, reaction } from "mobx";
import gameStore from "@/store/gameStore.ts";

export class Snake extends Container {
  head: Sprite = null;
  bodyArr: Sprite[] = [];

  bodyScale: number = 1;
  energy: number = 0;

  // 位置相关
  private ready: boolean = false;
  isLife: boolean = true;

  speed: number = 166;

  dir: Point = new Point(1, 0);

  collider: Circle = null;
  negCollider: Circle = null;

  levelDisposer: IReactionDisposer = null;

  init() {
    this.initUI();
    this.initPhy();
    this.levelDisposer = reaction(
      () => gameStore.gameInfo.level,
      (level) => {
        const { levelCfg } = GameConfig;
        const { skin } = levelCfg[level];
        this.setSkin(skin);
      },
      { fireImmediately: true }
    );
  }

  destroy() {
    this.levelDisposer();
    super.destroy();
  }

  setSkin(skin: string) {
    this.bodyScale = 1;
    this.head.texture = Assets.get(`蛇/${skin}/head.png`);
    this.bodyArr.forEach((body, i) => {
      body.texture = Assets.get(`蛇/${skin}/body${i % 2}.png`);
    });
  }

  initUI() {
    this.ready = false;

    this.bodyArr.forEach((body) => {
      body.removeFromParent();
    });

    this.energy = 0;
    this.scale = 1;
    this.bodyArr.length = 0;

    // 设置头部
    this.head = this.addChild(new Sprite(Assets.get("蛇/初级/head.png")));
    this.head.angle = 0;
    this.head.position.set(375, 375);
    this.head.anchor.set(0.5, 0.5);

    // 创建身体节点
    this.addEnergy(GameConfig.initEnergy);

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

  initPhy() {
    const p = this.head.getGlobalPosition();
    this.collider = collisionSys.createCircle(p, 46 / 2, {
      userData: {
        snake: true,
        isNeg: false,
      }
    });
    this.collider.setScale(this.bodyScale, this.bodyScale);

    this.negCollider = collisionSys.createCircle(p, 46 / 2, {
      userData: {
        snake: true,
        isNeg: true,
      }
    });
    this.negCollider.setScale(this.bodyScale, this.bodyScale);
  }

  updatePhy() {
    const p = this.head.getGlobalPosition();
    this.collider.setPosition(p.x, p.y);
    let scale = this.bodyScale;
    if (this.magnetInfo.time) {
      scale *= GameConfig.magnetScale;
    }
    this.collider.setScale(scale, scale);

    this.negCollider.setPosition(p.x, p.y);
    this.negCollider.setScale(this.bodyScale, this.bodyScale);
  }

  /**
   * 加能量
   * 暂定加长/双倍道具卡效果不叠加
   */
  addEnergy(value: number) {
    const { growthThreshold } = GameConfig;

    this.energy = Math.max(this.energy + value, 0);

    const originLen = this.bodyArr.length;
    const len = this.energy / growthThreshold >> 0;

    if (originLen < len) {
      for (let i = originLen; i < len; i++) {
        this.grow();
      }
    } else if (originLen > len) {
      this.bodyArr
        .splice(len, originLen - len)
        .forEach((body) => {
          body.removeFromParent();
        });
    }
  }

  /**
   * 生长
   */
  private grow() {

    if (!this.isLife) return;

    let len = this.bodyArr.length;

    const { levelCfg } = GameConfig;
    const { skin } = levelCfg[gameStore.gameInfo.level];

    const newBody = new Sprite(Assets.get(`蛇/${skin}/body${len % 2}.png`));

    const pre = this.bodyArr[len - 1] || this.head;

    newBody.angle = pre.angle;
    newBody.position.set(pre.x, pre.y);
    newBody.scale.set(this.bodyScale, this.bodyScale);
    this.addChildAt(newBody, 0);
    newBody.anchor.set(0.5, 0.5);

    this.bodyArr.splice(len, 0, newBody);
  }

  positions: PointData[] = []; // 存储历史位置点
  private readonly SEGMENT_SPACING = 5; // 增加节点间距

  moveTime = 1 / 60;
  totalTime = 0;

  onUpdate(dt: number) {
    this.totalTime += dt;
    while (this.totalTime >= this.moveTime) {
      this.totalTime -= this.moveTime;
      this.move(this.moveTime);
    }

    this.updatePhy();
  }

  protected move(dt: number) {
    if (!this.ready || !this.isLife) {
      return;
    }

    const bodyLen = this.bodyArr.length;

    const headAngle = this.head.angle = Math.atan2(this.dir.y, this.dir.x) * 180 / Math.PI;

    // 更新头部位置
    const newHeadPos = this.dir
      .multiplyScalar(dt * this.speed)
      .add(this.head.position);

    if (newHeadPos.x < -10) newHeadPos.x = 750 + 10;
    if (newHeadPos.x > 750 + 10) newHeadPos.x = -10;

    if (newHeadPos.y < -10) newHeadPos.y = mapSize.height + 10;
    if (newHeadPos.y > mapSize.height + 10) newHeadPos.y = -10;

    this.head.position.set(newHeadPos.x, newHeadPos.y);
    const flipY = headAngle > 90 && headAngle < 270;
    this.head.scale.set(this.bodyScale, flipY ? -this.bodyScale : this.bodyScale);

    const space = this.SEGMENT_SPACING * this.bodyScale;

    // 存储历史位置
    this.positions.unshift(newHeadPos);
    // 确保历史位置点足够多，以容纳所有身体节点
    const requiredLength = this.bodyArr.length * space + 1;
    if (this.positions.length > requiredLength) {
      this.positions.pop();
    }

    // 更新身体节点位置
    for (let i = 0; i < bodyLen; i++) {
      const body = this.bodyArr[i];

      // 为每个节点计算一个固定的偏移量
      const offset = (i + 1) * (~~space);

      // 确保不会超出历史位置数组范围
      if (offset < this.positions.length) {
        const targetPos = this.positions[offset];

        // 计算角度
        if (offset > 0) {
          const prevPos = this.positions[offset - 1];
          body.angle = Math.atan2(
            targetPos.y - prevPos.y,
            targetPos.x - prevPos.x
          ) * 180 / Math.PI;
        }

        body.position.set(targetPos.x, targetPos.y);
        body.scale.set(this.bodyScale, this.bodyScale);
      }
    }
  }

  /****************************** 磁铁 ******************************/

  magnetInfo = {
    time: 0,
  }

  useMagnet() {
    this.clearMagnet();
    this.magnetInfo.time = GameConfig.magnetTime;
    Tween.get(this.magnetInfo)
      .to({ time: 0 }, this.magnetInfo.time)
      .call(() => {
        this.clearMagnet();
      });
  }

  clearMagnet() {
    Tween.removeTweens(this.magnetInfo);
    this.magnetInfo.time = 0;
  }

}
