// EN: 3D STL Viewer for the BBW University Research Platform using Three.js
//     - Renders STL files in a web canvas with color mapping by material.
//     - Allows interactive lighting and model inspection.
// DE: 3D STL-Viewer für die Forschungsplattform der bbw Hochschule mit Three.js
//     - Rendert STL-Dateien im Web-Canvas mit Farbcodierung nach Material.
//     - Ermöglicht interaktive Beleuchtung und Modellinspektion.

// OrbitControls is loaded as UMD in the HTML
// OrbitControls wird als UMD im HTML geladen

/**
 * EN: Initializes the 3D viewer in the element with id 'viewer'.
 * DE: Initialisiert den 3D-Viewer im Element mit der ID 'viewer'.
 * @param {File} stlFile - EN: The STL file to visualize. / DE: Die zu visualisierende STL-Datei.
 */
function showSTLModel(stlFile) {
    // EN: Color mapping for all materials from materialen.csv
    // DE: Farbzuordnung für alle Materialien aus materialen.csv
    const materialColors = {
        // PLA
        "Black (10101)": "#222222",
        "Jade White (10100)": "#f8f8f8",
        "Red (10200)": "#e53935",
        "Bambu Green (10501)": "#4caf50",
        "Blue (10601)": "#1565c0",
        // ABS
        "ABS Black (40101)": "#222222",
        "ABS White (40100)": "#f8f8f8",
        "ABS Red (40200)": "#e53935",
        "ABS Bambu Green (40500)": "#4caf50",
        "ABS Blue (40600)": "#1565c0",
        // TPU
        "Black (53101)": "#222222",
        "White (53100)": "#f8f8f8",
        "Red (53200)": "#e53935",
        "Neon Green (53500)": "#39ff14",
        "Blue (53600)": "#1565c0",
    };
    const root = document.querySelector('.modulo-impresion3d');
    const viewer = document.getElementById('viewer');
    if (!viewer) return;
    const selectedMaterial = root?.dataset.selectedMaterial;
    Array.from(viewer.querySelectorAll('.viewer-msg')).forEach(m => m.remove());
    // EN: Check for material selection
    // DE: Überprüfen Sie die Materialauswahl
    if (!selectedMaterial) {
        // EN: Prompt to select material before viewing 3D model
        // DE: Hinweis, Material auswählen bevor das 3D-Modell angezeigt wird
        viewer.insertAdjacentHTML('beforeend', '<div class="viewer-msg" style="color: #b71c1c; background: #fff3f3; border: 1px solid #b71c1c; border-radius: 6px; margin: 1em auto; padding: 1em; max-width: 350px; text-align: center; font-weight: bold;">Please select a material before viewing the 3D model.<br>Bitte wählen Sie ein Material aus, bevor das 3D-Modell angezeigt wird.</div>');
        return;
    }
    Array.from(viewer.querySelectorAll('.viewer-msg')).forEach(m => m.remove());
    if (!selectedMaterial || !materialColors[selectedMaterial]) {
        // EN: Prompt to select valid material for 3D visualization
        // DE: Hinweis, gültiges Material für die 3D-Visualisierung auswählen
        viewer.insertAdjacentHTML('beforeend', '<div class="viewer-msg" style="color:red; text-align:center;">Please select a valid material to view the 3D model.<br>Bitte wählen Sie ein gültiges Material, um das 3D-Modell anzuzeigen.</div>');
        return;
    }
    Array.from(viewer.querySelectorAll('canvas')).forEach(c => c.remove());
    const scene = new THREE.Scene();
    scene.background = new THREE.Color(0xe0e4ea);
    Array.from(viewer.querySelectorAll('.viewer-msg')).forEach(m => m.remove());
    if (viewer.childNodes.length > 0) {
        Array.from(viewer.childNodes).forEach(node => {
            if (node.nodeType === Node.TEXT_NODE || (node.nodeType === Node.ELEMENT_NODE && node.textContent.trim() === '3D-Viewer')) {
                viewer.removeChild(node);
            }
        });
    }

    // EN: Set up camera
    // DE: Kamera einrichten
    const camera = new THREE.PerspectiveCamera(45, viewer.clientWidth / viewer.clientHeight, 0.1, 1000);
    camera.position.set(0, 0, 80);

    // EN: Set up renderer
    // DE: Renderer einrichten
    const renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setSize(viewer.clientWidth, viewer.clientHeight);
    viewer.appendChild(renderer.domElement);

    // EN: Set up controls
    // DE: Steuerung einrichten
    const controls = new THREE.OrbitControls(camera, renderer.domElement);
    controls.enableDamping = true;
    controls.dampingFactor = 0.08;
    controls.screenSpacePanning = false;
    controls.minDistance = 10;
    controls.maxDistance = 500;
    controls.target.set(0, 0, 0);

    // EN: Use ambient light and a directional light for interactive illumination
    // DE: Umgebungslicht und eine Richtungslichtquelle für interaktive Beleuchtung verwenden
    const ambientLight = new THREE.AmbientLight(0xffffff, 0.7);
    scene.add(ambientLight);

    // EN: Add a directional light for model inspection
    // DE: Richtungslicht für Modellinspektion hinzufügen
    const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
    directionalLight.position.set(0.5, 0.5, 1);
    scene.add(directionalLight);

    // EN: Connect sliders to directional light direction
    // DE: Slider mit Richtungslicht-Richtung verbinden
    setTimeout(() => {
        const lx = document.getElementById('lightX');
        const ly = document.getElementById('lightY');
        const lz = document.getElementById('lightZ');
        function updateLight() {
            const x = parseFloat(lx.value);
            const y = parseFloat(ly.value);
            const z = parseFloat(lz.value);
            directionalLight.position.set(x, y, z);
        }
        if (lx && ly && lz) {
            lx.addEventListener('input', updateLight);
            ly.addEventListener('input', updateLight);
            lz.addEventListener('input', updateLight);
            updateLight();
        }
    }, 0);

    // EN: Load STL geometry and apply color/material
    // DE: STL-Geometrie laden und Farbe/Material anwenden
    const STLLoader = window.THREE?.STLLoader || THREE.STLLoader;
    const loader = new STLLoader();
    const reader = new FileReader();
    reader.onload = function (e) {
        try {
            const geometry = loader.parse(e.target.result);
            console.log('STL geometry:', geometry);
            if (!geometry || !geometry.attributes || !geometry.attributes.position) {
                console.error('La geometría STL está vacía o corrupta');
            }
            const colorHex = materialColors[selectedMaterial] || "#cccccc";
            // EN: Use MeshLambertMaterial for matte, non-shiny appearance
            // DE: MeshLambertMaterial für matte, nicht glänzende Darstellung verwenden
            const material = new THREE.MeshLambertMaterial({ color: colorHex });
            const mesh = new THREE.Mesh(geometry, material);
            mesh.rotation.x = -Math.PI / 2;

            // EN: Center and scale the model for optimal viewing
            // DE: Modell zentrieren und skalieren für optimale Ansicht
            geometry.computeBoundingBox();
            const boundingBox = geometry.boundingBox;
            const center = new THREE.Vector3();
            boundingBox.getCenter(center);

            const size = new THREE.Vector3();
            boundingBox.getSize(size);
            const maxDim = Math.max(size.x, size.y, size.z);
            const desiredSize = 40;
            let scale = 1;
            if (maxDim > 0) {
                scale = desiredSize / maxDim;
            }
            mesh.scale.set(scale, scale, scale);

            mesh.position.set(-center.x, -center.y, -center.z);

            const group = new THREE.Group();
            group.add(mesh);

            const box = new THREE.Box3().setFromObject(group);
            const groupCenter = new THREE.Vector3();
            box.getCenter(groupCenter);
            group.position.set(-groupCenter.x, -groupCenter.y, -groupCenter.z);

            scene.add(group);
            console.log('Mesh añadido a la escena dentro de group:', mesh);

            // EN: Adjust camera to fit the model
            // DE: Kamera anpassen, um das Modell anzupassen
            const fov = camera.fov * (Math.PI / 180);
            const modelRadius = box.getSize(new THREE.Vector3()).length() / 2;
            const fitOffset = 1.3;
            const distance = modelRadius / Math.tan(fov / 2) * fitOffset;
            camera.position.set(0, 0, distance > 0 ? distance : 80);
            camera.lookAt(0, 0, 0);
            if (controls) {
                controls.target.set(0, 0, 0);
                controls.update();
            }
            animate();
        } catch (err) {
            viewer.insertAdjacentHTML('beforeend', '<div style="color:red; text-align:center;">Error al procesar el archivo STL.</div>');
        }
    };
    reader.readAsArrayBuffer(stlFile);

    // EN: Animation/render loop
    // DE: Animations-/Render-Schleife
    function animate() {
        requestAnimationFrame(animate);
        if (controls) controls.update();
        renderer.render(scene, camera);
    }
    window.showSTLModel = showSTLModel;
}
