import {
    _decorator,
    Collider,
    Color,
    easing, EPhysicsDrawFlags,
    ICollisionEvent,
    Input,
    input,
    instantiate,
    ITriggerEvent,
    Label, lerp,
    Light, MeshRenderer,
    Node,
    ParticleSystem, ParticleSystemComponent,
    PhysicsSystem,
    PointToPointConstraint,
    Prefab,
    RigidBody, Texture2D,
    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";

const {ccclass, property} = _decorator;


@ccclass(`BgAndColor`)
class BgAndColor {
    @property({type: Texture2D})
    bg: Texture2D = null;

    @property({type: Color})
    color: Color = Color.clone(Color.WHITE);
}


@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 _propScore: number = 0;
    set propScore(value: number) {
        this._propScore = value;
        this.updateScore();
    }

    get propScore() {
        return this._propScore;
    }

    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;

    onLoad() {


        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_MOVE, this.onTouchMove, this);
        input.on(Input.EventType.TOUCH_END, this.onTouchEnd, this);
        input.on(Input.EventType.TOUCH_CANCEL, this.onTouchEnd, this);
    }

    onDestroy() {
        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];
        return this.mainColor;
    }

    isOver: boolean = false;

    async gameOver() {
        this.isOver = true;

        await sleep(1.5);

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

    }

    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.propScore += 10;
            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) {
        console.log(event)
        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;

        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;

        console.log(this.getDifficultyRatio());

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

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

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

    onTouchMove(event: any) {

    }

    onTouchEnd(event: any) {
        if (this.isOver) 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: 10,
            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() {
        this.score = ~~(this.maxZ * 1115) + this.propScore;

        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;
    }

    update(dt: number) {

        if (this.isOver) return;

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

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

        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));
        }

    }


}
