import {
    _decorator,
    Animation,
    AudioSource,
    Button,
    EventTouch,
    input,
    Input,
    instantiate,
    Label,
    misc,
    Node,
    PhysicsSystem,
    Prefab,
    RenderTexture,
    SkeletalAnimation,
    Tween,
    tween,
    UIOpacity,
    UITransform,
    v3,
    Vec3
} from "cc";
import Scene from "db://assets/Module/Scene";
import { Quan } from "./MainGame/Quan";
import { eventTarget, MEvent } from "../../Module/MEvent";
import GameMgr from "db://assets/Scripts/GameMgr";
import { BaseScore, HAND_TYPE, LOOP_TYPE, LoopBuff } from "db://assets/Scripts/const";
import Svga from "db://assets/Component/Svga/Svga";
import { showPanel } from "db://assets/Module/UIFast";
import GameChooseHand from "db://assets/Scripts/Panels/GameChooseHand";
import ReviewPanel from "db://assets/Scripts/Panels/ReviewPanel";
import { _asyncThrottle, sleep } from "db://assets/Scripts/Utils/Utils";
import {
    LOG_TYPE,
    sendLog,
    sendWebNet,
    sendWebNetWithToken,
    WebNetName
} from "db://assets/Scripts/Utils/WebNet/WebNet";
import BackPanel from "db://assets/Scripts/Panels/BackPanel";

const {ccclass, property} = _decorator;

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

    @property({type: Prefab})
    ipPrefab: Prefab = null;

    @property(Node)
    handNode: Node = null;

    @property(Node)
    addScoreTip: Node = null;

    @property(Node)
    gameMask: Node = null;

    @property(AudioSource)
    bgMusic: AudioSource = null;

    @property({displayName: "游戏时间", group: "游戏配置"})
    totalCD = 60;
    @property({displayName: "手移动速度", group: "游戏配置"})
    handSpeed = 5;
    @property({displayName: "台子旋转速度", group: "游戏配置"})
    rotateSpeed = 50;

    @property({displayName: "铜圈", group: "圈", type: Prefab})
    cu: Prefab = null!;
    @property({displayName: "银圈", group: "圈", type: Prefab})
    ag: Prefab = null!;
    @property({displayName: "金圈", group: "圈", type: Prefab})
    au: Prefab = null!;
    @property({displayName: "翡翠圈", group: "圈", type: Prefab})
    jadeite: Prefab = null!;
    @property({displayName: "财神圈", group: "圈", type: Prefab})
    god: Prefab = null!;

    @property({displayName: "猫手", group: "手", type: Prefab})
    cat: Prefab = null!;
    @property({displayName: "龙手", group: "手", type: Prefab})
    dragon: Prefab = null!;
    @property({displayName: "人手", group: "手", type: Prefab})
    human: Prefab = null!;

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

    @property({displayName: "倒计时", group: "UI", type: Label})
    cdLabel: Label = null!;

    @property({displayName: "向上滑动", group: "UI", type: Node})
    operationTip: Node = null!;

    @property({displayName: "闹钟图标", group: "UI", type: Node}) clockIcon: Node = null!;

    @property(RenderTexture) rt: RenderTexture = null;
    @property(RenderTexture) rt3: RenderTexture = null;

    loopArr: Prefab[] = [];
    handArr: Prefab[] = [];

    _richManNum = 0;
    get richManNum() {
        return this._richManNum;
    }

    set richManNum(v) {
        this._richManNum = v;
        this.uiLayer.view["天降财神"].active = v > 0;
        this.uiLayer.view["天降财神/num"].getComponent(Label).string = v.toString();

        if (v > 0) {
            sendLog(LOG_TYPE.EXPOSURE, 16)

        }
    }

    table: Node = null; // 台子
    hand: Node = null; // 手

    rongNum = 0; // 套中的个数
    _score = 0;
    scoreTween = null;

    get score() {
        return this._score;
    }

    set score(v) {
        this._score = v >> 0;
        this.scoreTween = tween(this)
            // @ts-ignore
            .to(0.66, {_tempScore: this.score}, {progress: this.updateScore})
            .start();
    }

    _tempScore = 0;

    updateScore = (start: number, end: number, current: number, ratio: number) => {
        let newScore = Math.floor(start + (end - start) * ratio);
        this.scoreLabel.string = newScore.toString();
        this.scoreLabel.fontSize = (newScore > 999) ? 46 : 56;
        return newScore;
    }

    _cd = 0;
    get cd() {
        return this._cd;
    }

    set cd(v) {
        this._cd = v;
        this.cdLabel.string = this.cd.toString();
    }

    handType: HAND_TYPE = HAND_TYPE.CAT;
    loopType: LOOP_TYPE = LOOP_TYPE.CU;
    isDouble = false;

    isUseGod = false;

    isStart = false;
    isGuide = false;
    isOver = false;

    cdPause = false;

    _canvas: HTMLCanvasElement = null;
    _canvas2: HTMLCanvasElement = null;

    // _canvas3: HTMLCanvasElement = null;
    copyRenderTex() {
        const width = this.rt.width;
        const height = this.rt.height;
        const buffer = this.rt.readPixels();
        // const buffer3 = this.rt3.readPixels();

        if (!this._canvas) {
            this._canvas = document.createElement('canvas');
            this._canvas.width = width;
            this._canvas.height = height;
            this._canvas2 = document.createElement('canvas');
            this._canvas2.width = width;
            this._canvas2.height = height;
            // this._canvas3 = document.createElement('canvas');
            // this._canvas3.width = width;
            // this._canvas3.height = height;
        } else {
            const ctx = this._canvas.getContext('2d');
            ctx.clearRect(0, 0, width, height);
            const ctx2 = this._canvas2.getContext('2d');
            ctx2.clearRect(0, 0, width, height);
            // const ctx3 = this._canvas3.getContext('2d');
            // ctx3.clearRect(0, 0, width, height);
        }

        // const ctx3 = this._canvas3.getContext('2d')!;
        // const copiedBuffer3 = new ArrayBuffer(buffer3.length);
        // const copiedUint8Array3 = new Uint8ClampedArray(copiedBuffer3);
        // copiedUint8Array3.set(buffer3);
        // const imageData3 = new ImageData(copiedUint8Array3, width, height);
        // ctx3.putImageData(imageData3, 0, 0);


        const ctx2 = this._canvas2.getContext('2d')!;
        const copiedBuffer = new ArrayBuffer(buffer.length);
        const copiedUint8Array = new Uint8ClampedArray(copiedBuffer);
        copiedUint8Array.set(buffer);
        const imageData = new ImageData(copiedUint8Array, width, height);
        ctx2.putImageData(imageData, 0, 0);

        const ctx = this._canvas.getContext('2d');
        // ctx.drawImage(this._canvas3, 0, 0);
        ctx.drawImage(this._canvas2, 0, 0);
        const b64 = this._canvas.toDataURL("image/jpeg", 0.9);

        return b64;
    }


    async uploadScreenshot() {
        try {
            const b64 = this.copyRenderTex();
            const {success, data} = await sendWebNet(WebNetName.imgUploadUrl, {
                img64: b64,
            }, null, true, false, {
                'Content-Type': 'application/json'
            })
            if (!success) return;
            await sendWebNet(WebNetName.transfer, {
                startId: GameMgr.ins.gameInfo.startId,
                detail: data,
            }, null, true);

        } catch (e) {
            console.log(e);
        }
    }

    onLoad() {
        PhysicsSystem.instance.enable = true;
        // PhysicsSystem.instance.debugDrawFlags
        // 	= EPhysicsDrawFlags.AABB
        // 	| EPhysicsDrawFlags.CONSTRAINT
        // 	| EPhysicsDrawFlags.WIRE_FRAME;
        PhysicsSystem.instance.gravity = v3(0, -40, 0);

        this.table = this.node.getChildByName("table");

        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.onTouchEC, this);
        input.on(Input.EventType.TOUCH_CANCEL, this.onTouchEC, this);
        eventTarget.on(MEvent.ADD_SCORE, this.addScore, this);

        this.handArr = [this.cat, this.dragon, this.human];

        this.loopArr = [this.cu, this.ag, this.au, this.jadeite, this.god];
    }

    _musicState = true;
    get musicState() {
        return this._musicState;
    }

    set musicState(v) {
        this._musicState = v;
        if (v) {
            this.bgMusic.play();
        } else {
            this.bgMusic.pause();
        }
        this.uiLayer.view["top/音乐开"].active = v;
        this.uiLayer.view["top/音乐关"].active = !v;
    }

    changeMusic() {
        this.musicState = !this.musicState;
    }

    clickBack() {
        this.cdPause = true;
        showPanel(BackPanel, {
            cancel: () => {
                this.cdPause = false;
            }
        });
    }

    async start() {
        this.uiLayer.view["天降财神"].on(Button.EventType.CLICK, this.useGod, this);
        this.uiLayer.view["gamePre/选择套圈手"].on(Button.EventType.CLICK, this.chooseHand, this);
        this.uiLayer.view["top/音乐开"].on(Button.EventType.CLICK, this.changeMusic, this);
        this.uiLayer.view["top/音乐关"].on(Button.EventType.CLICK, this.changeMusic, this);
        this.uiLayer.view["top/返回"].on(Button.EventType.CLICK, this.clickBack, this);

        this.rotateSpeed += Math.random() * 5 - 2.5;
        this.handNode.getPosition(this.handPos);

        this.handSpeed += Math.random() * 2 - 1;

        const {loopType, handType, rongNum, targetNum, richManNum, isDouble, newGuide} = GameMgr.ins.gameInfo;

        this.isDouble = isDouble;
        this.richManNum = 0;
        this.initIp(rongNum);
        this.initTarget(targetNum);
        this.changeHand(handType as HAND_TYPE);
        this.changeLoop(loopType as LOOP_TYPE);

        this.cd = this.totalCD;

        this.gameMask.active = false;
        this.uiLayer.view["gamePre"].active = false;
        this.uiLayer.view["guide1"].active = false;
        this.uiLayer.view["guide2"].active = false;

        this.isGuide = newGuide;
        if (newGuide) {
            await this.guide1();
        }

        this.richManNum = richManNum;

        if (newGuide) {
            await this.guide2();
        }

        this.isGuide = false;
        this.startPre();

        // 不能用scheduleOnce，不然会把下面的计时器也取消掉
        setTimeout(() => {
            this.uploadScreenshot();
        }, 1000)
        this.schedule(this.uploadScreenshot, 10);
    }

    async guide1() {
        return new Promise<void>((resolve) => {
            sendLog(LOG_TYPE.EXPOSURE, 14)
            this.gameMask.active = true;
            this.uiLayer.view["guide1"].active = true;
            input.once(
                Input.EventType.TOUCH_END,
                async () => {
                    sendLog(LOG_TYPE.CLICK, 14)
                    this.gameMask.active = false;
                    this.uiLayer.view["guide1"].active = false;
                    this.shoot(1, 0);
                    await sleep(3);
                    resolve();
                },
                this
            );
        });
    }

    async guide2() {
        return new Promise<void>((resolve) => {
            sendLog(LOG_TYPE.EXPOSURE, 15)
            this.gameMask.active = true;
            this.uiLayer.view["guide2"].active = true;
            this.uiLayer.view["天降财神"].once(
                Button.EventType.CLICK,
                () => {
                    sendLog(LOG_TYPE.CLICK, 16)
                    sendLog(LOG_TYPE.CLICK, 15)
                    this.gameMask.active = false;
                    this.uiLayer.view["guide2"].active = false;
                    setTimeout(() => {
                        sendWebNet(WebNetName.completeGuide);
                    }, 2000);
                    resolve();
                },
                this
            );
        });
    }

    startPre() {
        this.uiLayer.view["gamePre"].active = true;
        this.scheduleOnce(() => {
            this.uiLayer.view["gamePre/cd"].on(Svga.EventType.END_FRAME, () => {
                this.uiLayer.view["gamePre/cd"].active = false;
                tween(this.operationTip.getComponent(UIOpacity))
                    .to(0.5, {opacity: 255})
                    .delay(2)
                    .call(() => {
                        this.uiLayer.view["gamePre"].active = false;
                        this.startGame();
                    })
                    .start();
            });
            this.uiLayer.view["gamePre/cd"].getComponent(Svga).play();
        }, 0.188);
    }

    startGame() {
        this.isStart = true;
        this.isOver = false;
        this.unschedule(this.cdDown);
        this.schedule(this.cdDown, 1);
    }

    clockAnim = false;

    cdDown() {
        if (this.cdPause) return;
        this.cd--;
        if (!this.clockAnim && this.cd <= 10) {
            this.clockAnim = true;
            this.clockIcon.getComponent(Animation).play();
        }
        if (this.cd <= 0) {
            this.unschedule(this.cdDown);
            this.gameOver();
        }
    }

    /**
     * 游戏结束
     */
    gameOver() {
        if (this.isOver) return;
        this.isOver = true;

        const {isUsedDelay} = GameMgr.ins.gameInfo;
        if (isUsedDelay) {
            this.submit();
        } else {
            GameMgr.ins.gameInfo.isUsedDelay = true;
            showPanel(ReviewPanel, {
                review: this.review,
                submit: this.submit,
            });
        }
    }

    /**
     * 复活
     */
    review = async () => {
        const {success, data} = await sendWebNetWithToken(WebNetName.delay);
        if (!success) {
            return this.submit();
        }
        this.cd = GameMgr.ins.gameInfo.delaySecond;
        this.startGame();
    };

    /**
     * 提交分数
     */
    submit = async () => {
        const success = await GameMgr.ins.submit(this.score, this.rongNum);
    };

    /**
     * 选择套圈手
     */
    chooseHand = () => {
        const svga = this.uiLayer.view["gamePre/cd"].getComponent(Svga);
        svga.stop();
        showPanel(GameChooseHand, {
            curHand: this.handType,
            sucCall: (type) => {
                this.changeHand(type);
                svga.play(svga.curFrame);
            },
        });
    };

    /**
     * 使用财神道具
     */
    useGod = async () => {
        sendLog(LOG_TYPE.CLICK, 16)
        if (((!this.isStart || this.isOver) && !this.isGuide) || this.isUseGod || this.richManNum <= 0) return;

        this.btnEnable(this.uiLayer.view["天降财神"], false);

        const success = await GameMgr.ins.useSp();

        this.btnEnable(this.uiLayer.view["天降财神"], true);

        if (!success) return;

        this.richManNum--;
        this.isUseGod = true;
        this.uiLayer.view["天降财神"].active = false;
        // this.scheduleOnce(() => {
        //     this.isUseGod = false;
        // }, 5)
    };

    /**
     * 套中一个
     */
    addScore() {
        if ((!this.isStart || this.isOver) && !this.isGuide) {
            return;
        }

        this.rongNum++;
        this.score += BaseScore * (1 + LoopBuff[this.loopType]) * (this.isDouble ? 2 : 1);

        // this.stringScore
        const pointNode = this.targetPoint.shift();
        pointNode && (pointNode.active = true);

        const scoreTipAlpha = this.addScoreTip.getComponent(UIOpacity);

        const duration = 0.233;
        const delay = 0.5;
        const moveY = 50;
        const stopY = -230;
        Tween.stopAllByTarget(this.addScoreTip);
        Tween.stopAllByTarget(scoreTipAlpha);
        tween(this.addScoreTip)
            .set({position: v3(0, stopY - moveY, 0)})
            .to(duration, {position: v3(0, stopY, 0)})
            .delay(delay)
            .to(duration, {position: v3(0, stopY + moveY, 0)})
            .start();

        tween(scoreTipAlpha).set({opacity: 0}).to(duration, {opacity: 225}).delay(delay).to(duration, {opacity: 0}).start();
    }

    changeHand(type: HAND_TYPE) {
        this.handType = type;
        const handPar = this.handNode.getChildByName("hand");
        handPar.removeAllChildren();
        const handPrefab = this.handArr[+type - 1];
        const hand = instantiate(handPrefab);
        handPar.addChild(hand);
        this.hand = hand;
    }

    changeLoop(type: LOOP_TYPE) {
        this.loopType = type;
        const loopPar = this.handNode.getChildByName("loop");
        loopPar.children.forEach((v) => {
            if (v.name == "trigger") return;
            v.destroy();
        });
        const loopPrefab = this.loopArr[+type - 1];
        const loop = instantiate(loopPrefab);
        loopPar.addChild(loop);
    }

    initIp(count) {
        const angle = 360 / count;
        for (let i = 0; i < count; i++) {
            const ip = instantiate(this.ipPrefab);
            this.table.addChild(ip);
            ip.setPosition(0, 0, 0);
            ip.setRotationFromEuler(0, angle * i, 0);
        }
    }

    targetPoint: Node[] = [];

    initTarget(count) {
        const pointWidth = (42 + 7) * count;
        const progNode = this.uiLayer.view["top/游戏进度"];
        progNode.getComponent(UITransform).width = pointWidth + 142;
        this.uiLayer.view["top/游戏进度/box"].setPosition(pointWidth + 36, 0);

        const tempBar = this.uiLayer.view["top/游戏进度/bar"];
        const tempFill = this.uiLayer.view["top/游戏进度/fill"];

        for (let i = 0; i < count; i++) {
            const bar = instantiate(tempBar);
            const fill = instantiate(tempFill);
            bar.setPosition(62 + (42 + 7) * i, 0);
            fill.setPosition(62 + (42 + 7) * i, 0);
            fill.active = false;
            progNode.addChild(bar);
            progNode.addChild(fill);
            this.targetPoint.push(fill);
        }

        tempBar.destroy();
        tempFill.destroy();
    }

    onTouchStart = (e: EventTouch) => {
    };

    onTouchMove = (e: EventTouch) => {
    };

    onTouchEC = _asyncThrottle((e: EventTouch) => {
        if (!this.isStart || this.isOver) return;
        const start = e.getStartLocation ? e.getStartLocation() : e.getLocation();
        const end = e.getLocation();

        const dp = end.subtract(start);

        const powerScale = misc.clampf(dp.length() / 250, 0.95, 1.08);

        if (this.isUseGod) {
            this.schedule(
                () => {
                    this.shoot(powerScale, dp.x / 130);
                },
                0.1,
                5
            );
        } else {
            this.shoot(powerScale, dp.x / 130);
        }
    }, 1000);

    /**
     * 发射
     * @param powerScale
     * @param endX
     */
    shoot(powerScale: number, endX: number) {
        const loopTemp = this.handNode.getChildByName("loop");

        const loop = instantiate(loopTemp);
        this.node.addChild(loop);

        const setPos = Vec3.add(v3(), loopTemp.position, this.handNode.position);
        loop.setPosition(setPos);

        const loopTs = loop.getComponent(Quan);
        loopTs.shoot(powerScale, endX);

        const handAni = this.hand.getComponent(SkeletalAnimation);
        handAni.play();

        return loop;
    }

    rotateY = 0;

    handPos = v3(0, 0, 0);
    handDir = 1;

    update(deltaTime: number) {
        if (!this.isStart || this.isOver) return;

        this.rotateY -= this.rotateSpeed * deltaTime;
        this.table.setRotationFromEuler(0, this.rotateY, 0);

        this.handPos.x += this.handSpeed * deltaTime * this.handDir;

        if (this.handPos.x >= 5) {
            this.handDir = -1;
        } else if (this.handPos.x <= -1.5) {
            this.handDir = 1;
        }

        this.handNode.setPosition(this.handPos);
    }
}
