"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.buildHumanoidR6Entity = buildHumanoidR6Entity;
const materials_1 = require("../../materials");
const face_map_1 = require("./textures/face-map");
const artifacts_1 = require("./textures/artifacts");
function applyR6PartNodeGeometry(partKey, node, sourceSize, hasSourceSize, basePosition, torsoWidth, pivotX) {
    if (partKey === 'arm_left' || partKey === 'arm_right') {
        const sign = partKey === 'arm_right' ? -1 : 1;
        const [width, height, depth] = sourceSize;
        const length = height;
        const thicknessY = width;
        const thicknessZ = depth;
        const targetSize = [length, thicknessY, thicknessZ];
        node.size = [...targetSize];
        node.center = [sign * (length / 2 - thicknessY / 2), 0, 0];
        node.position = [
            sign * (torsoWidth / 2 + thicknessY / 2),
            basePosition[1],
            basePosition[2],
        ];
        return targetSize;
    }
    if (partKey === 'leg_left' || partKey === 'leg_right') {
        const [width, height, depth] = sourceSize;
        const targetSize = [width, height, depth];
        node.size = [...targetSize];
        node.center = [0, -height / 2, 0];
        node.position = [Number.isFinite(pivotX) ? pivotX : basePosition[0], basePosition[1], basePosition[2]];
        return targetSize;
    }
    const templateSize = (node.size ?? sourceSize).map(value => Number(value));
    const targetSize = hasSourceSize ? sourceSize : templateSize;
    node.size = [...targetSize];
    if (Array.isArray(node.center)) {
        node.center = node.center.map(value => Number(value));
    }
    node.position = [...basePosition];
    return targetSize;
}
function assignPrimitiveFaceTexture(primitive, face, textureId, overlay = false) {
    switch (face) {
        case 'up':
            if (overlay) {
                primitive.topOverlay = textureId;
            }
            else {
                primitive.top = textureId;
            }
            return;
        case 'down':
            if (overlay) {
                primitive.bottomOverlay = textureId;
            }
            else {
                primitive.bottom = textureId;
            }
            return;
        case 'north':
            if (overlay) {
                primitive.northOverlay = textureId;
            }
            else {
                primitive.north = textureId;
            }
            return;
        case 'south':
            if (overlay) {
                primitive.southOverlay = textureId;
            }
            else {
                primitive.south = textureId;
            }
            return;
        case 'east':
            if (overlay) {
                primitive.eastOverlay = textureId;
            }
            else {
                primitive.east = textureId;
            }
            return;
        case 'west':
            if (overlay) {
                primitive.westOverlay = textureId;
            }
            else {
                primitive.west = textureId;
            }
            return;
    }
}
function buildHumanoidR6Entity(options) {
    const { template, source, outputId } = options;
    const entity = JSON.parse(JSON.stringify(template));
    entity.id = outputId;
    entity.geometry.id = `${outputId}_geometry`;
    const textureScaleX = source.textureScaleX ?? 1;
    const textureScaleY = source.textureScaleY ?? 1;
    const nodesById = new Map();
    (function collect(node) {
        nodesById.set(node.id, node);
        node.children.forEach((child) => collect(child));
    })(entity.geometry.root);
    const primitives = [];
    const materials = [];
    const patterns = [];
    const textures = [];
    const materialEnsurer = (0, materials_1.createMaterialEnsurer)(materials, { prefix: 'humanoid_r6_mat' });
    const ensureMaterial = (color) => materialEnsurer.ensure(color);
    const registerPrimitive = (nodeId, primitive) => {
        const node = nodesById.get(nodeId);
        if (!node) {
            throw new Error(`Template node "${nodeId}" is missing`);
        }
        node.mesh = primitive.id;
        primitives.push(primitive);
    };
    const torsoNode = nodesById.get('torso');
    const torsoSource = source.parts.get('torso');
    const torsoWidth = Number(torsoSource?.base.cube.size?.[0] ?? torsoNode?.size?.[0] ?? 8);
    const partToNode = {
        torso: 'torso',
        head: 'head',
        arm_left: 'arm_left',
        arm_right: 'arm_right',
        leg_left: 'leg_left',
        leg_right: 'leg_right',
    };
    Object.keys(partToNode).forEach(partKey => {
        const part = source.parts.get(partKey);
        if (!part) {
            throw new Error(`Conversion source missing part "${partKey}"`);
        }
        const nodeId = partToNode[partKey];
        const node = nodesById.get(nodeId);
        if (!node) {
            throw new Error(`Template missing node "${nodeId}"`);
        }
        const sourceSizeRaw = part.base.cube.size ?? [0, 0, 0];
        const sourceSize = sourceSizeRaw.map(value => Number(value));
        const hasSourceSize = sourceSize.every(value => Number.isFinite(value) && value > 0);
        const basePosition = [
            Number(node.position?.[0] ?? 0),
            Number(node.position?.[1] ?? 0),
            Number(node.position?.[2] ?? 0),
        ];
        const pivotX = Number(part.base.bone.pivot?.[0]);
        const targetSize = applyR6PartNodeGeometry(partKey, node, sourceSize, hasSourceSize, basePosition, torsoWidth, pivotX);
        const torsoFaceTransforms = face_map_1.PART_FACE_TRANSFORMS['torso'];
        if (!torsoFaceTransforms) {
            throw new Error('Missing torso face transforms');
        }
        const faceTransforms = (face_map_1.PART_FACE_TRANSFORMS[partKey] ?? torsoFaceTransforms);
        const primitive = {
            id: `${nodeId}_primitive`,
            kind: 'textured_box',
            size: [...targetSize],
        };
        let primitiveNeedsCutout = false;
        ['up', 'down', 'north', 'south', 'east', 'west'].forEach(face => {
            const transform = faceTransforms[face] ?? torsoFaceTransforms[face];
            const expectedDimensions = (0, face_map_1.getFaceDimensions)(targetSize, face);
            const scaledExpected = [
                Math.max(1, Math.round(expectedDimensions[0] * textureScaleX)),
                Math.max(1, Math.round(expectedDimensions[1] * textureScaleY)),
            ];
            const baseArtifacts = (0, artifacts_1.createFaceArtifacts)({
                baseId: `${nodeId}_${face}`,
                face,
                source: part.base,
                transform,
                expectedSize: scaledExpected,
                textureWidth: source.textureWidth,
                textureHeight: source.textureHeight,
                texturePixels: source.texturePixels,
                ensureMaterial,
            });
            if (!baseArtifacts.pattern || !baseArtifacts.texture) {
                throw new Error(`Base layer for "${nodeId}" face "${face}" produced no visible pixels`);
            }
            patterns.push(baseArtifacts.pattern);
            textures.push(baseArtifacts.texture);
            if (baseArtifacts.hasTransparency) {
                primitiveNeedsCutout = true;
            }
            assignPrimitiveFaceTexture(primitive, face, baseArtifacts.texture.id);
            if (part.overlay) {
                const overlayArtifacts = (0, artifacts_1.createFaceArtifacts)({
                    baseId: `${nodeId}_${face}`,
                    face,
                    source: part.overlay,
                    transform,
                    expectedSize: scaledExpected,
                    textureWidth: source.textureWidth,
                    textureHeight: source.textureHeight,
                    texturePixels: source.texturePixels,
                    ensureMaterial,
                    variant: 'overlay',
                });
                if (overlayArtifacts.pattern && overlayArtifacts.texture && overlayArtifacts.hasVisiblePixels) {
                    patterns.push(overlayArtifacts.pattern);
                    textures.push(overlayArtifacts.texture);
                    assignPrimitiveFaceTexture(primitive, face, overlayArtifacts.texture.id, true);
                }
            }
        });
        if (primitiveNeedsCutout) {
            primitive.renderMode = 'cutout';
        }
        registerPrimitive(nodeId, primitive);
    });
    entity.primitives = primitives;
    entity.materials = materials;
    entity.patterns = patterns;
    entity.textures = textures;
    return entity;
}
//# sourceMappingURL=builder.js.map