{% load static %}
<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title>Building Twin — Wireframe Stadium (Real Size + Smart Can)</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">

  <!-- A-Frame -->
  <script src="https://aframe.io/releases/1.4.1/aframe.min.js"></script>

  <style>
    html, body { height:100%; margin:0; background:#0b1220; color:#e5e7eb; font-family: system-ui, sans-serif; }
    #hud {
      position: fixed; top: 10px; left: 10px; z-index: 1000;
      background:#0f172a; padding:10px 12px; border-radius:10px; box-shadow:0 10px 30px rgba(0,0,0,.35);
      font-size:12px; line-height:1.25; max-width: 560px;
    }
    code { background:#111827; padding:0 4px; border-radius:4px }
  </style>
</head>
<body>
  <div id="hud">
    <div style="font-weight:600; margin-bottom:6px">Wireframe Stadium (real meters)</div>
    <div>Use <code>?scale=2</code> (double), <code>?scale=0.5</code> (half). Field defaults to 105×68 m (override with <code>?field_w=&field_h=</code>).</div>
    <div>Smart can params: <code>?can_units=m|cm|mm</code>, <code>?can_scale=1</code>, <code>?can_angle=45</code>, <code>?can_y=0</code>, <code>?can_face_center=1</code>, optional <code>?can_tri=1</code> to download triangles JSON.</div>
    <div style="opacity:.75; margin-top:6px">WASD to move, drag to look. Enter VR to feel the 1:1 scale.</div>
  </div>

  <a-scene background="color: #0b1220"
           renderer="antialias:false; powerPreference:high-performance; physicallyCorrectLights:false; logarithmicDepthBuffer:true">
    <!-- Assets (GLB fast path; OBJ fallback) -->
    <a-assets timeout="30000">
      <a-asset-item id="smartcan-glb" src="{% static 'pxy_building_digital_twins/models/smartcan.glb' %}"></a-asset-item>
      <a-asset-item id="smartcan-obj" src="{% static 'pxy_building_digital_twins/models/smartcan.obj' %}"></a-asset-item>
    </a-assets>

    <!-- Lights -->
    <a-entity light="type: ambient; intensity: 0.6; color: #ffffff"></a-entity>
    <a-entity light="type: directional; intensity: 0.7; color: #ffffff" position="50 80 40"></a-entity>

    <!-- Camera rig at human eye height -->
    <a-entity id="rig" position="0 1.6 180">
      <a-entity camera look-controls wasd-controls="acceleration: 40" position="0 0 0"></a-entity>
    </a-entity>

    <!-- Ground grid (reference) -->
    <a-entity id="grid"></a-entity>

    <!-- Stadium parts -->
    <a-entity id="stadium"></a-entity>
    <a-entity id="bins"></a-entity>
  </a-scene>

  <script>
    // ---------- URL params ----------
    const q = new URLSearchParams(location.search);
    const SCALE = Math.max(0.1, Math.min(5, parseFloat(q.get('scale') || '1')));  // 0.1x .. 5x
    const FIELD_W = parseFloat(q.get('field_w') || '105'); // meters (FIFA)
    const FIELD_H = parseFloat(q.get('field_h') || '68');

    // ---------- Seeded RNG (deterministic with ?seed=) ----------
    function mulberry32(a){ return function(){ var t=a+=0x6D2B79F5; t=Math.imul(t^t>>>15,t|1);
      t^=t+Math.imul(t^t>>>7,t|61); return ((t^t>>>14)>>>0)/4294967296; } }
    const urlSeed = q.get("seed");
    const seedNum = urlSeed ? Array.from(String(urlSeed)).reduce((s,ch)=>s+ch.charCodeAt(0),0) : Math.floor(Math.random()*1e9);
    const rand = mulberry32(seedNum);
    const randf = (a,b)=> a + (b-a)*rand();
    const randi = (a,b)=> Math.floor(randf(a,b+1));

    // ---------- A-Frame helpers ----------
    AFRAME.registerComponent('wire-ellipse', {
      schema: { rx:{type:'number'}, ry:{type:'number'}, y:{type:'number',default:0},
                seg:{type:'int',default:256}, color:{type:'color',default:'#93a3b8'}, width:{type:'number',default:1} },
      init: function () {
        const THREE = AFRAME.THREE, d = this.data;
        const geom = new THREE.BufferGeometry(); const pts = [];
        for(let i=0;i<d.seg;i++){ const a = i/d.seg * Math.PI*2; pts.push(d.rx*Math.cos(a), d.y, d.ry*Math.sin(a)); }
        pts.push(pts[0], pts[1], pts[2]); // close
        geom.setAttribute('position', new THREE.BufferAttribute(new Float32Array(pts), 3));
        const mat = new THREE.LineBasicMaterial({ color: d.color, linewidth: d.width });
        this.el.setObject3D('mesh', new THREE.Line(geom, mat));
      },
      remove: function(){ const o=this.el.getObject3D('mesh'); if(o){ o.geometry.dispose(); o.material.dispose(); this.el.removeObject3D('mesh'); } }
    });

    function addWireBox(parent, w, h, t, y, color){ // w=width (x), h=depth (z), t=thickness (y)
      const e = document.createElement('a-box');
      e.setAttribute('width', w); e.setAttribute('depth', h); e.setAttribute('height', t);
      e.setAttribute('position', `0 ${y} 0`);
      e.setAttribute('material', `wireframe: true; color: ${color}; opacity: 0.65; transparent: true`);
      parent.appendChild(e); return e;
    }

    function addBin(parent, x, y, z, color='#10b981'){
      const s = document.createElement('a-sphere');
      s.setAttribute('radius', 0.6 * SCALE);  // ~60cm glowing puck
      s.setAttribute('position', `${x} ${y} ${z}`);
      s.setAttribute('material', `color: ${color}; emissive: ${color}; emissiveIntensity: 0.9`);
      parent.appendChild(s); return s;
    }

    // Smart can loader (GLB first, OBJ fallback)
    function addSmartCan(parent, x, y, z, scale=1, rotY=0){
      const e = document.createElement('a-entity');
      if (document.getElementById('smartcan-glb')) {
        e.setAttribute('gltf-model', '#smartcan-glb'); // FAST PATH
      } else if (document.getElementById('smartcan-obj')) {
        e.setAttribute('obj-model', 'obj: #smartcan-obj'); // fallback (no MTL)
        e.setAttribute('material', 'color: #9ca3af; metalness: 0.2; roughness: 0.6');
      } else {
        // last-resort placeholder
        e.setAttribute('geometry','primitive:cylinder; radius:0.35; height:1.0');
        e.setAttribute('material','color:#9ca3af');
      }
      e.setAttribute('position', `${x} ${y} ${z}`);
      e.setAttribute('rotation', `0 ${rotY} 0`);
      e.setAttribute('scale', `${scale} ${scale} ${scale}`);
      parent.appendChild(e);
      return e;
    }

    // Optional: extract triangles from loaded model (works for GLB & OBJ)
    AFRAME.registerComponent('extract-triangles', {
      schema: { download:{type:'boolean', default:true}, log:{type:'boolean', default:true} },
      init() {
        this.onLoaded = () => {
          const root = this.el.getObject3D('mesh'); if (!root) return;
          const tris = [];
          root.traverse(n => {
            if (!n.isMesh || !n.geometry) return;
            let g = n.geometry;
            if (g.index) g = g.toNonIndexed();
            const pos = g.getAttribute('position'); if (!pos) return;
            const a = pos.array;
            for (let i=0;i<a.length;i+=9){
              tris.push([[a[i],a[i+1],a[i+2]],[a[i+3],a[i+4],a[i+5]],[a[i+6],a[i+7],a[i+8]]]);
            }
          });
          if (this.data.log) console.log(`extract-triangles: ${tris.length} triangles`, tris);
          if (this.data.download){
            const blob = new Blob([JSON.stringify({model:'smartcan',triangles:tris})],{type:'application/json'});
            const url = URL.createObjectURL(blob);
            const a = Object.assign(document.createElement('a'), {href:url, download:'smartcan_triangles.json'});
            document.body.appendChild(a); a.click(); a.remove(); setTimeout(()=>URL.revokeObjectURL(url), 2000);
          }
        };
        this.el.addEventListener('model-loaded', this.onLoaded);
      },
      remove(){ this.el.removeEventListener('model-loaded', this.onLoaded); }
    });

    // Grid in meters
    (function makeGrid(){
      const e = document.querySelector('#grid');
      const size=500*SCALE, step=5*SCALE, y=0;
      for(let x=-size; x<=size; x+=step){ const l=document.createElement('a-entity');
        l.setAttribute('line', `start: ${x} ${y} ${-size}; end: ${x} ${y} ${size}; color: #1f2937`); e.appendChild(l); }
      for(let z=-size; z<=size; z+=step){ const l=document.createElement('a-entity');
        l.setAttribute('line', `start: ${-size} ${y} ${z}; end: ${size} ${y} ${z}; color: #1f2937`); e.appendChild(l); }
    })();

    // ---------- Build at real scale (meters) ----------
    (function build(){
      // Clean previous geometry if re-running
      const stadium = document.querySelector('#stadium');
      const bins = document.querySelector('#bins');
      while (stadium.firstChild) stadium.removeChild(stadium.firstChild);
      while (bins.firstChild) bins.removeChild(bins.firstChild);

      // Heights (meters)
      const Y_FIELD = 0.20*SCALE;     // field mesh lift
      const Y_CONC  = 8.0*SCALE;      // concourse level
      const Y_STAND = 22.0*SCALE;     // upper stands level (wire)
      const EYE_H   = 1.6*SCALE;      // human eye height

      // Field (exact meters)
      const fieldW = FIELD_W * SCALE;
      const fieldH = FIELD_H * SCALE;

      // Derive rings from field size (meters)
      const safeX = (FIELD_W/2 + randf(6,10)) * SCALE;
      const safeZ = (FIELD_H/2 + randf(6,10)) * SCALE;

      const standsInRX = safeX + randf(12,18)*SCALE;
      const standsInRZ = safeZ + randf(12,18)*SCALE;

      const concInRX   = standsInRX + randf(4,6)*SCALE;   // concourse corridor inside
      const concInRZ   = standsInRZ + randf(4,6)*SCALE;
      const concOutRX  = concInRX + randf(6,10)*SCALE;    // concourse corridor outside
      const concOutRZ  = concInRZ + randf(6,10)*SCALE;

      const standsOutRX = standsInRX + randf(35,45)*SCALE;
      const standsOutRZ = standsInRZ + randf(35,45)*SCALE;

      // Field (wireframe box)
      addWireBox(stadium, fieldW, fieldH, 0.4*SCALE, Y_FIELD, '#16a34a');

      // Concourse inner/outer
      const concInner = document.createElement('a-entity');
      concInner.setAttribute('wire-ellipse', { rx: concInRX, ry: concInRZ, y: Y_CONC, color:'#22d3ee' });
      const concOuter = document.createElement('a-entity');
      concOuter.setAttribute('wire-ellipse', { rx: concOutRX, ry: concOutRZ, y: Y_CONC, color:'#38bdf8' });
      stadium.appendChild(concInner); stadium.appendChild(concOuter);

      // Stands inner/outer (higher)
      const standInner = document.createElement('a-entity');
      standInner.setAttribute('wire-ellipse', { rx: standsInRX, ry: standsInRZ, y: Y_STAND, color:'#a78bfa' });
      const standOuter = document.createElement('a-entity');
      standOuter.setAttribute('wire-ellipse', { rx: standsOutRX, ry: standsOutRZ, y: Y_STAND, color:'#60a5fa' });
      stadium.appendChild(standInner); stadium.appendChild(standOuter);

      // Ribs
      const ribs = randi(14, 22);
      for(let i=0;i<ribs;i++){
        const a = i/ribs * Math.PI*2 + randf(-0.02,0.02);
        const x1 = concOutRX * Math.cos(a), z1 = concOutRZ * Math.sin(a);
        const x2 = standsInRX * Math.cos(a), z2 = standsInRZ * Math.sin(a);
        const rib = document.createElement('a-entity');
        rib.setAttribute('line', `start: ${x1} ${Y_CONC} ${z1}; end: ${x2} ${Y_STAND} ${z2}; color: #14b8a6`);
        stadium.appendChild(rib);
      }

      // Bins around concourse midline
      const midRX = (concInRX + concOutRX)/2, midRZ = (concInRZ + concOutRZ)/2;
      const binCount = randi(44, 64);
      const colors = ['#10b981', '#0ea5e9', '#8b5cf6', '#f59e0b'];
      for(let i=0;i<binCount;i++){
        const a = i/binCount * Math.PI*2 + randf(-0.03,0.03);
        const x = midRX * Math.cos(a), z = midRZ * Math.sin(a);
        addBin(bins, x, Y_CONC+0.25*SCALE, z, colors[i % colors.length]);
      }

      // --- Smart can (GLB fast path; OBJ fallback) ---
      const units = (q.get('can_units') || 'm').toLowerCase();   // m|cm|mm
      const canScaleUser = parseFloat(q.get('can_scale') || '1');
      const canAngleDeg   = parseFloat(q.get('can_angle') || (Math.random()*360).toFixed(1));
      const canYOffset    = parseFloat(q.get('can_y') || '0');   // meters
      const faceCenter    = (q.get('can_face_center') || '1') !== '0';

      const unitToMeter   = units==='cm' ? 0.01 : units==='mm' ? 0.001 : 1;
      const canScaleFinal = SCALE * unitToMeter * canScaleUser;

      const ang  = canAngleDeg * Math.PI/180;
      const canX = midRX * Math.cos(ang);
      const canZ = midRZ * Math.sin(ang);
      const rotY = faceCenter ? -canAngleDeg : 0;

      const can = addSmartCan(stadium, canX, Y_CONC + canYOffset, canZ, canScaleFinal, rotY);

      // Optional triangle dump: add ?can_tri=1 to URL
      if (q.get('can_tri') === '1') {
        can.setAttribute('extract-triangles', 'download:true; log:true');
      }

      // Start the rig at eye height just outside the outer stands, facing center
      const startZ = Math.max(standsOutRX, standsOutRZ) + 15*SCALE;
      document.querySelector('#rig').setAttribute('position', `0 ${EYE_H} ${startZ}`);
    })();
  </script>
</body>
</html>
