import { _decorator,Node, geometry, Camera, screen, EventTouch, Prefab, instantiate, v3, tween, RigidBody, MeshRenderer, Vec3, find, input, Input, Tween, PhysicsSystem, view } from 'cc';
import { TileItem } from './TileItem';
import { Main } from './Main';
import { ResUtil } from '../core/utils/ResUtil';
import { ComponentEx } from '../core/component/ComponentEx';
import { audioMgr } from '../core/manager/AudioManager';
import BusyLoadingManager, { BUSY_TYPE } from '../core/manager/BusyLoadingManager';
import Data from '../core/manager/Data';
import WindowManager from '../core/manager/WindowManager';
import MsgHints from '../core/utils/MsgHints';
import Utils from '../core/utils/Utils';
import { DrawStarLayer } from '../gameui/DrawStarLayer';
import { eventEmitter } from '../core/event/EventEmitter';
import platformSystem from '../platform/platformSystem';
import { GameConst, ITEM_TYPE } from '../core/common/GameConst';
import { levelsData } from '../user/LevelsData';
import { PoolManager } from '../core/manager/PoolManager';

const { Ray } = geometry;
const { ccclass, property, executeInEditMode } = _decorator;

@ccclass('GameNode')
export class GameNode extends ComponentEx {
    public lock: boolean = false;
    public collectpos: Vec3[] = [];
    public collectTiles: Node[] = [];
    public allTiles: Node[] = [];
    public optionTiles: Node[] = [];

    public start() {
        setTimeout(() => {
            this.initWall();
            this.collectpos = [];
            this.GetNode("collectbox").children.map(a => {
                this.collectpos.push(a.worldPosition);
                a.active = false;
            })
        }, 100);

        eventEmitter.register(this, GameConst.CLEAR_ALL_BOX, () => {
            console.log("清空盒子")
            this.lock=false
            setTimeout(() => {
                for (let i = 0; i < this.collectTiles.length; ++i) {
                    this.pushBask(this.collectTiles[i].name);
                }
                this.collectTiles.map(a => {
                    a.destroy();
                })
                this.collectTiles = [];
            }, 500);
        })
        //提示
        eventEmitter.register(this, GameConst.USE_ITEM_HINT,this.prompt.bind(this))
    }

    onEnable() {
        input.on(Input.EventType.TOUCH_START, this.onTouchStart, this);
        input.on(Input.EventType.TOUCH_END, this.onTouchEnd, this);
        input.on(Input.EventType.TOUCH_MOVE, this.onTouchMove, this);
    }

    onDisable() {
        input.off(Input.EventType.TOUCH_START, this.onTouchStart, this);
        input.off(Input.EventType.TOUCH_END, this.onTouchEnd, this);
        input.off(Input.EventType.TOUCH_MOVE, this.onTouchMove, this);
    }

    

    moveToRightPos() {
        this.collectTiles.map((a, i) => {
            let tileItem = a.getComponent(TileItem)
            Tween.stopAllByTarget(a);
            tween(a).to(0.3, { worldPosition: this.collectpos[i].clone(), worldScale: v3(1, 1, 1) }).call(() => {
                tileItem.auto_rotation = true;
                if (tileItem.removed) {
                    Utils.remove(this.optionTiles,a);
                    tween(a).to(0.25, { worldPosition: tileItem.removedPos }, { easing: 'backIn' }).call(async () => {
                        //最中央的播放移除特效
                        if (tileItem.playRmovedEff) {
                            //增加星星
                            let pos = this.GetNode("Main Camera").getComponent(Camera).convertToUINode(a.worldPosition, find("Canvas"));
                            let node_ani = await ResUtil.playSkAni("spine/effect_hecheng", "effect", find("Canvas"), pos);
                            audioMgr.playOneShot(GameConst.audios.starCollect);
                            platformSystem.platform.vibrateShort();
                            Utils.flyAnim(ITEM_TYPE.Star, node_ani, Main.I._GameUI.starNode, 1, 0, (b) => {
                                if (b) {
                                    Main.I._GameUI.star++;
                                }
                            });
                        }
                        PoolManager.putNode(a);
                        this.moveToRightPos()
                        this.checkResult()
                    }).start() 
                }
            }).start();
        })
    }
    
    chooseTile(tile: Node) {
        if (!tile || !tile.isValid) return;
        let tileItem = tile.getComponent(TileItem);
        if (!tileItem) return;
        if (tileItem.isMoving) return;
        let chooseName = tile.name;
        this.optionTiles.push(tile);
        // 删除点击对象从列表
        tileItem.destoryCollider();
        for (var i = 0; i < this.allTiles.length; ++i) {
            if (this.allTiles[i] == tile) {
                this.allTiles.splice(i, 1);
                break;
            }
        }

        // 计算目标位置
        let targetPos: Vec3;
        let bInsert = false;
        for (var i = this.collectTiles.length - 1; i >= 0; --i) {
            if (this.collectTiles[i].name == chooseName) {
                targetPos = this.collectpos[i + 1]?.clone() || this.collectpos[this.collectpos.length - 1].clone();
                this.collectTiles.splice(i + 1, 0, tile);
                bInsert = true;
                break;
            }
        }
        if (!bInsert) {
            targetPos = this.collectpos[this.collectTiles.length]?.clone() || this.collectpos[0].clone();
            this.collectTiles.push(tile);
        }
        //添加上抛动画 标记物品为移动中
        tileItem.isMoving = true;
        const startPos = tile.worldPosition.clone();
        const midHeight = 10; // 上抛高度
        const duration = 0.2; // 动画时长
        this.parabolicMovementWithScale(
            tile,
            startPos,
            targetPos,
            midHeight,
            duration,
            tile.scale.clone(),
            v3(1, 1, 1),
            (n: Node) => {
                this.moveToRightPos()
                tile.getComponent(TileItem).isMoving = false;
                //动画完成后执行原有逻辑
                let tmp = this.collectTiles.filter(a => {
                    return a.name == chooseName;
                })
                if (tmp.length >= 3) {
                    for (var i = this.collectTiles.length - 1; i >= 0; --i) {
                        if (this.collectTiles[i].name == chooseName) {
                            let delNode = this.collectTiles[i];
                            delNode.getComponent(TileItem).removed = true;
                            this.collectTiles.splice(i, 1);
                            delNode.getComponent(TileItem).removedPos = tmp[1].worldPosition;
                            if (delNode == tmp[1])
                                delNode.getComponent(TileItem).playRmovedEff = true;
                        }
                    }
                }
                this.checkResult();
            }
        );
    }

    checkResult() {
        if(this.lock)return
        //检测胜利和失败
        if (this.collectTiles.length >= 7) {
            this.lock=true
            if (!WindowManager.ins.isShow("OutOfBoxLayer"))
                WindowManager.ins.open("OutOfBoxLayer");
        } else if (this.collectTiles.length == 0 && this.allTiles.length == 0) {
            this.lock=true
            WindowManager.ins.open("DrawStarLayer").then((com: DrawStarLayer) => {
                com.setStar(Main.I._GameUI.star);
            })
        }
    }

    private touchTileItem: TileItem = null;
    onTouchStart(event: EventTouch) {
        if (Main.I._GameUI.pasue) return;
        if (Data.user.useMagnet) return;
        if (this.allTiles.length == 0) return;
        if (this.collectTiles.length >= 7) return;
        const p = event.getLocation();
        let camera = this.GetNode("Main Camera").getComponent(Camera);
        const r = new Ray();
        camera?.screenPointToRay(p.x, p.y, r);
        let b = PhysicsSystem.instance.raycastClosest(r, 1);
        if (b) {
            let collider = PhysicsSystem.instance.raycastClosestResult.collider;
            if (!collider || !collider.node || !collider.node.parent) return;
            let item = collider.node.parent.getComponent(TileItem);
            if (!item || !item.node || !item.node.isValid) return;
            item.enableCollider = false;
            let pos = item.node.getWorldPosition();
            pos.y += 0.3;
            item.node.setWorldPosition(pos);
            this.touchTileItem = item;
        }
    }

    onTouchEnd(event: EventTouch) {
        if (Main.I._GameUI.pasue) return;
        if (Data.user.useMagnet) return;
        if (!this.touchTileItem) return;
        if (this.allTiles.length == 0) return;
        if (this.collectTiles.length >= 7) {
            return;
        }
        const p = event.getLocation();
        let camera = this.GetNode("Main Camera").getComponent(Camera);
        const r = new Ray();
        camera?.screenPointToRay(p.x, p.y, r);
        let b = PhysicsSystem.instance.raycastClosest(r, 1);
        if (b) {
            let collider = PhysicsSystem.instance.raycastClosestResult.collider;
            // if(!collider.getComponent(RigidBody))return;
            let item = collider.node.parent.getComponent(TileItem);
            if (item && item == this.touchTileItem) {
                platformSystem.platform.vibrateShort();
                this.chooseTile(item.node);
                this.touchTileItem = null;
                audioMgr.playOneShot(GameConst.audios.tap);
            }else {
                this.touchTileItem.enableCollider = true;
            }
        }
    }


    onTouchMove(event: EventTouch) {
        if (Main.I._GameUI.pasue) return;
        if (Data.user.useMagnet) return;
        if (this.allTiles.length == 0) return;
        if (this.collectTiles.length >= 7) {
            return;
        }
        const p = event.getLocation();
        let camera = this.GetNode("Main Camera").getComponent(Camera);
        const r = new Ray();
        camera?.screenPointToRay(p.x, p.y, r);
        let b = PhysicsSystem.instance.raycastClosest(r, 1)
        if (b) {
            let collider = PhysicsSystem.instance.raycastClosestResult.collider
            // if(!collider.getComponent(RigidBody))return;
            let item = collider.node.parent.getComponent(TileItem);
            if (item) {
                if (this.touchTileItem && item == this.touchTileItem) {
                }else {
                    if (this.touchTileItem) this.touchTileItem.enableCollider = true;
                    item.enableCollider = false;
                    let pos = item.node.getWorldPosition();
                    pos.y += 0.3;
                    // console.log("切换")
                    item.node.setWorldPosition(pos);
                    this.touchTileItem = item;
                }
            }
            else {
                if (this.touchTileItem) {
                    // console.log("无目标")
                    this.touchTileItem.enableCollider = true;
                    this.touchTileItem = null;
                }
            }
        }
    }

    initWall() {
        if (!PhysicsSystem.instance) return;
        const OFFSET = 25;
        let camera = this.GetNode("Main Camera").getComponent(Camera);
        const r = new Ray();
        let size = screen.windowSize;
        //左边墙
        camera?.screenPointToRay(OFFSET, size.height / 2, r);
        if (PhysicsSystem.instance.raycastClosest(r)) {
            const result = PhysicsSystem.instance.raycastClosestResult;
            this.GetNode("PlaneLeft").setWorldPosition(result.hitPoint);
        }
        //右边墙
        camera?.screenPointToRay(size.width - OFFSET, size.height / 2, r);
        if (PhysicsSystem.instance.raycastClosest(r)) {
            const result = PhysicsSystem.instance.raycastClosestResult;
            this.GetNode("PlaneRight").setWorldPosition(result.hitPoint);
        }
        //上边墙
        camera?.screenPointToRay(size.width / 2, size.height - OFFSET * 4, r);
        if (PhysicsSystem.instance.raycastClosest(r)) {
            const result = PhysicsSystem.instance.raycastClosestResult;
            this.GetNode("PlaneTop").setWorldPosition(result.hitPoint);
        }
        let minx = this.GetNode("PlaneLeft").worldPosition.x;
        let maxx = this.GetNode("PlaneRight").worldPosition.x;
        let scale = (maxx - minx) / 8.461091041564941;
        this.GetNode("bottom").scale = v3(scale, scale, scale);

        //格子位置
        camera?.screenPointToRay(size.width / 2, 10, r);
        if (PhysicsSystem.instance.raycastClosest(r)) {
            const result = PhysicsSystem.instance.raycastClosestResult;
            //result.hitPoint
            let p: Vec3 = new Vec3(result.hitPoint.x,4,result.hitPoint.z);
            this.GetNode("bottom").setWorldPosition(p);
        }
        //下边墙
        this.GetNode("PlaneDown").setWorldPosition(this.GetNode("bottom").worldPosition.add3f(0, 0, -2.5 * scale));
    }

    async createTiles() {
        let Margin = 0
        let minx = this.GetNode("PlaneLeft").worldPosition.x + Margin;
        let maxx = this.GetNode("PlaneRight").worldPosition.x - Margin;
        let maxz = this.GetNode("PlaneDown").worldPosition.z + Margin;
        let minz = this.GetNode("PlaneTop").worldPosition.z - Margin;
        let obj =  levelsData.getCurLevelInfo();
        //数量
        let tileCount = Math.floor(obj.count / 3);
        let names:Array<string> = levelsData.getModesNames();
        let tilepools = Utils.getRandomElements(names,obj.kind);
        tilepools = Utils.extendArray(tilepools,tileCount);
        Main.I._GameUI.pasue = true;
        BusyLoadingManager.ins.addBusy(BUSY_TYPE.RES);
        for (let i = 0; i < tileCount; i++) {
            let ret = await ResUtil.loadModelPrefabName(tilepools[i]) as Prefab;
            if (!ret) continue;
            //获取节点下的 MeshRenderer 组件 设置阴影投射模式
            (ret.data.getComponentInChildren(MeshRenderer) as MeshRenderer).shadowCastingMode = MeshRenderer.ShadowCastingMode.ON;
            for (let j = 0; j < 3; j++) {
                const globalIndex = (i * 3 + j) % 9;
                let lnode = this.GetNode(`l${globalIndex}`);
                tween(this.node).delay(j * 0.03).call(() => {
                    let nomal_node: Node = instantiate(ret);
                    nomal_node.name = ret.name;
                    let tile = nomal_node.addComponent(TileItem);
                    let rigid = tile.addCollider();
                    nomal_node.scale = v3(1.25, 1.25, 1.25)
                    if (Data.user.lv == 1) {
                        nomal_node.setWorldPosition(lnode.getWorldPosition());
                        nomal_node.parent = this.node;
                        this.allTiles.push(nomal_node);
                    }else{
                        let p = v3(Utils.getRandomInt(minx / 2, maxx / 2), Utils.getRandom(12, 15), Utils.getRandomInt(minz / 2, maxz / 2));
                        nomal_node.setWorldRotationFromEuler(Utils.getRandom(0, 300), Utils.getRandom(0, 300), Utils.getRandom(0, 300))
                        nomal_node.setWorldPosition(p);
                        nomal_node.parent = this.node;
                        rigid.applyImpulse(v3(0, 3, 0))
                        this.allTiles.push(nomal_node);
                    }
                }).start()
            }
            if (i == tileCount - 1) {
                BusyLoadingManager.ins.removeBusy(BUSY_TYPE.RES);
                Main.I._GameUI.pasue = false;
            }
        }

        setTimeout(() => {
            if (Data.user.useMagnet) {
                Main.I._GameUI.pasue = true;
                Data.user.magnet--;
                let p = this.GetNode("magnet").worldPosition;
                this.GetNode("magnet").worldPosition = v3(-10, p.y, p.z);
                tween(this.GetNode("magnet")).to(0.5, { worldPosition: v3(0, p.y, p.z) }).call(async () => {
                    let arr = [];
                    for (var i = this.allTiles.length - 1; i >= 0; --i) {
                        let pick = false;
                        let item = this.allTiles[i];
                        let tmp: Node = arr[0];
                        if(tmp){
                            if (tmp.name == item.name && arr.length < 3) {
                                arr.push(item);
                                pick = true;
                            }
                        }else{
                            arr.push(item);
                            pick = true;
                        }
                        if(pick) {
                            let tile: TileItem = this.allTiles[i].getComponent(TileItem);
                            tile.destoryCollider();
                            this.GetNode("magnet_content").addChild(tile.node);
                            tween(tile.node).to(0.5, { worldPosition: this.GetNode(arr.length % 2 ? "m_left" : "r_left").worldPosition }).start();
                            this.allTiles.splice(i, 1);
                            console.log("删除", tile.name, this.allTiles.length)

                        }
                    }
                    Data.user.useMagnet = false;
                }).delay(1.5).to(0.5, { worldPosition: v3(10, p.y, p.z) }).call(() => {
                    this.GetNode("magnet_content").removeAllChildren();
                    Main.I._GameUI.pasue = false;
                    if (Data.user.useTime && Data.user.time > 0)
                        eventEmitter.dispatch(GameConst.USE_ITEM_TIME)
                }).start();
            }else {
                if (Data.user.useTime && Data.user.time > 0){
                    eventEmitter.dispatch(GameConst.USE_ITEM_TIME)}
            }
        }, 2000);
    }

    /**
     * 提示
     */

    restart() {
        this.allTiles.map(a => a.destroy());
        this.allTiles = [];
        Main.I._GameUI.star = 0;

        this.collectTiles.map(a => a.destroy());
        this.collectTiles = [];
        this.createTiles();
        this.lock=false
    }

    /**移除*/
    public remove(){
        if (this.optionTiles.length === 0 || this.lock) return;
        // 获取最后一个收集的物品
        const lastItem = this.optionTiles.pop();
        if (!lastItem) return;
        Utils.remove(this.collectTiles,lastItem);
        const tile = lastItem.getComponent(TileItem);
        tile.removed = false;
        tile.auto_rotation = false;
        //播放撤销动画
        Tween.stopAllByTarget(lastItem);
        //const globalIndex = Math.floor(Math.random() * 9);
        let lnode = this.GetNode(`l${4}`);
        let p:Vec3 = lnode.position.clone();
        tween(tile.node)
            .to(0.35, { 
                worldPosition: new Vec3(p.x,p.y + 1,p.z),
                worldScale: v3(1.25, 1.25, 1.25) 
            })
            .call(() => {
                tile.addCollider();
                tile.enableCollider = true;
                this.allTiles.push(lastItem);
                //向上施加力 然后在向下重力效果
                tile.rigid.applyImpulse(v3(0, 3, 0))
                this.moveToRightPos(); // 重新排列收集框中的物品
            })
            .start();
    }

    /**乱序*/
    public mess(){
        if(this.allTiles.length === 0 || this.lock) return;
        this.allTiles.forEach((item, index) => {
            const tileItem = item.getComponent(TileItem);
            tileItem.auto_rotation = false;
            tileItem.enableCollider = true;
            Tween.stopAllByTarget(item);
            tween(item)
                .to(0.3, {
                    worldPosition: this.GetNode("l4").worldPosition.clone().add(v3(0, 1, 0)),
                    worldScale: v3(1.25, 1.25, 1.25)
                })
                .call(() => {
                    const rigid = tileItem.addCollider();
                    rigid.applyImpulse(v3(
                        Utils.getRandom(-3, 3),
                        Utils.getRandom(5, 8),
                        Utils.getRandom(-3, 3)
                    ));
                    item.setWorldRotationFromEuler(Utils.getRandom(0, 300), Utils.getRandom(0, 300), Utils.getRandom(0, 300))
                })
                .start();
        });
        //audioManager.playOneShot(GameConst.audios.shuffle);
        platformSystem.platform.vibrateShort();
    }

    /**凑齐*/
    public prompt(){
        if(this.collectTiles.length <= 0 || Data.user.hint <= 0)return;
        let obj = {}
        for (var i = 0; i < this.collectTiles.length; ++i) {
            let name = this.collectTiles[i].name;
            if (!obj[name]) obj[name] = 0;
            obj[name]++;
        }
        let hint_succ = false;
        let bFind = false;
        let hint_name = ""
        for (const key in obj) {
            if (obj[key] == 2) {
                hint_name = key;
                bFind = true;
                break;
            }
        }
        //找到两个一样
        if (bFind) {
            console.log("找到两个一样")
            for (let i = 0; i < this.allTiles.length; ++i) {
                if (this.allTiles[i].name == hint_name) {
                    this.chooseTile(this.allTiles[i]);
                    break;
                }
            }
            hint_succ = true;
        }else {
            if (this.collectTiles.length >= 5) {
                MsgHints.show("未找到合适物品");
                return
            }else {
                hint_name = this.collectTiles[0] ? this.collectTiles[0].name : this.allTiles[0].name;
                let lsit = this.allTiles.filter(a => {
                    return a.name == hint_name;
                })
                lsit.map((a, i) => {
                    setTimeout(() => {
                        this.chooseTile(a);
                    }, 350 * i);
                })
                hint_succ = true;
            }
        }
        if (hint_succ) {
            Data.user.hint--;
        }
    }

    //放回场景中
    async pushBask(name: string) {
        let Margin = 0;
        let maxz = this.GetNode("PlaneDown").worldPosition.z + Margin;
        let minz = this.GetNode("PlaneTop").worldPosition.z - Margin;
        let minx = this.GetNode("PlaneLeft").worldPosition.x + Margin;
        let maxx = this.GetNode("PlaneRight").worldPosition.x - Margin;
        let ret = await ResUtil.loadModelPrefabName(name) as Prefab;
        let nomal_node: Node = instantiate(ret);
        nomal_node.name = name;
        let tile = nomal_node.addComponent(TileItem);
        let rigid = tile.addCollider();
        nomal_node.scale = v3(1.25,1.25, 1.25)
        nomal_node.parent = this.node;
        rigid.applyImpulse(v3(0, 3, 0))
        let p = v3(minx + (maxx - minx) / 2, Utils.getRandom(12, 15), minz + (maxz - minz) / 2);
        nomal_node.setWorldRotationFromEuler(Utils.getRandom(0, 300), Utils.getRandom(0, 300), Utils.getRandom(0, 300))
        nomal_node.setWorldPosition(p);
        this.allTiles.push(nomal_node);
    }

    /**
     * 抛物线运动工具（带缩放动画）
     * @param node 要移动的节点
     * @param startPos 起始坐标（世界坐标）
     * @param endPos 目标坐标（世界坐标）
     * @param height 抛物线最高点高度（相对于起点）
     * @param duration 动画时长（秒）
     * @param startScale 起始缩放（默认原始大小）
     * @param endScale 目标缩放（默认原始大小）
     * @param onComplete 完成回调
     */
    public parabolicMovementWithScale(
        node: Node,
        startPos: Vec3,
        endPos: Vec3,
        height: number = 2,
        duration: number = 1.5,
        startScale: Vec3 = Vec3.ONE,
        endScale: Vec3 = Vec3.ONE,
        onComplete?: (n:Node) => void
    ): Tween<Node> {
        if(Vec3.equals(startPos, endPos)) {
            onComplete?.(node);
        }else{
            //初始化节点状态
            node.setWorldPosition(startPos.clone());
            node.setScale(startScale);
            //计算抛物线顶点
            const midPoint = new Vec3(
                (startPos.x + endPos.x) / 2,
                Math.max(startPos.y, endPos.y) + height,
                (startPos.z + endPos.z) / 2
            );
            //创建组合动画
            return tween(node)
                .to(duration, 
                    { 
                        worldPosition: endPos,
                        worldScale: endScale 
                    },
                    { 
                        onUpdate: (target: Node, ratio: number) => {
                            //1. 计算抛物线位置
                            const t = ratio;
                            const invT = 1 - t;
                            const tempPos = new Vec3();
                            // XZ轴线性插值
                            Vec3.lerp(tempPos, startPos, endPos, t);
                            //Y轴抛物线计算
                            const y = invT * invT * startPos.y 
                                + 2 * invT * t * midPoint.y 
                                + t * t * endPos.y;
                            //2. 计算当前缩放比例
                            const currentScale = new Vec3();
                            Vec3.lerp(currentScale, startScale, endScale, t);
                            //更新节点状态
                            target.worldPosition = new Vec3(tempPos.x, y, tempPos.z);
                            target.setScale(currentScale);
                        }
                    }
                )
                .call(() => {
                    // 最终确保缩放精确
                    node.setScale(endScale);
                    onComplete?.(node);
                })
                .start();
        }
        
    }
}
