<script>
(function () {
    var THREE_JS = "api/notes/__THREE_ID__/download";
    var ORBIT_JS = "api/notes/__THREE_ORBIT_ID__/download";
    var STL_JS = "api/notes/__THREE_STL_ID__/download";
    var OCCT_JS = "api/notes/__OCCT_JS_ID__/download";
    var OCCT_WASM = "api/notes/__OCCT_WASM_ID__/download";

    var MODEL_RE = /\.(stl|step|stp)$/i;

    function makeViewer(name) {
        var box = document.createElement("div");
        box.className = "model-viewer";
        var wrap = document.createElement("div");
        wrap.className = "model-viewer-canvas";
        var status = document.createElement("div");
        status.className = "model-viewer-status";
        status.textContent = "Loading 3D model\u2026";
        wrap.appendChild(status);
        box.appendChild(wrap);
        if (name) {
            var cap = document.createElement("div");
            cap.className = "model-viewer-caption";
            cap.textContent = name;
            box.appendChild(cap);
        }
        return box;
    }

    function loadScript(src, cb) {
        var s = document.createElement("script");
        s.src = src;
        s.onload = cb;
        document.head.appendChild(s);
    }

    function initViewer(el, url, ext) {
        var isStep = /\.(step|stp)$/i.test(ext);

        function loadThree(cb) {
            if (window.THREE && window.THREE.OrbitControls) { cb(); return; }
            loadScript(THREE_JS, function () {
                loadScript(ORBIT_JS, function () {
                    if (isStep) { cb(); return; }
                    if (window.THREE.STLLoader) { cb(); return; }
                    loadScript(STL_JS, cb);
                });
            });
        }

        function status(text) {
            var s = el.querySelector(".model-viewer-status");
            if (s) s.textContent = text;
        }

        function fit(group) {
            var box = new THREE.Box3().setFromObject(group);
            if (box.isEmpty()) return;
            var size = box.getSize(new THREE.Vector3()).length() || 1;
            var center = box.getCenter(new THREE.Vector3());
            var d = size * 2.4;
            camera.position.set(center.x + d * 0.9, center.y + d * 0.6, center.z + d);
            controls.target.copy(center);
            controls.update();
        }

        function buildScene(cb) {
            var statusEl = el.querySelector(".model-viewer-status");
            var wrap = el.querySelector(".model-viewer-canvas") || el;
            var w = wrap.clientWidth || el.clientWidth || 400;
            var h = Math.min(520, Math.max(320, Math.round(w * 0.62)));
            var renderer = new THREE.WebGLRenderer({ antialias: true });
            renderer.setSize(w, h);
            renderer.setClearColor(0x1d2021, 1);
            wrap.innerHTML = "";
            wrap.appendChild(renderer.domElement);
            var hint = document.createElement("div");
            hint.className = "model-viewer-hint";
            hint.textContent = "Drag to rotate \u00b7 scroll to zoom";
            wrap.appendChild(hint);

            var scene = new THREE.Scene();
            var camera = new THREE.PerspectiveCamera(45, w / h, 0.1, 1000000);
            camera.position.set(300, 220, 320);
            scene.add(new THREE.AmbientLight(0xffffff, 0.55));
            var dir = new THREE.DirectionalLight(0xffffff, 1.0);
            dir.position.set(1, 1.5, 1);
            scene.add(dir);
            var back = new THREE.DirectionalLight(0xffffff, 0.35);
            back.position.set(-1, -0.5, -1);
            scene.add(back);
            var controls = new THREE.OrbitControls(camera, renderer.domElement);
            controls.enableDamping = true;

            var group = new THREE.Group();
            scene.add(group);

            function animate() {
                requestAnimationFrame(animate);
                controls.update();
                renderer.render(scene, camera);
            }
            animate();

            cb({ group: group, scene: scene, camera: camera, controls: controls, statusEl: statusEl });
        }

        function onError(msg) {
            var s = el.querySelector(".model-viewer-status");
            if (s) { s.textContent = msg || "Could not load 3D model."; s.classList.add("error"); }
        }

        loadThree(function () {
            buildScene(function (ctx) {
                fetch(url).then(function (r) {
                    if (!r.ok) throw new Error("fetch " + r.status);
                    return r.arrayBuffer();
                }).then(function (buf) {
                    var data = new Uint8Array(buf);
                    if (isStep) {
                        ctx.statusEl.textContent = "Loading STEP parser\u2026";
                        function runOcct() {
                            ctx.statusEl.textContent = "Parsing STEP\u2026";
                            window.occtimportjs({ locateFile: function () { return OCCT_WASM; } }).then(function (occt) {
                                var result = occt.ReadStepFile(data, null);
                                if (!result || !result.success) { onError("Could not parse STEP file."); return; }
                                (result.meshes || []).forEach(function (mesh) {
                                    if (!mesh.attributes || !mesh.attributes.position) return;
                                    var geo = new THREE.BufferGeometry();
                                    geo.setAttribute("position", new THREE.Float32BufferAttribute(mesh.attributes.position.array, 3));
                                    if (mesh.attributes.normal && mesh.attributes.normal.array) {
                                        geo.setAttribute("normal", new THREE.Float32BufferAttribute(mesh.attributes.normal.array, 3));
                                    }
                                    if (mesh.attributes.index && mesh.attributes.index.array) {
                                        geo.setIndex(mesh.attributes.index.array);
                                    }
                                    var color = mesh.color && mesh.color.length === 3
                                        ? new THREE.Color(mesh.color[0], mesh.color[1], mesh.color[2]) : null;
                                    var mat = new THREE.MeshStandardMaterial({
                                        color: color || 0xb8bb26,
                                        metalness: 0.25,
                                        roughness: 0.55,
                                        side: THREE.DoubleSide
                                    });
                                    ctx.group.add(new THREE.Mesh(geo, mat));
                                });
                                fit(ctx.group);
                            }).catch(function (e) { onError("Could not load STEP parser."); });
                        }
                        if (window.occtimportjs) { runOcct(); } else { loadScript(OCCT_JS, runOcct); }
                    } else {
                        var geo = new THREE.STLLoader().parse(data);
                        geo.computeVertexNormals();
                        var mat = new THREE.MeshStandardMaterial({ color: 0xb8bb26, metalness: 0.25, roughness: 0.55 });
                        ctx.group.add(new THREE.Mesh(geo, mat));
                        fit(ctx.group);
                    }
                }).catch(function (e) { onError("Could not load 3D model."); });
            });
        });
    }

    var type = (document.body.className.match(/type-([\w-]+)/) || [])[1];

    if (type === "file") {
        var title = ((document.getElementById("title") || {}).textContent || document.title || "").trim();
        var m = title.match(MODEL_RE);
        if (m) {
            var content = document.getElementById("content");
            if (content) {
                var noteId = document.body.getAttribute("data-note-id");
                var el = makeViewer(title.trim());
                content.innerHTML = "";
                content.appendChild(el);
                initViewer(el, "api/notes/" + noteId + "/download", m[0]);
            }
        }
    }

    document.querySelectorAll("#content a[href*='api/attachments/']").forEach(function (a) {
        var name = (a.textContent || "").trim();
        var m = name.match(MODEL_RE);
        if (!m) return;
        var href = a.getAttribute("href");
        var el = makeViewer(name);
        a.replaceWith(el);
        initViewer(el, href, m[0]);
    });
})();
</script>