import XStaticMesh from "./XStaticMesh.js"
import EMeshType from "./enum/EMeshType.js"
import BreathPoint from "./BreathPoint.js"
import Logger from "./Logger.js"

const logger = new Logger('XBreathPointManager')
export default class XBreathPointManager {
    constructor(e) {
        E(this, "_scene");
        E(this, "materialMap", new Map);
        E(this, "breathPoints", new Map);
        E(this, "_sceneManager");
        E(this, "_allIds", new Set);
        E(this, "_loopBPKeys", []);
        E(this, "addBreathPoint", async e=>{
            const t = [{
                url: "https://static.xverse.cn/qqktv/texture.png"
            }];
            if (t.length <= 0) {
                logger.warn("[Engine] BreathPoint get texture list error: textureList.length <= 0"),
                new XBreathPointError("[Engine] BreathPoint get texture list error!");
                return
            }
            const r = t[0]
              , {id: n, spriteSheet: o=r.url, spriteWidthNumber: a=20, spriteHeightNumber: s=1, position: l, rotation: u={
                pitch: -90,
                yaw: 270,
                roll: 0
            }, size: c=.6, width: h=-1, height: f=-1, fps: d=30, billboardMode: _=!1, forceLeaveGround: g=!1, type: m="default", lifeTime: v=-1, backfaceculling: y=!0, maxVisibleRegion: b=-1, skinInfo: T="default"} = e;
            if (this.breathPoints.get(n)) {
                logger.warn("[Engine] Cannot add breathPoint with an existing id: [" + n + "]"),
                new XBreathPointError("[Engine] Cannot add breathPoint with an existing id: [" + n + "]");
                return
            }
            if (g) {
                const I = this.castRay(new BABYLON.Vector3(l.x,l.y,l.z)) * scaleFromUE4toXverse;
                I != 0 ? l.z = l.z - I + 1 : l.z = l.z + 1
            }
            let C;
            if (this.materialMap.get(m)) {
                const I = this.materialMap.get(m);
                I.count = I.count + 1,
                C = I.mat
            } else {
                const I = new BABYLON.Texture(o,this._scene,!0,!0,BABYLON.Texture.BILINEAR_SAMPLINGMODE,null,()=>{
                    logger.error("[Engine] Breathpoint create texture error."),
                    new XBreathPointError("[Engine] Breathpoint create texture error.")
                }
                ,null,!0);
                I.name = "TexBreathPoint_" + n,
                C = new BABYLON.StandardMaterial(`MaterialBreathPoint_${n}`,this._scene),
                C.alpha = 1,
                C.emissiveTexture = I,
                C.backFaceCulling = y,
                C.diffuseTexture = I,
                C.diffuseTexture.hasAlpha = !0,
                C.useAlphaFromDiffuseTexture = !0,
                this.materialMap.set(m, {
                    mat: C,
                    count: 1,
                    lastRenderTime: Date.now(),
                    fps: d,
                    spriteWidthNumber: a,
                    spriteHeightNumber: s,
                    spriteSheet: o,
                    texture: I
                })
            }
            const A = new Array(6);
            for (let I = 0; I < 6; I++)
                A[I] = new BABYLON.Vector4(0,0,0,0);
            A[0] = new BABYLON.Vector4(0,0,1 / a,1 / s),
            A[1] = new BABYLON.Vector4(0,0,1 / a,1 / s);
            let S = {};
            h > 0 && f > 0 ? S = {
                width: h,
                height: f,
                depth: .01,
                faceUV: A
            } : S = {
                size: c,
                depth: .01,
                faceUV: A
            };
            const P = BABYLON.MeshBuilder.CreateBox(n, S, this._scene);
            P.material = C;
            const R = new XStaticMesh({
                id: n,
                mesh: P,
                xtype: EMeshType.XBreathPoint,
                skinInfo: T
            });
            let M = u;
            _ && (P.billboardMode = BABYLON.Mesh.BILLBOARDMODE_ALL,
            R.allowMove(),
            M = {
                pitch: 0,
                yaw: 270,
                roll: 0
            });
            const x = new BreathPoint({
                type: m,
                mesh: R,
                id: n,
                position: l,
                rotation: M,
                mat: C,
                maxVisibleRegion: b,
                scene: this._scene,
                skinInfo: T
            });
            this.breathPoints.set(n, x),
            this._allIds.add(n),
            v > 0 && setTimeout(()=>{
                this.clearBreathPoints(n)
            }
            , v * 1e3)
        }
        );
        E(this, "reg_breathpoint_update", ()=>{
            const e = new Date().getTime();
            if (this.materialMap != null)
                for (const [t,r] of this.materialMap)
                    e - r.lastRenderTime > 1e3 / r.fps && (r.lastRenderTime = e,
                    Math.abs(r.mat.diffuseTexture.uOffset - (1 - 1 / r.spriteWidthNumber)) < 1e-6 ? (r.mat.diffuseTexture.uOffset = 0,
                    Math.abs(r.mat.diffuseTexture.vOffset - (1 - 1 / r.spriteHeightNumber)) < 1e-6 ? r.mat.diffuseTexture.vOffset = 0 : r.mat.diffuseTexture.vOffset += 1 / r.spriteHeightNumber) : r.mat.diffuseTexture.uOffset += 1 / r.spriteWidthNumber)
        }
        );
        E(this, "reg_breathpoint_autovisible", ()=>{
            if (this._scene.getFrameId() % 2 == 0)
                if (this._loopBPKeys.length == 0)
                    this._loopBPKeys = Array.from(this._allIds);
                else {
                    const e = this._getMainPlayerPosition();
                    for (let t = 0; t < 5 && this._loopBPKeys.length > 0; ++t) {
                        const r = this._loopBPKeys.pop();
                        if (r != null) {
                            const n = this.getBreathPoint(r);
                            if (n != null && n.maxvisibleregion >= 0 && n.mesh.visibility == 1) {
                                const o = n.mesh.position;
                                calcDistance3DVector(e, o) >= n.maxvisibleregion ? n == null || n.removeFromScene() : n == null || n.addToScene()
                            }
                        }
                    }
                }
        }
        );
        this._sceneManager = e,
        this._scene = e.Scene,
        this._scene.registerBeforeRender(this.reg_breathpoint_update),
        this._scene.registerBeforeRender(this.reg_breathpoint_autovisible)
    }
    setAllBreathPointVisibility(e) {
        for (const [t,r] of this.breathPoints.entries())
            r.toggleVisibility(e)
    }
    toggleBPVisibilityBySkinInfo(e, t) {
        for (const [r,n] of this.breathPoints.entries())
            n.skinInfo == e && n.toggleVisibility(t)
    }
    toggleBPVisibilityById(e, t) {
        const r = this.getBreathPoint(e);
        r != null && r.toggleVisibility(t)
    }
    getBreathPointBySkinInfo(e) {
        const t = [];
        for (const [r,n] of this.breathPoints.entries())
            n.skinInfo == e && t.push(n);
        return t
    }
    getAllBreathPoint() {
        return this.breathPoints
    }
    getBreathPoint(e) {
        return this.breathPoints.get(e)
    }
    delete(e) {
        const t = this.breathPoints.get(e);
        if (t != null) {
            t.dispose(),
            this._allIds.delete(e);
            const r = this.materialMap.get(t._type);
            r != null && (r.count = r.count - 1,
            r.count <= 0 && (r.count = 0,
            r.texture.dispose(),
            r.mat.dispose(!0, !0),
            this.materialMap.delete(t._type))),
            this.breathPoints.delete(e)
        }
    }
    castRay(e) {
        var s;
        e = ue4Position2Xverse({
            x: e.x,
            y: e.y,
            z: e.z
        });
        const t = new BABYLON.Vector3(0,-1,0)
          , r = new BABYLON.Ray(e,t,length)
          , n = []
          , o = (s = this._sceneManager) == null ? void 0 : s.getGround({
            x: e.x,
            y: e.y,
            z: e.z
        });
        let a = r.intersectsMeshes(o);
        if (a.length > 0) {
            const l = a[0];
            if (l && l.pickedMesh) {
                const u = l.distance;
                t.y = 1;
                const c = r.intersectsMeshes(n);
                let h = 1e8;
                if (c.length > 0) {
                    const f = c[0];
                    return f && f.pickedMesh && (h = -f.distance),
                    h == 1e8 ? u : Math.abs(h) < Math.abs(u) ? h : u
                }
            }
        } else if (t.y = 1,
        a = r.intersectsMeshes(n),
        a.length > 0) {
            const l = a[0];
            if (l && l.pickedMesh)
                return l.distance
        }
        return 0
    }
    changePickable(e) {
        for (const [t,r] of this.breathPoints.entries())
            r.changePickable(e)
    }
    clearBreathPoints(e) {
        logger.info(`[Engine] clearBreathPoints: ${e}`);
        for (const [t,r] of this.breathPoints.entries())
            (r._type == e || r._id == e) && this.delete(r._id)
    }
    clearBreathPointsBySkinInfo(e) {
        logger.info(`[Engine] clearBreathPointsBySkinInfo: ${e}`);
        for (const [t,r] of this.breathPoints.entries())
            r.skinInfo == e && this.delete(r._id)
    }
    clearAllBreathPoints() {
        logger.info("[Engine] ClearAllBreathPoints");
        for (const [e,t] of this.breathPoints.entries())
            this.delete(t._id)
    }
    _getMainPlayerPosition() {
        var r;
        const e = this._sceneManager.cameraComponent.MainCamera.position
          , t = this._sceneManager.avatarComponent.getMainAvatar();
        if (t != null && t != null) {
            const n = (r = t == null ? void 0 : t.rootNode) == null ? void 0 : r.position;
            if (n != null)
                return n
        }
        return e
    }
    changeBreathPointPose(e, t, r) {
        const n = new BABYLON.Vector3(e.position.x,e.position.y,e.position.z);
        if (this.breathPoints.get(r) != null) {
            logger.info(`[Engine] changeBreathPointPose, id:${r}`);
            const o = this.breathPoints.get(r)
              , a = o.mesh.position;
            let s = a.subtract(n);
            s = BABYLON.Vector3.Normalize(s);
            const l = BABYLON.Vector3.Distance(a, n)
              , u = new Ray(n,s,l)
              , c = this._scene.multiPickWithRay(u);
            if (c) {
                for (let h = 0; h < c.length; h++)
                    if (c[h].pickedMesh != null && t.mesh.name.indexOf(c[h].pickedMesh.name) >= 0) {
                        const f = c[h].pickedPoint;
                        o.mesh.position = n.add(f.subtract(n).scale(.99)),
                        this.breathPoints.set(r, o)
                    }
            }
        } else
            logger.warn(`[Engine] changeBreathPointPose, id:${r} is not existing!`)
    }
}