import {
    _decorator,
    Collider2D,
    Component,
    Contact2DType, instantiate, IVec2Like,
    math,
    Node,
    PhysicsGroup,
    Prefab,
    Sprite,
    SpriteFrame,
    tween, UITransform,
    v2,
    v3, Vec2,
    Vec3,
} from "cc";
import { FoodType } from "./Common/Enums";
import { Global } from "./Global";
import { isIntersect, loadSkin } from "./utils/uitl";
import { MainGame } from "./MainGame";
import { bodyPool } from "./Manager/CommonPool";
import { Food } from "./Props/Food";

const { ccclass, property } = _decorator;

export interface IInitConfig {
    x?: number;
    y?: number;
    angle?: number;
    skinName?: string;
    scale?: number;
    bodyCount?: number;
    initEnergy?: number,
}

@ccclass("Snake")
export class Snake extends Component {

    static tag: number = 0;

    // 属性装饰器
    @property({ type: Node, displayName: "头部" }) head: Node = null;

    @property(Prefab) bodyPrefab: Prefab = null;

    // 私有成员变量
    bodyArr: Node[] = [];
    private imgHead: SpriteFrame = null;
    private imgBody1: SpriteFrame = null;
    private imgBody2: SpriteFrame = null;
    private imgTail: SpriteFrame = null;

    // 蛇的状态
    isLife: boolean = false;
    private scale: number = 0.2;
    speed: number = 300;
    private energy: number = 0;
    protected tag: number = 0;

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

    private _length: number = 0;
    get length() {
        return this._length;
    }

    set length(value: number) {
        this._length = value;
        this.node.emit("updateLength", value);
    }

    get radius() {
        return this.scale * 29;
    }

    // 初始化方法
    public async init(config: IInitConfig = {}) {

        const {
            x = 0, y = 0, angle = 0, scale = 0.5,
            skinName = "default",
            initEnergy = 5,
        } = config;

        await this.setSkin(skinName);

        this.ready = false;
        this.energy = 0;
        this.bodyArr = [];
        this.scale = scale;
        this.tag = Snake.tag++;

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

        const hw = this.imgHead.originalSize.width;
        const bw = this.imgBody1.originalSize.width;
        this.head.getComponent(UITransform).anchorX = (bw / 2) / hw;

        // 创建尾巴节点
        const tile = bodyPool.get() || instantiate(this.bodyPrefab);
        tile.angle = angle;
        tile.setPosition(x, y);
        tile.setScale(scale, scale);

        if (this.imgTail) {
            tile.getComponent(Sprite).spriteFrame = this.imgTail;
            const tw = this.imgTail.originalSize.width;
            tile.getComponent(UITransform).anchorX = (bw / 2) / tw;
        } else {
            tile.getComponent(Sprite).spriteFrame = this.imgBody1;
        }

        const collider = tile.getComponent(Collider2D);
        collider.tag = this.tag;

        this.node.addChild(tile);
        this.bodyArr.push(tile);

        this.length = 2;

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

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

    async setSkin(skinName: string) {
        const skin = await loadSkin(skinName);
        this.imgHead = skin.getSpriteFrame("head");
        this.imgBody1 = skin.getSpriteFrame("body1");
        this.imgBody2 = skin.getSpriteFrame("body2") || this.imgBody1;
        this.imgTail = skin.getSpriteFrame("tail");
    }

    onEnable() {
        const collider = this.head.getComponent(Collider2D);
        collider.on(Contact2DType.BEGIN_CONTACT, this.onBeginHead, this);

        const eye = this.head.getChildByName("范围").getComponent(Collider2D);
        eye.on(Contact2DType.BEGIN_CONTACT, this.onBeginEye, this);
    }

    onDisable() {
        const collider = this.head.getComponent(Collider2D);
        collider.off(Contact2DType.BEGIN_CONTACT, this.onBeginHead, this);

        const eye = this.head.getChildByName("范围").getComponent(Collider2D);
        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();
        }
    }

    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(() => {
                    if (!this.isLife) return;
                    const foodTs = otherCollider.getComponent(Food);
                    this.addEnergy(foodTs.energy);
                    foodTs.recycle();
                })
                .start();
        }
    }

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


        value += this.lastRemaining;

        while (value >= growthThreshold) {
            this.grow();
            value -= growthThreshold;
            if (this.scale < 1) {
                this.scale += 0.005;
            }
        }

        this.lastRemaining = value;

        // this.speed = 600 * this.scale;
    }

    // 蛇身体生长
    private grow() {

        this.length += 1;

        let len = this.bodyArr.length;
        if (this.imgTail) len -= 1;

        const newBody = bodyPool.get() || instantiate(this.bodyPrefab);

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

        newBody.angle = pre.angle;
        newBody.setPosition(pre.position);
        newBody.setScale(this.scale, this.scale);
        this.node.addChild(newBody);

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

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

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

    setAngle(angle: number) {
        this.isLife && (this.head.angle = angle);
    }

    isFast = false;
    private positions: Vec3[] = []; // 存储历史位置点
    private readonly HISTORY_LENGTH = 100; // 增加历史点数量
    private readonly SEGMENT_SPACING = 8; // 增加节点间距

    moveTime = 1 / 60;
    totalTime = 0;
    moveScale = 1;

    onUpdate(dt: number) {
        let moveScale = this.moveScale;
        if (this.isFast) {
            moveScale += 1;
        }

        this.totalTime += dt * moveScale;
        while (this.totalTime >= this.moveTime) {
            this.totalTime -= this.moveTime;
            this.move(this.moveTime);
        }
    }

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

        // 更新头部位置
        const newHeadPos = this.getNewPos(
            this.head.angle,
            dt,
            this.head.getPosition()
        );
        this.head.setPosition(newHeadPos);
        this.head.setScale(this.scale, this.scale);

        const space = ~~(this.SEGMENT_SPACING * this.scale);

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

        // 更新身体节点位置
        for (let i = 0; i < this.bodyArr.length; 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 / 3.1415927;
                }

                body.setPosition(targetPos);
                body.setScale(this.scale, this.scale);
                body.setSiblingIndex(this.bodyArr.length - i);

                // body.active = isIntersect(
                //     targetPos,
                //     this.head.getPosition(),
                //     this.vw,
                //     this.vh
                // );
            }
        }

        // 边界检查
        const mapHalfWidth = Global.MAP_WIDTH / 2;
        const mapHalfHeight = Global.MAP_HEIGHT / 2;
        if (
            newHeadPos.x <= -mapHalfWidth
            || newHeadPos.x >= mapHalfWidth
            || newHeadPos.y <= -mapHalfHeight
            || newHeadPos.y >= mapHalfHeight
        ) {
            this.death();
        }

    }

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

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

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

        const len = this.bodyArr.length;
        const foodArr = this.bodyArr.map((body) => {
            body.removeFromParent();
            bodyPool.put(body);

            return {
                x: body.position.x + Math.random() * 10 - 5,
                y: body.position.y + Math.random() * 10 - 5,
                energy: ~~(this.energy / len),
            };
        });

        MainGame.ins.fondManger.initFoods(foodArr);
    }

    protected getNewPos(angle: number, dt: number, currentPos: Vec3, speed: number = this.speed): Vec3 {
        const direction = this.getVelocity(angle);
        return v3(currentPos.x + dt * direction.x * speed, currentPos.y + dt * direction.y * speed, 0);
    }

    getVelocity(angle = this.head.angle) {
        const radian = angle / 180 * Math.PI;
        return v2(Math.cos(radian), Math.sin(radian));
    }

    /**
     * 护盾时间
     * 假如有两个护盾同时生效，则取时间大的，正是因为这样，才设计成数字
     */
    _invincibleTime: number = 0;
    get invincibleTime() {
        return this._invincibleTime;
    }

    set invincibleTime(value: number) {
        this._invincibleTime = Math.max(this._invincibleTime, value);
    }

    clearInvincible() {
        this._invincibleTime = 0;
    }

    onLoad() {

    }


}