import XCameraComponent from "./XCameraComponent.js"
import XStaticMeshComponent from "./XStaticMeshComponent.js"
import XMaterialComponent from "./XMaterialComponent.js"
import XStats from "./XStats.js"
import XBreathPointManager from "./XBreathPointManager.js"
import XDecalManager from "./XDecalManager.js"
import XAvatarManager from "./XAvatarManager.js"
import XBillboardManager from "./XBillboardManager.js"
import XLightManager from "./XLightManager.js"
import XEngineRunTimeStats from "./XEngineRunTimeStats.js"
import EShaderMode from "./enum/EShaderMode.js"
import defaultLog from "./defaultLog.js"
import Logger from "./Logger.js"

const logger = new Logger('XSceneManager')

const getAlphaWidthMap = (i,e)=>{
    const t = new BABYLON.DynamicTexture("test",3,e)
      , r = new Map;
    for (let n = 32; n < 127; n++) {
        const o = String.fromCodePoint(n)
          , a = 2 + "px " + i;
        t.drawText(o, null, null, a, "#000000", "#ffffff", !0);
        const s = t.getContext();
        s.font = a;
        const l = s.measureText(o).width;
        r.set(n, l)
    }
    return t.dispose(),
    r
}

export default class XSceneManager {
    constructor(e, t) {
        E(this, "scene");
        E(this, "engine");
        E(this, "canvas");
        E(this, "gl");
        E(this, "_yuvInfo");
        E(this, "cameraParam");
        E(this, "shaderMode");
        E(this, "panoInfo");
        E(this, "_initEngineScaleNumber");
        E(this, "_forceKeepVertical", !1);
        E(this, "_currentShader");
        E(this, "_currentPanoId");
        E(this, "_renderStatusCheckCount", 0);
        E(this, "_renderStatusNotChecktCount", 0);
        E(this, "_nonlinearCanvasResize", !1);
        E(this, "_bChangeEngineSize", !0);
        E(this, "_cameraManager");
        E(this, "_lowpolyManager");
        E(this, "_materialManager");
        E(this, "_statisticManager");
        E(this, "_breathPointManager");
        E(this, "_skytv");
        E(this, "_mv", []);
        E(this, "_decalManager");
        E(this, "_lightManager");
        E(this, "_avatarManager");
        E(this, "urlTransformer");
        E(this, "_billboardManager");
        E(this, "_backgroundImg");
        E(this, "engineRunTimeStats");
        E(this, "uploadHardwareSystemInfo", ()=>{
            const e = this.statisticComponent.getHardwareRenderInfo()
              , t = this.statisticComponent.getSystemInfo()
              , r = {
                driver: t.driver,
                vender: t.vender,
                webgl: t.version,
                os: t.os
            };
            logger.warn(JSON.stringify(e)),
            logger.warn(JSON.stringify(r))
        }
        );
        E(this, "addNewLowPolyMesh", async(e,t)=>(this._currentShader == null && await this.initSceneManager(),
        this._lowpolyManager.addNewLowPolyMesh(e, t, this._currentShader)));
        E(this, "initSceneManager", async()=>(await this._materialManager.initMaterial(),this.applyShader()));
        E(this, "registerAfterRender", ()=>{
            var e;
            if (this._forceKeepVertical) {
                const t = this.canvas.width
                  , r = this.canvas.height;
                let n = 0
                  , o = [[0, 0, 0, 0], [0, 0, 0, 0]];
                if (((e = this._cameraManager.MainCamera) == null ? void 0 : e.fovMode) === BABYLON.Camera.FOVMODE_HORIZONTAL_FIXED ? (n = Math.ceil((r - this._yuvInfo.height * t / this._yuvInfo.width) / 2),
                o = [[0, 0, t, n], [0, r - n, t, n]]) : (n = Math.ceil((t - this._yuvInfo.width * r / this._yuvInfo.height) / 2),
                o = [[0, 0, n, r], [t - n, 0, n, r]]),
                n > 0) {
                    this.gl.enable(this.gl.SCISSOR_TEST);
                    for (let a = 0; a < o.length; ++a)
                        this.gl.scissor(o[a][0], o[a][1], o[a][2], o[a][3]),
                        this.gl.clearColor(0, 0, 0, 1),
                        this.gl.clear(this.gl.COLOR_BUFFER_BIT);
                    this.gl.disable(this.gl.SCISSOR_TEST)
                }
            }
        }
        );
        E(this, "resetRender", ()=>{
            this.scene.environmentTexture && (this.scene.environmentTexture._texture ? this.lightComponent.setIBL(this.scene.environmentTexture._texture.url) : this.scene.environmentTexture.url && this.lightComponent.setIBL(this.scene.environmentTexture.url))
        }
        );
        const r = /iphone|ipad/gi.test(window.navigator.userAgent) || t.disableWebGL2
          , n = new BABYLON.Engine(e,!0,{
            preserveDrawingBuffer: !0,
            stencil: !0,
            disableWebGL2Support: r
        },!0)
          , o = new BABYLON.Scene(n);
        this.scene = o,
        this.engine = n,
        this.canvas = e,
        this.scene.clearColor = new BABYLON.Color4(.7,.7,.7,1),
        this.engine.getCaps().parallelShaderCompile = void 0,
        this._initEngineScaleNumber = this.engine.getHardwareScalingLevel(),
        this.engine.enableOfflineSupport = !1,
        this.engine.doNotHandleContextLost = !0,
        this.scene.clearCachedVertexData(),
        this.scene.cleanCachedTextureBuffer(),
        // 周恩光加 调试用工具栏
        this.scene.debugLayer.show({
            embedMode: true,
        })
        this.urlTransformer = t.urlTransformer || (s=>Promise.resolve(s)),
        t.logger && defaultLog.setLogger(t.logger),
        this.gl = e.getContext("webgl2", {
            preserveDrawingBuffer: !0
        }) || e.getContext("webgl", {
            preserveDrawingBuffer: !0
        }) || e.getContext("experimental-webgl", {
            preserveDrawingBuffer: !0
        }),
        this.gl.pixelStorei(this.gl.UNPACK_ALIGNMENT, 1),
        this._currentPanoId = 0,
        t.forceKeepVertical != null && (this._forceKeepVertical = t.forceKeepVertical),
        t.panoInfo != null && (this.panoInfo = t.panoInfo),
        t.shaderMode != null && (this.shaderMode = t.shaderMode),
        t.yuvInfo != null ? this._yuvInfo = t.yuvInfo : this._yuvInfo = {
            width: t.videoResOriArray[0].width,
            height: t.videoResOriArray[0].height,
            fov: 50
        },
        t.cameraParam != null && (this.cameraParam = t.cameraParam),
        t.nonlinearCanvasResize != null && (this._nonlinearCanvasResize = t.nonlinearCanvasResize),
        this._cameraManager = new XCameraComponent(this.canvas,this.scene,{
            cameraParam: this.cameraParam,
            yuvInfo: this._yuvInfo,
            forceKeepVertical: this._forceKeepVertical
        }),
        this._lowpolyManager = new XStaticMeshComponent(this),
        this._materialManager = new XMaterialComponent(this,{
            videoResOriArray: t.videoResOriArray,
            yuvInfo: this._yuvInfo,
            panoInfo: this.panoInfo,
            shaderMode: this.shaderMode
        }),
        this._statisticManager = new XStats(this),
        this._breathPointManager = new XBreathPointManager(this),
        this._decalManager = new XDecalManager(this),
        this._avatarManager = new XAvatarManager(this),
        this._billboardManager = new XBillboardManager(this),
        this.billboardComponent.loadBackGroundTexToIDB(),
        this._lightManager = new XLightManager(this),
        this.postprocessing(),
        this.initSceneManager(),
        this.engineRunTimeStats = new XEngineRunTimeStats,
        /iphone/gi.test(window.navigator.userAgent) && window.devicePixelRatio && window.devicePixelRatio === 3 && window.screen.width === 375 && window.screen.height === 812 ? this.engine.setHardwareScalingLevel(this._initEngineScaleNumber * 2) : this.engine.setHardwareScalingLevel(this._initEngineScaleNumber * 1.8),
        this.scene.registerBeforeRender(()=>{
            this._nonlinearCanvasResize && this._bChangeEngineSize && (this.setEngineSize(this._yuvInfo),
            this._bChangeEngineSize = !1)
        }
        ),
        this.scene.registerAfterRender(()=>{
            this._nonlinearCanvasResize || this.registerAfterRender()
        }
        ),
        window.addEventListener("resize", ()=>{
            this._nonlinearCanvasResize ? this._bChangeEngineSize = !0 : this.engine.resize()
        }
        ),
        XBillboardManager.alphaWidthMap = getAlphaWidthMap("Arial", this.scene),
        this.uploadHardwareSystemInfo()
    }
    get yuvInfo() {
        return this.getCurrentShaderMode() == 1 ? this._yuvInfo : {
            width: -1,
            height: -1,
            fov: -1
        }
    }
    set yuvInfo(e) {
        this.getCurrentShaderMode() == 1 && (this._yuvInfo = e,
        this._cameraManager.cameraFovChange(e))
    }
    get mainScene() {
        return this.scene
    }
    get cameraComponent() {
        return this._cameraManager
    }
    get staticmeshComponent() {
        return this._lowpolyManager
    }
    get materialComponent() {
        return this._materialManager
    }
    get statisticComponent() {
        return this._statisticManager
    }
    get avatarComponent() {
        return this._avatarManager
    }
    get lightComponent() {
        return this._lightManager
    }
    get Engine() {
        return this.engine
    }
    get Scene() {
        return this.scene
    }
    get billboardComponent() {
        return this._billboardManager
    }
    get breathPointComponent() {
        return this._breathPointManager
    }
    get skytvComponent() {
        return this._skytv
    }
    get mvComponent() {
        return this._mv
    }
    get decalComponent() {
        return this._decalManager
    }
    get currentShader() {
        return this._currentShader
    }
    get initEngineScaleNumber() {
        return this._initEngineScaleNumber
    }
    setImageQuality(e) {
        e == 0 ? (this.engine.setHardwareScalingLevel(this._initEngineScaleNumber * 1.8),
        logger.info("[Engine] change image quality to low, [" + this._initEngineScaleNumber * 1.8 + "]")) : e == 1 ? (this.engine.setHardwareScalingLevel(this._initEngineScaleNumber * 1.5),
        logger.info("[Engine] change image quality to mid, [" + this._initEngineScaleNumber * 1.5 + "]")) : e == 2 && (this.engine.setHardwareScalingLevel(this._initEngineScaleNumber * 1),
        logger.info("[Engine] change image quality to high, [" + this._initEngineScaleNumber * 1 + "]"))
    }
    setNonlinearCanvasResize(e) {
        this._nonlinearCanvasResize = e,
        this._bChangeEngineSize = e,
        e || this.engine.resize()
    }
    setBackgroundColor(e) {
        this.scene.clearColor = new Color4(e.r,e.g,e.b,e.a)
    }
    setBackgroundImg(e) {
        return this._backgroundImg != null && this._backgroundImg.url == e ? Promise.resolve(!0) : new Promise((t,r)=>{
            this.urlTransformer(e).then(n=>{
                this._backgroundImg == null ? this._backgroundImg = {
                    layer: new Layer("tex_background_" + Date.now(),n,this.Scene,!0),
                    url: e
                } : this._backgroundImg.url != e && this._backgroundImg.layer != null && this._backgroundImg.layer.texture != null && (this._backgroundImg.layer.texture.updateURL(n),
                this._backgroundImg.layer.name = "tex_background_" + Date.now(),
                this._backgroundImg.url = e),
                t(!0)
            }
            ).catch(n=>{
                logger.error(`[Engine] set background image Error: ${n}`),
                r(`[Engine] set background image Error: ${n}`)
            }
            )
        }
        )
    }
    cleanTheWholeScene() {
        const e = this.scene.getFrameId();
        this.scene.onBeforeRenderObservable.clear(),
        this.scene.onAfterRenderObservable.clear(),
        this.scene.clearCachedVertexData(),
        this.scene.cleanCachedTextureBuffer(),
        this.scene.registerBeforeRender(()=>{
            this.scene.getFrameId() - e > 5 && this.scene.dispose()
        }
        )
    }
    getAreaAvatar(e, t) {
        const r = [];
        return this._avatarManager.getAvatarList().forEach(n=>{
            const o = e
              , a = n.position;
            a && o && calcDistance3D(o, a) < t && r.push(n.id)
        }
        ),
        r
    }
    setEngineSize(e) {
        const t = e.width
          , r = e.height
          , n = this.canvas.width;
        this.canvas.height,
        this.engine.setSize(Math.round(n), Math.round(n * (r / t)))
    }
    getCurrentShaderMode() {
        return this._currentShader === this._materialManager.getDefaultShader() ? 0 : this._currentShader === this._materialManager.getPureVideoShader() ? 1 : 2
    }
    addSkyTV(e, t) {
        return this._skytv = new XTelevision(this.scene,e,this,t),
        this._skytv
    }
    addMv(e, t) {
        this._mv.push(new XTelevision(this.scene,e,this,t))
    }
    addMeshInfo(e) {
        this._lowpolyManager.setMeshInfo(e)
    }
    applyShader() {
        return new Promise((e,t)=>{
            this.shaderMode == EShaderMode.videoAndPano || this.shaderMode == EShaderMode.video ? this.changeVideoShaderForLowModel() : this.shaderMode == EShaderMode.default && this.changeDefaultShaderForLowModel(),
            e(!0)
        }
        )
    }
    changeHardwareScaling(e) {
        e < 1 ? e = 1 : e > 2.5 && (e = 2.5),
        this._bChangeEngineSize = !0,
        this.engine.setHardwareScalingLevel(this._initEngineScaleNumber * e)
    }
    getCurrentUsedPanoId() {
        return this._currentPanoId
    }
    render() {
        try {
            this.scene.render()
        } catch (e) {
            throw logger.error(`[Engine] Render Error: ${e}`),
            e
        }
    }
    isReadyToRender(e) {
        const {checkMesh: t=!0, checkEffect: r=!1, checkPostProgress: n=!1, checkParticle: o=!1, checkAnimation: a=!1, materialNameWhiteLists: s=[]} = e;
        if (this.scene._isDisposed)
            return logger.error("[Engine] this.scene._isDisposed== false "),
            !1;
        let l;
        const u = this.scene.getEngine();
        if (r && !u.areAllEffectsReady())
            return logger.error("[Engine] engine.areAllEffectsReady == false"),
            !1;
        if (a && this.scene._pendingData.length > 0)
            return logger.error("[Engine] scene._pendingData.length > 0 && animation error"),
            !1;
        if (t) {
            for (l = 0; l < this.scene.meshes.length; l++) {
                const c = this.scene.meshes[l];
                if (!c.isEnabled() || !c.subMeshes || c.subMeshes.length === 0 || c != null && c.material != null && !(c.material.name.startsWith("Pure") || c.material.name.startsWith("Pano")))
                    continue;
                if (!c.isReady(!0))
                    return logger.error(`[Engine] scene. mesh isReady == false, mesh name:${c.name}, mesh xtype: ${c == null ? void 0 : c.xtype}, mesh xgroup: ${c == null ? void 0 : c.xgroup}, mesh xskinInfo: ${c == null ? void 0 : c.xskinInfo}`),
                    !1;
                const h = c.hasThinInstances || c.getClassName() === "InstancedMesh" || c.getClassName() === "InstancedLinesMesh" || u.getCaps().instancedArrays && c.instances.length > 0;
                for (const f of this.scene._isReadyForMeshStage)
                    if (!f.action(c, h))
                        return logger.error(`[Engine] scene._isReadyForMeshStage == false, mesh name:${c.name}, mesh xtype: ${c == null ? void 0 : c.xtype}, mesh xgroup: ${c == null ? void 0 : c.xgroup}, mesh xskinInfo: ${c == null ? void 0 : c.xskinInfo}`),
                        !1
            }
            for (l = 0; l < this.scene.geometries.length; l++)
                if (this.scene.geometries[l].delayLoadState === 2)
                    return logger.error("[Engine] geometry.delayLoadState === 2"),
                    !1
        }
        if (n) {
            if (this.scene.activeCameras && this.scene.activeCameras.length > 0) {
                for (const c of this.scene.activeCameras)
                    if (!c.isReady(!0))
                        return logger.error("[Engine] camera not ready === false, ", c.name),
                        !1
            } else if (this.scene.activeCamera && !this.scene.activeCamera.isReady(!0))
                return logger.error("[Engine] activeCamera ready === false, ", this.scene.activeCamera.name),
                !1
        }
        if (o) {
            for (const c of this.scene.particleSystems)
                if (!c.isReady())
                    return logger.error("[Engine] particleSystem ready === false, ", c.name),
                    !1
        }
        return !0
    }
    changePanoShaderForLowModel(e) {
        return logger.info(`[Engine] changePanoShaderForLowModel: ${e}`),
        this._materialManager.allowYUVUpdate(),
        new Promise((t,r)=>{
            this._materialManager._isInDynamicRange(e) == !1 && r(!1),
            this._currentPanoId = e,
            this._currentShader = this._materialManager.getDynamicShader(e),
            this.changeShaderForLowModel().then(()=>{
                t(!0)
            }
            )
        }
        )
    }
    changeVideoShaderForLowModel() {
        return logger.info("[Engine] changeVideoShaderForLowModel"),
        this._currentShader = this._materialManager.getPureVideoShader(),
        this._materialManager.allowYUVUpdate(),
        this.changeShaderForLowModel()
    }
    changeDefaultShaderForLowModel() {
        return logger.info("[Engine] changeDefaultShaderForLowModel"),
        this._currentShader = this._materialManager.getDefaultShader(),
        this._materialManager.stopYUVUpdate(),
        this.changeShaderForLowModel()
    }
    changeShaderForLowModel() {
        return new Promise((e,t)=>{
            this._lowpolyManager.getMeshes().forEach(r=>{
                r.setMaterial(this._currentShader)
            }
            ),
            this._lowpolyManager.getCgMesh().mesh.material = this._currentShader,
            e(!0)
        }
        )
    }
    setIBL(e) {
        this._lightManager.setIBL(e)
    }
    postprocessing() {
        const e = new BABYLON.DefaultRenderingPipeline("default",!0,this.scene);
        e.imageProcessingEnabled = !1,
        e.bloomEnabled = !0,
        e.bloomThreshold = 1,
        e.bloomWeight = 1,
        e.bloomKernel = 64,
        e.bloomScale = .1
    }
    getGround(e) {
        const t = this._lowpolyManager.getMeshes()
          , r = [];
        return t.forEach(n=>{
            n.mesh.name.indexOf("SM_Stage") >= 0 && r.push(n.mesh)
        }
        ),
        this.Scene.meshes.forEach(n=>{
            n.name.split("_")[0] === "ground" && r.push(n)
        }
        ),
        r
    }
}