import {
    _decorator, Collider, CollisionEventType, color, Color, easing, ICollisionEvent,
    Input,
    input,
    instantiate, ITriggerEvent, Label, Light,
    Node, ParticleSystem, PhysicsGroup, PhysicsSystem,
    PointToPointConstraint,
    Prefab,
    primitives,
    RigidBody, Tween, tween,
    v3,
    Vec3,
} from "cc";
import Scene from "db://assets/Module/Scene";
import { ImprovedNoise } from "../../Utils/ImprovedNoise";
import { Wall } from "./Wall";
import { sleep } from "../../Utils/Utils";
import { MainColor, randomManColor } from "./Config";

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(Node) crushNode: Node = null;

    @property(Light) light: Light = null;

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

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

    get score() {
        return this._score;
    }

    isTouch = false;

    pNoise: ImprovedNoise = new ImprovedNoise();

    wallArr: Node[] = [];

    onLoad() {

        this.lightColor = Color.clone(this.light.color);

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

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

        this.player.getComponent(Collider)
            .on("onCollisionEnter", this.onPlayerCollision, this);


        this.player.getComponent(Collider)
            .on('onTriggerEnter', this.onTriggerEnter, this);


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

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

    }

    async start() {

    }

    isOver: boolean = false;

    async gameOver(success) {
        this.isOver = true;

        await sleep(3);

    }

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

        const otherName = otherCollider.node.name;

        if (otherName == "光幕") {
            otherCollider.node.active = false;
            const color = randomManColor();
            this.changeMainColor(color);
        } else if (otherName == "圆环") {
            otherCollider.node.active = false;
            this.score += 10;
        }
    }

    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.destroy();

            this.crushNode.setPosition(this.player.position);
            this.crushNode.getComponent(ParticleSystem)
                .play();

            this.gameOver(false);
        }

    }

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

    changeMainColor(color: Color) {
        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 newGroup = instantiate(this.wallPrefab);
        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;

        const velocityMultiplier = 0.1;

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

    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: 20,
            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;
                }
            }
        }
    }

    maxZ = 0;

    update(dt: number) {
        const pPos = this.player.position;
        const pz = Math.abs(Math.round(pPos.z));

        if (pz > this.maxZ) {
            this.score += pz - this.maxZ;
            this.maxZ = pz;
        }

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

    }


}
