"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.renderSchematic = void 0;
const schematicjs_1 = require("@enginehub/schematicjs");
const loader_1 = require("./model/loader");
const resourceLoader_1 = require("../resource/resourceLoader");
const types_1 = require("./model/types");
const utils_1 = require("./utils");
const core_1 = require("@babylonjs/core");
const parser_1 = require("./model/parser");
const shapes_1 = require("./shapes");
const CASSETTE_DECK_URL = `https://services.enginehub.org/cassette-deck/minecraft-versions/find?dataVersion=`;
const URL_1_13 = 'https://launcher.mojang.com/v1/objects/c0b970952cdd279912da384cdbfc0c26e6c6090b/client.jar';
function getClientJarUrlDefault({ dataVersion, corsBypassUrl, }) {
    var _a, _b;
    return __awaiter(this, void 0, void 0, function* () {
        const versionManifestFile = dataVersion
            ? yield (yield fetch(`${corsBypassUrl}${CASSETTE_DECK_URL}${dataVersion}`)).json()
            : undefined;
        return `${corsBypassUrl}${(_b = (_a = versionManifestFile === null || versionManifestFile === void 0 ? void 0 : versionManifestFile[0]) === null || _a === void 0 ? void 0 : _a.clientJarUrl) !== null && _b !== void 0 ? _b : URL_1_13}`;
    });
}
function renderSchematic(canvas, schematic, { corsBypassUrl, getClientJarUrl = getClientJarUrlDefault, resourcePacks, size, orbit = true, orbitSpeed = 0.02, renderArrow = false, renderBars = false, antialias = false, backgroundColor = 0xffffff, debug = false, disableAutoRender = false, }) {
    return __awaiter(this, void 0, void 0, function* () {
        const engine = new core_1.Engine(canvas, antialias, {
            alpha: backgroundColor !== 'transparent',
            powerPreference: 'high-performance',
        });
        if (size) {
            if (typeof size === 'number') {
                engine.setSize(size, size);
                console.warn('Usage of deprecated `size: number` property in Schematic renderer.');
            }
            else {
                engine.setSize(size.width, size.height);
            }
        }
        const scene = new core_1.Scene(engine, {
            useGeometryUniqueIdsMap: true,
            useClonedMeshMap: true,
        });
        scene.performancePriority = core_1.ScenePerformancePriority.Intermediate;
        scene.renderingManager.maintainStateBetweenFrames = true;
        scene.skipFrustumClipping = true;
        scene.ambientColor = new core_1.Color3(0.5, 0.5, 0.5);
        if (backgroundColor !== 'transparent') {
            scene.clearColor = core_1.Color4.FromHexString(`#${backgroundColor.toString(16)}FF`);
        }
        else {
            scene.clearColor = new core_1.Color4(0, 0, 0, 0);
        }
        let hasDestroyed = false;
        const camera = new core_1.ArcRotateCamera('camera', -Math.PI / 2, Math.PI / 2.5, 10, new core_1.Vector3(0, 0, 0), scene);
        camera.wheelPrecision = 50;
        const light = new core_1.HemisphericLight('light1', new core_1.Vector3(1, 1, 0), scene);
        light.specular = new core_1.Color3(0, 0, 0);
        const render = () => {
            if (hasDestroyed) {
                return;
            }
            scene.render();
        };
        if (!disableAutoRender) {
            engine.runRenderLoop(render);
        }
        const loadedSchematic = (0, schematicjs_1.loadSchematic)((0, utils_1.parseNbt)(schematic));
        const { width: worldWidth, height: worldHeight, length: worldLength, } = loadedSchematic;
        const cameraOffset = Math.max(worldWidth, worldLength, worldHeight) / 2 + 1;
        camera.radius = cameraOffset * 3;
        const resourceLoader = yield (0, resourceLoader_1.getResourceLoader)([
            yield getClientJarUrl({
                dataVersion: loadedSchematic.dataVersion,
                corsBypassUrl,
            }),
            ...(resourcePacks !== null && resourcePacks !== void 0 ? resourcePacks : []),
        ]);
        const modelLoader = (0, loader_1.getModelLoader)(resourceLoader);
        const blockModelLookup = new Map();
        for (const block of loadedSchematic.blockTypes) {
            if (utils_1.INVISIBLE_BLOCKS.has(block.type)) {
                continue;
            }
            const blockState = yield (0, parser_1.loadBlockStateDefinition)(block.type, resourceLoader);
            const blockModelData = modelLoader.getBlockModelData(block, blockState);
            if (!blockModelData.models.length) {
                console.log(blockState);
                continue;
            }
            blockModelLookup.set(block, blockModelData);
        }
        core_1.Mesh.INSTANCEDMESH_SORT_TRANSPARENT = true;
        scene.blockMaterialDirtyMechanism = true;
        for (const pos of loadedSchematic) {
            const { x, y, z } = pos;
            const block = loadedSchematic.getBlock(pos);
            if (!block || utils_1.INVISIBLE_BLOCKS.has(block.type)) {
                continue;
            }
            const modelData = blockModelLookup.get(block);
            if (!modelData) {
                continue;
            }
            let anyVisible = false;
            for (const face of types_1.POSSIBLE_FACES) {
                const faceOffset = (0, utils_1.faceToFacingVector)(face);
                const offBlock = loadedSchematic.getBlock({
                    x: x + faceOffset[0],
                    y: y + faceOffset[1],
                    z: z + faceOffset[2],
                });
                if (!offBlock || utils_1.NON_OCCLUDING_BLOCKS.has(offBlock.type)) {
                    anyVisible = true;
                    break;
                }
            }
            if (!anyVisible) {
                continue;
            }
            const option = modelLoader.getModelOption(modelData);
            const meshes = yield modelLoader.getModel(option, block, scene);
            for (const mesh of meshes) {
                if (!mesh) {
                    continue;
                }
                mesh.position.x += -worldWidth / 2 + x + 0.5;
                mesh.position.y += -worldHeight / 2 + y + 0.5;
                mesh.position.z += -worldLength / 2 + z + 0.5;
                mesh.freezeWorldMatrix();
                scene.addMesh(mesh);
            }
        }
        scene.blockMaterialDirtyMechanism = false;
        if (renderArrow) {
            (0, shapes_1.addArrowToScene)(scene, cameraOffset);
        }
        if (renderBars) {
            (0, shapes_1.addBarsToScene)(scene, cameraOffset, worldWidth, worldHeight, worldLength);
        }
        scene.createOrUpdateSelectionOctree();
        scene.freezeMaterials();
        if (debug) {
            scene.debugLayer.show();
        }
        blockModelLookup.clear();
        resourceLoader.clearCache();
        modelLoader.clearCache();
        camera.attachControl(false, true);
        if (orbit) {
            scene.registerBeforeRender(() => {
                camera.alpha += orbitSpeed;
            });
        }
        return {
            resize(size) {
                engine.setSize(size, size);
            },
            setSize(width, height) {
                engine.setSize(width, height);
            },
            destroy() {
                engine.dispose();
                hasDestroyed = true;
            },
            render,
            getEngine() {
                return engine;
            },
        };
    });
}
exports.renderSchematic = renderSchematic;
//# sourceMappingURL=renderer.js.map