import {
    _decorator,
    Collider,
    Color,
    easing,
    ICollisionEvent,
    Input,
    input,
    instantiate,
    ITriggerEvent,
    Label,
    lerp,
    Light,
    MeshRenderer,
    Node,
    ParticleSystem,
    PhysicsSystem,
    PointToPointConstraint,
    Prefab,
    RigidBody,
    Tween,
    tween,
    v3,
    Vec3,
} from "cc";
import Scene from "db://assets/Module/Scene";
import { Wall } from "./Wall";
import { sleep } from "../../Utils/Utils";
import GameMgr from "../../GameMgr";
import { AudioMgr } from "../../../core_tgx/base/AudioMgr";
import { BgAndColor } from "./BgAndColor";

const { ccclass, property } = _decorator;


@ccclass("MainGame")
export class MainGame extends Scene {
    static bundle: string = "MainGame";
    static skin: string = "MainGame";
    // static group: string[] = ["MainGame"];

    @property(Node) linkPoint: Node = null;
    @property(Node) line: Node = null;

    @property(Node) player: Node = null;
    playerBody: RigidBody = null;
    playerConstraint: PointToPointConstraint = null;

    @property(Node) initWall: Node = null;

    @property(Prefab) wallPrefab: Prefab = null;
    @property(Prefab) wallPrefab2: Prefab = null;

    @property(Prefab) crushPrefab: Prefab = null;

    @property(Light) light: Light = null;

    @property({ type: Label, group: "UI" }) scoreLabel: Label = null;
    @property({ type: Node, group: "UI" }) hitTip: Node = null;

    @property({ type: Node }) bg: Node = null;
    @property({ type: [BgAndColor], visible: true, displayName: `背景和颜色` })
    private bgAndColor: BgAndColor[] = [];

    private _score: number = 0;

    set score(value: number) {
        this._score = value;
        this.scoreLabel.string = value.toString();
    }

    get score() {
        return this._score;
    }

    isTouch = false;

    wallArr: Node[] = [];

    maxZ = 0;
    scoreRadio = 1;

    onLoad() {

        PhysicsSystem.instance.autoSimulation = true;

        const playerCollider = this.player.getComponent(Collider);
        playerCollider.on("onCollisionEnter", this.onPlayerCollision, this);
        playerCollider.on('onTriggerEnter', this.onTriggerEnter, this);

        this.mainColor = this.bgAndColor.splice(0, 1)[0];
        this.bg.getComponent(MeshRenderer).material.setProperty("mainTexture", this.mainColor.bg);
        this.light.color = Color.clone(this.mainColor.color);
        this.lightColor = Color.clone(this.mainColor.color);

        this.playerBody = this.player.getComponent(RigidBody);
        this.playerConstraint = this.linkPoint.getComponent(PointToPointConstraint);
    }

    async start() {

        this.wallArr[4] = this.initWall;
        for (let i = 0; i <= 3; i++) {
            this.wallArr[i] = this.createCube(4 - i);
        }

        for (let i = 5; i <= 11; i++) {
            this.wallArr[i] = this.createCube();
        }

        // await sleep(0.5);
        input.on(Input.EventType.TOUCH_START, this.onTouchStart, this);
        input.on(Input.EventType.TOUCH_END, this.onTouchEnd, this);
        input.on(Input.EventType.TOUCH_CANCEL, this.onTouchEnd, this);
    }

    onDestroy() {
        input.off(Input.EventType.TOUCH_START, this.onTouchStart, this);
        input.off(Input.EventType.TOUCH_END, this.onTouchEnd, this);
        input.off(Input.EventType.TOUCH_CANCEL, this.onTouchEnd, this);

        this.linkPoint.destroy();
        // this.playerConstraint.connectedBody = null;
        // this.playerConstraint.enabled = false;
    }


    mainColor: BgAndColor = null;

    randomMainColor() {
        // const index = Math.floor(Math.random() * this.bgAndColor.length);
        // this.mainColor = this.bgAndColor.splice(index, 1, this.mainColor)[0];

        this.mainColor = this.bgAndColor.splice(0, 1)[0];
        this.bgAndColor.push(this.mainColor);
        return this.mainColor;
    }

    isOver: boolean = false;

    async gameOver() {
        this.isOver = true;

        await sleep(1.5);

        await GameMgr.ins.submit(this.score);

    }

    scoreRadioFunc = () => {
        this.scoreRadio = 1;
    }

    onTriggerEnter(event: ITriggerEvent) {
        const { otherCollider, selfCollider } = event;

        const otherName = otherCollider.node.name;

        if (otherName == "光幕") {
            otherCollider.node.active = false;
            this.changeMainColor();
        } else if (otherName == "圆环") {
            otherCollider.node.active = false;
            this.scoreRadio = 2;
            this.unschedule(this.scoreRadioFunc);
            this.scheduleOnce(this.scoreRadioFunc, 5);
            tween(this.hitTip)
                .set({ active: true })
                .delay(2)
                .set({ active: false })
                .start();

            const crushNode = instantiate(this.crushPrefab);
            this.node.addChild(crushNode);
            crushNode.setPosition(otherCollider.node.worldPosition);
            const particleSys = crushNode.getComponent(ParticleSystem);
            particleSys.play();
            this.scheduleOnce(() => {
                crushNode.destroy();
            }, particleSys.duration);
        }
    }

    onPlayerCollision(event: ICollisionEvent) {
        if (this.isOver) return;

        const { otherCollider, selfCollider } = event;

        const otherGroup = otherCollider.getGroup();

        if (otherGroup === PhysicsSystem.PhysicsGroup['Wall']) {
            // 死了
            this.playerBody.enabled = false;
            this.player.active = false;

            const crushNode = instantiate(this.crushPrefab);
            this.node.addChild(crushNode);
            crushNode.setPosition(this.player.position);
            const particleSys = crushNode.getComponent(ParticleSystem);
            particleSys.play();
            this.scheduleOnce(() => {
                crushNode.destroy();
            }, particleSys.duration);

            this.gameOver();
        }

    }

    lightTween: Tween<any> = null;
    lightColor: Color = null;

    changeMainColor() {
        this.randomMainColor();
        const { bg, color } = this.mainColor;

        this.bg.getComponent(MeshRenderer).material.setProperty("mainTexture", bg);
        this.lightTween?.removeSelf();
        this.lightTween = tween({ radio: 0 })
            .to(1, { radio: 1 }, {
                easing: easing.quadInOut,
                onUpdate: (target, ratio) => {
                    this.lightColor.lerp(color, ratio);
                    this.light.color = this.lightColor;
                }
            })
            .start();
    }

    createCube(z = this.wallArr[this.wallArr.length - 1].position.z - 1) {
        const singular = z % 2 == 0;
        const newGroup = instantiate(singular ? this.wallPrefab : this.wallPrefab2);
        this.node.addChild(newGroup);
        newGroup.setPosition(v3(0, Math.random(), z));
        return newGroup;
    }

    onTouchStart(event: any) {
        if (this.isOver) return;

        this.isTouch = true;

        AudioMgr.ins.playOneShot("audio/点击音效", {
            bundleName: "common",
        });

        this.line.active = true;

        const pPos = this.player.position;
        const targetZ = pPos.z - 0.5;
        const index = this.wallArr[0].position.z - Math.round(targetZ);
        const y = this.wallArr[index].getComponent(Wall).topY;

        const linkPos = v3(0, y, targetZ);
        this.linkPoint.setPosition(linkPos);

        // 计算角度
        const dir = Vec3.subtract(new Vec3(), linkPos, pPos);
        const angle = Math.atan(dir.z / dir.y) * 180 / Math.PI;
        this.player.setRotationFromEuler(angle, 0, 0);

        // 重新设置连接点
        const constraintY = Vec3.distance(pPos, linkPos);
        this.playerConstraint.pivotB = v3(0, constraintY, 0);
        this.playerConstraint.enabled = true;

        const velocityMultiplier = lerp(0, 1, this.getDifficultyRatio());

        const linear = v3();
        this.playerBody.getLinearVelocity(linear);
        linear.multiplyScalar(velocityMultiplier);
        this.playerBody.setLinearVelocity(linear);

        const angular = v3();
        this.playerBody.getAngularVelocity(angular);
        angular.multiplyScalar(velocityMultiplier);
        this.playerBody.setAngularVelocity(v3());
    }

    onTouchEnd(event: any) {
        if (this.isOver) return;
        if (!this.isTouch) return;

        this.isTouch = false;
        this.line.active = false;
        this.playerConstraint.enabled = false;
    }

    checkFrame = 0;
    propCFG = [
        {
            lastZ: 0,
            create: false,
            interval: 50,
            funcName: "createLight",
        },
        {
            lastZ: 0,
            create: false,
            interval: 25,
            funcName: "createRing",
        },
    ]

    checkWall() {
        this.checkFrame = 0;
        const pPos = this.player.position;
        const pz = Math.round(pPos.z);
        const index = this.wallArr[0].position.z - pz;

        const propLen = this.propCFG.length;
        let create = false;
        for (let i = 0; i < propLen; i++) {
            const { lastZ, interval } = this.propCFG[i];
            const dz = lastZ - pz;
            if (create) {
                this.propCFG[i].lastZ = pz;
            } else if (dz >= interval) {
                create = true;
                this.propCFG[i].create = true;
                this.propCFG[i].lastZ = pz;
            }
        }

        const createCount = index - 4;
        for (let i = 0; i < createCount; i++) {
            const wall = this.wallArr.shift();
            wall.setPosition(0, Math.random(), this.wallArr[this.wallArr.length - 1].position.z - 1);
            this.wallArr.push(wall);

            const wallTs = wall.getComponent(Wall);
            wallTs.reset();

            for (let i = 0; i < propLen; i++) {
                const { funcName, create } = this.propCFG[i];
                if (create) {
                    this.propCFG[i].create = false;
                    wallTs[funcName]();
                    break;
                }
            }
        }
    }

    updateScore(addScore) {
        this.score += ~~((addScore * 15) * this.scoreRadio);
        if (this.score > 9999) {
            this.score = 9999;
            PhysicsSystem.instance.enable = false;
            this.gameOver();
        }
    }

    /**
     * 获取难度比例，根据分数返回难度比例，最大到5000，难度最大
     * @returns {number} 返回计算后的难度比例值
     */
    getDifficultyRatio(): number {
        return Math.min(this.score, 5000) / 5000;
    }

    protected lateUpdate(dt: number) {
        // PhysicsSystem.instance.physicsWorld.syncSceneToPhysics();
        //
        // // PhysicsSystem.instance.fixedTimeStep = dt;
        // // PhysicsSystem.instance.update(dt);
        // PhysicsSystem.instance.step(1/60, dt);
        //
        // PhysicsSystem.instance.physicsWorld.emitEvents();
        // PhysicsSystem.instance.physicsWorld.syncAfterEvents();
    }

    update(dt: number) {

        if (this.isOver) return;

        const pz = -this.player.position.z;
        const addScore = pz - this.maxZ;

        if (pz > 0) {
            this.maxZ += addScore;
            this.updateScore(addScore);
        }

        this.checkFrame++;
        // 检测是否需要生成墙
        if (this.checkFrame >= 20) {
            this.checkWall();
        }

        if (this.isTouch) {
            const lineScale = Vec3.distance(this.player.position, this.linkPoint.position);
            this.line.setScale(1, lineScale / 2, 1);

            this.playerBody.applyForce(v3(0, 0, -10));
        } else {
            // this.playerBody.applyForce(v3(0, 0, -10));
        }

    }

}
