<!DOCTYPE html>
<html>
<head><script>(function(){var n=0;function show(p,m){var el=document.createElement('div');el.style.cssText='position:fixed;left:0;right:0;background:#e04040;color:#fff;padding:6px 12px;z-index:'+(99999-n)+';font:13px/1.4 monospace;white-space:pre-wrap;border-bottom:1px solid #b02020;top:'+n*30+'px';el.textContent=p+m;document.body.appendChild(el);n++}window.onerror=function(msg,src,line){show('ERROR '+(src||'').split('/').pop()+':'+line+' \u2014 ',msg)};window.addEventListener('unhandledrejection',function(e){show('REJECT \u2014 ',e.reason&&e.reason.message||e.reason||'unknown')})})()</script>
<meta charset="utf-8">
<title>Basic3dView — DIP-8</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
  overflow: hidden;
  height: 100vh;
  display: flex;
  flex-direction: column;
  background: #1a1a1a;
  color: #ccc;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
}
#viewer-wrap {
  flex: 1;
  position: relative;
  min-height: 0;
}
.viewer-container { position: relative; width: 100%; height: 100%; overflow: hidden; }
.viewer-container canvas { display: block; width: 100%; height: 100%; outline: none; }

#toolbar {
  position: absolute;
  top: 8px;
  left: 8px;
  z-index: 20;
  display: flex;
  gap: 1px;
  background: #1a1a1a;
  border: 1px solid #333;
  border-radius: 6px;
  padding: 2px;
}
#toolbar button {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 28px;
  background: transparent;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  transition: background 0.12s;
  position: relative;
}
#toolbar button svg {
  width: 16px;
  height: 16px;
  fill: none;
  stroke: #888;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}
#toolbar button:hover { background: #2a2a2a; }
#toolbar button:hover svg { stroke: #ccc; }
#toolbar button.active svg { stroke: #00b8b1; }
#toolbar button:active { background: #333; }
#toolbar button::after {
  content: attr(data-tip);
  position: absolute;
  top: calc(100% + 6px);
  left: 50%;
  transform: translateX(-50%);
  background: #1a1a1a;
  color: #ccc;
  font-size: 11px;
  padding: 4px 8px;
  border-radius: 4px;
  border: 1px solid #333;
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.15s;
  z-index: 30;
}
#toolbar button:hover::after { opacity: 1; }

#caption-bar {
  display: none;
  padding: 8px 14px;
  background: rgba(13, 17, 23, 0.85);
  border-top: 1px solid #222;
  font-size: 13px;
  color: #b0b8c4;
  line-height: 1.4;
}
#caption-bar.visible { display: block; }
#info-bar {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 6px 12px;
  background: #111;
  border-top: 1px solid #333;
  font-size: 12px;
  color: #888;
  min-height: 32px;
}
#info-bar .label { color: #00b8b1; font-weight: 600; }
#info-bar .viewer-name { margin-left: auto; color: #00b8b1; font-weight: 600; }

#loading {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  color: #888;
  font-size: 14px;
  z-index: 10;
}


/* Drag-drop overlay */
#drop-overlay {
  display: none;
  position: absolute;
  top: 0; left: 0; right: 0; bottom: 0;
  background: rgba(0, 184, 177, 0.15);
  border: 3px dashed #00b8b1;
  z-index: 100;
  align-items: center;
  justify-content: center;
  font-size: 18px;
  color: #00b8b1;
  font-weight: 600;
}
#drop-overlay.active { display: flex; }

/* Mesh list panel */
#mesh-panel {
  display: none;
  position: absolute;
  top: 8px;
  right: 8px;
  z-index: 20;
  background: #1a1a1a;
  border: 1px solid #333;
  border-radius: 6px;
  padding: 8px;
  max-height: 60vh;
  overflow-y: auto;
  min-width: 200px;
  max-width: 300px;
  font-size: 11px;
}
#mesh-panel.open { display: block; }
#mesh-panel h3 {
  font-size: 12px;
  color: #00b8b1;
  margin-bottom: 6px;
  font-weight: 600;
}
#mesh-panel .mesh-item {
  padding: 3px 6px;
  border-radius: 3px;
  cursor: pointer;
  display: flex;
  justify-content: space-between;
  gap: 8px;
}
#mesh-panel .mesh-item:hover { background: #2a2a2a; }
#mesh-panel .mesh-item .name { color: #ccc; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#mesh-panel .mesh-item .verts { color: #666; flex-shrink: 0; }
</style>
</head>
<body>
<div id="viewer-wrap">
  <div id="loading">Loading 3D engine...</div>
  <div id="drop-overlay">Drop GLB file here</div>
  <div id="toolbar" style="display:none">
    <button id="btn-home" data-tip="Reset camera">
      <svg viewBox="0 0 24 24"><path d="M3 12l9-9 9 9"/><path d="M9 21V12h6v9"/></svg>
    </button>
    <button id="btn-frame" data-tip="Frame model">
      <svg viewBox="0 0 24 24"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="12" cy="12" r="3"/></svg>
    </button>
    <button id="btn-ground" data-tip="Toggle ground" class="active">
      <svg viewBox="0 0 24 24"><path d="M2 20L12 4l10 16H2z"/></svg>
    </button>
    <button id="btn-wireframe" data-tip="Toggle wireframe">
      <svg viewBox="0 0 24 24"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/></svg>
    </button>
    <button id="btn-axes" data-tip="Toggle axes">
      <svg viewBox="0 0 24 24"><path d="M12 2v20M2 12h20"/><path d="M12 2l-2 4M12 2l2 4"/><path d="M22 12l-4-2M22 12l-4 2"/></svg>
    </button>
    <button id="btn-meshes" data-tip="Mesh list">
      <svg viewBox="0 0 24 24"><path d="M4 6h16M4 12h16M4 18h16"/></svg>
    </button>
  </div>
  <div id="mesh-panel"><h3>Meshes</h3><div id="mesh-list"></div></div>
</div>
<div id="caption-bar"></div>
<div id="info-bar">
  <span id="info-text">Drag & drop a .glb file or load via MCP tool</span>
  <span class="viewer-name">Basic3dView</span>
</div>

<script>
var viewer = null;
var loadingEl = document.getElementById('loading');
var toolbarEl = document.getElementById('toolbar');
var groundVisible = true;
var wireframeMode = false;
var axesVisible = false;
var axesMeshes = [];
var meshPanelOpen = false;

// ── Drag & Drop ────────────────────────────────────────────────
var dropOverlay = document.getElementById('drop-overlay');
var dragCounter = 0;

document.addEventListener('dragenter', function(e) {
  e.preventDefault();
  dragCounter++;
  dropOverlay.classList.add('active');
});
document.addEventListener('dragleave', function(e) {
  e.preventDefault();
  dragCounter--;
  if (dragCounter <= 0) {
    dragCounter = 0;
    dropOverlay.classList.remove('active');
  }
});
document.addEventListener('dragover', function(e) {
  e.preventDefault();
});
document.addEventListener('drop', function(e) {
  e.preventDefault();
  dragCounter = 0;
  dropOverlay.classList.remove('active');

  var files = e.dataTransfer.files;
  for (var i = 0; i < files.length; i++) {
    var file = files[i];
    if (file.name.toLowerCase().endsWith('.glb') || file.name.toLowerCase().endsWith('.gltf')) {
      loadFromFile(file);
      return;
    }
  }
});

function loadFromFile(file) {
  if (!viewer) return;
  loadingEl.style.display = '';
  loadingEl.textContent = 'Loading ' + file.name + '...';

  var url = URL.createObjectURL(file);
  viewer.clearScene();

  viewer.loadModel(url).then(function() {
    URL.revokeObjectURL(url);
    loadingEl.style.display = 'none';
    viewer.frameModel();
    fixNearClip();
    updateInfo(file.name);
    populateMeshList();
    parent.postMessage(JSON.stringify({ type: 'basic_3d_loaded' }), '*');
  }).catch(function(err) {
    URL.revokeObjectURL(url);
    loadingEl.textContent = 'Load failed: ' + (err.message || err);
    loadingEl.style.color = '#e04040';
    parent.postMessage(JSON.stringify({ type: 'basic_3d_loaded', error: true }), '*');
  });
}

// ── Load from URL ──────────────────────────────────────────────
function loadFromUrl(modelUrl, options) {
  if (!viewer) {
    window._pendingModel = { modelUrl: modelUrl, options: options || {} };
    return;
  }
  options = options || {};

  loadingEl.style.display = '';
  loadingEl.textContent = 'Loading 3D model...';

  var fullUrl = resolveModelUrl(modelUrl);
  viewer.clearScene();

  viewer.loadModel(fullUrl).then(function() {
    loadingEl.style.display = 'none';
    viewer.frameModel();
    fixNearClip();
    updateInfo(options.title || options.fileName || modelUrl.split('/').pop());
    setCaption(options.caption || '');
    populateMeshList();
    parent.postMessage(JSON.stringify({ type: 'basic_3d_loaded' }), '*');
  }).catch(function(err) {
    loadingEl.textContent = 'Load failed: ' + (err.message || err);
    loadingEl.style.color = '#e04040';
    parent.postMessage(JSON.stringify({ type: 'basic_3d_loaded', error: true }), '*');
  });
}

function resolveModelUrl(modelUrl) {
  if (modelUrl.startsWith('http://') || modelUrl.startsWith('https://') || modelUrl.startsWith('blob:')) {
    return modelUrl;
  }
  var base = location.pathname.replace(/\/[^/]*$/, '');
  return base + (modelUrl.startsWith('/') ? '' : '/') + modelUrl;
}

function fixNearClip() {
  var cam = viewer.getCamera();
  if (cam && cam.radius < 0.1) {
    cam.minZ = cam.radius / 1000;
  }
}

function setCaption(text) {
  var el = document.getElementById('caption-bar');
  if (text) {
    el.textContent = text;
    el.classList.add('visible');
  } else {
    el.textContent = '';
    el.classList.remove('visible');
  }
}

function updateInfo(name) {
  var scene = viewer.getScene();
  var meshCount = 0;
  var vertCount = 0;
  scene.meshes.forEach(function(m) {
    if (m.getTotalVertices && m.getTotalVertices() > 0) {
      meshCount++;
      vertCount += m.getTotalVertices();
    }
  });
  var info = name;
  info += ' \u2014 ' + meshCount + ' meshes, ' + vertCount.toLocaleString() + ' verts';
  document.getElementById('info-text').textContent = info;
}

function populateMeshList() {
  var scene = viewer.getScene();
  var listEl = document.getElementById('mesh-list');
  listEl.innerHTML = '';
  scene.meshes.forEach(function(m) {
    if (!m.getTotalVertices || m.getTotalVertices() === 0) return;
    var div = document.createElement('div');
    div.className = 'mesh-item';
    var nameSpan = document.createElement('span');
    nameSpan.className = 'name';
    nameSpan.textContent = m.name || '(unnamed)';
    nameSpan.title = m.name || '(unnamed)';
    var vertSpan = document.createElement('span');
    vertSpan.className = 'verts';
    vertSpan.textContent = m.getTotalVertices().toLocaleString() + 'v';
    div.appendChild(nameSpan);
    div.appendChild(vertSpan);
    div.onclick = function() {
      // Highlight mesh briefly
      var origColor = null;
      var mat = m.material;
      if (mat) {
        origColor = mat.emissiveColor ? mat.emissiveColor.clone() : null;
        mat.emissiveColor = new window.Adom3DViewer.BABYLON.Color3(0, 0.72, 0.69);
        setTimeout(function() {
          if (origColor) mat.emissiveColor = origColor;
          else mat.emissiveColor = new window.Adom3DViewer.BABYLON.Color3(0, 0, 0);
        }, 1500);
      }
    };
    listEl.appendChild(div);
  });
}

// ── postMessage API ────────────────────────────────────────────
window.addEventListener('message', function(e) {
  var msg;
  try { msg = JSON.parse(e.data); } catch(ex) { return; }
  if (msg.type === 'load_model') {
    loadFromUrl(msg.modelUrl, msg.options);
  } else if (msg.type === 'basic3d_toggle') {
    var btn = document.getElementById('btn-' + msg.name);
    if (btn) btn.click();
  } else if (msg.type === 'basic3d_set_camera') {
    var cam = viewer && viewer.getCamera();
    if (cam) {
      if (msg.alpha !== undefined) cam.alpha = msg.alpha;
      if (msg.beta !== undefined) cam.beta = msg.beta;
      if (msg.radius !== undefined) cam.radius = msg.radius;
    }
  } else if (msg.type === 'basic3d_set_caption') {
    setCaption(msg.text || '');
  } else if (msg.type === 'basic3d_fill') {
    // Fill entire iframe with a solid color (for screenshot calibration)
    var overlay = document.getElementById('calibration-overlay');
    if (msg.color) {
      if (!overlay) {
        overlay = document.createElement('div');
        overlay.id = 'calibration-overlay';
        overlay.style.cssText = 'position:fixed;top:0;left:0;width:100%;height:100%;z-index:9999;';
        document.body.appendChild(overlay);
      }
      overlay.style.background = msg.color;
      overlay.style.display = 'block';
    } else if (overlay) {
      overlay.style.display = 'none';
    }
  }
});

// ── Init Babylon.js ────────────────────────────────────────────
var base = location.pathname.replace(/\/[^/]*$/, '');
var script = document.createElement('script');
script.src = base + '/adom-3d-viewer.min.js?v=' + Date.now();
script.onload = function() {
  loadingEl.textContent = 'Initializing viewer...';
  try {
    var wrap = document.getElementById('viewer-wrap');
    loadingEl.style.display = 'none';

    var envUrl = base + '/environmentSpecular.env';
    viewer = window.Adom3DViewer.init(wrap, {
      zUp: true,
      showViewCube: true,
      showGround: true,
      environmentUrl: envUrl,
    });

    toolbarEl.style.display = '';
    initToolbar();

    parent.postMessage(JSON.stringify({ type: 'basic_3d_ready' }), '*');

      // Auto-load model for wiki demo
      (function() {
        var glbUrl = 'dip-8.glb';
        loadFromUrl(glbUrl, { title: 'DIP-8', caption: 'DIP-8 mesh inspector — click any mesh name to highlight it' });
        // Toggle features after model loads
        var poll = setInterval(function() {
          if (document.getElementById('loading').style.display === 'none' && viewer) {
            clearInterval(poll);
            document.getElementById('btn-meshes').click();
          }
        }, 200);
      })();

    if (window._pendingModel) {
      var pending = window._pendingModel;
      window._pendingModel = null;
      loadFromUrl(pending.modelUrl, pending.options);
    }
  } catch (err) {
    loadingEl.style.display = '';
    loadingEl.textContent = 'Failed to init viewer: ' + err.message;
    loadingEl.style.color = '#e04040';
  }
};
script.onerror = function() {
  loadingEl.textContent = 'Failed to load 3D viewer bundle';
  loadingEl.style.color = '#e04040';
};
document.head.appendChild(script);

// ── Toolbar ────────────────────────────────────────────────────
function initToolbar() {
  document.getElementById('btn-home').onclick = function() {
    if (viewer) viewer.goHome();
  };
  document.getElementById('btn-frame').onclick = function() {
    if (viewer) viewer.frameModel();
  };
  document.getElementById('btn-ground').onclick = function() {
    groundVisible = !groundVisible;
    if (viewer) viewer.setGroundVisible(groundVisible);
    this.classList.toggle('active', groundVisible);
  };
  document.getElementById('btn-wireframe').onclick = function() {
    wireframeMode = !wireframeMode;
    var scene = viewer.getScene();
    scene.meshes.forEach(function(m) {
      if (m.material) m.material.wireframe = wireframeMode;
    });
    this.classList.toggle('active', wireframeMode);
  };
  document.getElementById('btn-axes').onclick = function() {
    axesVisible = !axesVisible;
    toggleAxes(axesVisible);
    this.classList.toggle('active', axesVisible);
  };
  document.getElementById('btn-meshes').onclick = function() {
    meshPanelOpen = !meshPanelOpen;
    document.getElementById('mesh-panel').classList.toggle('open', meshPanelOpen);
    this.classList.toggle('active', meshPanelOpen);
  };
}

function toggleAxes(show) {
  if (!viewer) return;
  var scene = viewer.getScene();
  var BABYLON = window.Adom3DViewer.BABYLON;

  // Remove existing
  axesMeshes.forEach(function(m) { m.dispose(); });
  axesMeshes = [];

  if (!show) return;

  var len = 5;
  var colors = [
    new BABYLON.Color3(1, 0.2, 0.2),   // X = red
    new BABYLON.Color3(0.2, 1, 0.2),   // Y = green
    new BABYLON.Color3(0.3, 0.5, 1),   // Z = blue
  ];
  var dirs = [
    new BABYLON.Vector3(len, 0, 0),
    new BABYLON.Vector3(0, len, 0),
    new BABYLON.Vector3(0, 0, len),
  ];
  var labels = ['X', 'Y', 'Z'];

  for (var i = 0; i < 3; i++) {
    var points = [BABYLON.Vector3.Zero(), dirs[i]];
    var lines = BABYLON.MeshBuilder.CreateLines('axis_' + labels[i], {
      points: points,
    }, scene);
    lines.color = colors[i];
    lines.isPickable = false;
    axesMeshes.push(lines);
  }
}
</script>
</body>
</html>