{% load static %}
<!DOCTYPE html>
<html lang="zh-hans">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>3D Globe</title>
<style>
* { margin:0; padding:0; box-sizing:border-box; }
body { background:#0a0a1a; overflow:hidden; width:100vw; height:100vh; }
#globeContainer { width:100%; height:100%; }
</style>
</head>
<body>
<div id="globeContainer"></div>
<script src="{% static 'tianxuan/three.min.js' %}"></script>
<script>
const geoFlows = {{ geo_flows|safe }};
const W = window.innerWidth, H = window.innerHeight;

var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(45, W/H, 0.1, 1000);
camera.position.set(0, 2, 12);

var renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setSize(W, H);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
document.getElementById('globeContainer').appendChild(renderer.domElement);

// Earth
var earthGeo = new THREE.SphereGeometry(5, 64, 64);
var earthMat = new THREE.MeshPhongMaterial({ color: 0x1a3a5c, emissive: 0x0a1a2a, specular: new THREE.Color(0x333333), shininess: 5 });
var earth = new THREE.Mesh(earthGeo, earthMat);
scene.add(earth);

// Glow
var glow = new THREE.Mesh(new THREE.SphereGeometry(5.1, 64, 64), new THREE.MeshBasicMaterial({ color: 0x224488, transparent: true, opacity: 0.1 }));
scene.add(glow);

// Lights
scene.add(new THREE.AmbientLight(0x222244));
var dl = new THREE.DirectionalLight(0xffffff, 1);
dl.position.set(5, 10, 7);
scene.add(dl);
scene.add(new THREE.DirectionalLight(0x4488ff, 0.3));

// Stars
var starGeo = new THREE.BufferGeometry();
var starPos = new Float32Array(1000 * 3);
for (var i = 0; i < 1000 * 3; i++) starPos[i] = (Math.random() - 0.5) * 200;
starGeo.setAttribute('position', new THREE.BufferAttribute(starPos, 3));
scene.add(new THREE.Points(starGeo, new THREE.PointsMaterial({ color: 0xffffff, size: 0.15 })));

// Lat/lon grid
var gridMat = new THREE.LineBasicMaterial({ color: 0x6699cc, transparent: true, opacity: 0.1 });
for (var lat = -80; lat <= 80; lat += 20) {
    var pts = [];
    for (var lon = 0; lon <= 360; lon += 5) {
        var phi = (90 - lat) * Math.PI / 180;
        var theta = (lon + 180) * Math.PI / 180;
        pts.push(new THREE.Vector3(-5.02 * Math.sin(phi) * Math.cos(theta), 5.02 * Math.cos(phi), 5.02 * Math.sin(phi) * Math.sin(theta)));
    }
    scene.add(new THREE.Line(new THREE.BufferGeometry().setFromPoints(pts), gridMat));
}
for (var lon = 0; lon < 360; lon += 20) {
    var pts = [];
    for (var lat = -90; lat <= 90; lat += 5) {
        var phi = (90 - lat) * Math.PI / 180;
        var theta = (lon + 180) * Math.PI / 180;
        pts.push(new THREE.Vector3(-5.02 * Math.sin(phi) * Math.cos(theta), 5.02 * Math.cos(phi), 5.02 * Math.sin(phi) * Math.sin(theta)));
    }
    scene.add(new THREE.Line(new THREE.BufferGeometry().setFromPoints(pts), gridMat));
}

// Flow arcs
var tlsColors = { 'TLSv1.3': 0x4cc9f0, 'TLSv1.2': 0x43aa8b, 'TLSv1.1': 0xf8961e, 'TLSv1.0': 0xf94144 };
var flowGroup = new THREE.Group();
scene.add(flowGroup);

function latLonToVec3(lat, lon, r) {
    if (lat == null || lon == null) return null;
    var phi = (90 - lat) * Math.PI / 180;
    var theta = (lon + 180) * Math.PI / 180;
    return new THREE.Vector3(-r * Math.sin(phi) * Math.cos(theta), r * Math.cos(phi), r * Math.sin(phi) * Math.sin(theta));
}

var arcMeshes = [];
var flowData = Array.isArray(geoFlows) ? geoFlows : (typeof geoFlows === 'string' ? JSON.parse(geoFlows) : []);
flowData.forEach(function(flow) {
    var from = latLonToVec3(flow.slat, flow.slon, 5);
    var to = latLonToVec3(flow.dlat, flow.dlon, 5);
    if (!from || !to) return;
    var mid = new THREE.Vector3().addVectors(from, to).multiplyScalar(0.5).normalize().multiplyScalar(7);
    var curve = new THREE.QuadraticBezierCurve3(from, mid, to);
    var color = tlsColors[flow.tls] || 0x888888;
    var mat = new THREE.MeshBasicMaterial({ color: color, transparent: true, opacity: 0.6 });
    var mesh = new THREE.Mesh(new THREE.TubeGeometry(curve, 20, 0.02, 4, false), mat);
    mesh.userData = flow;
    flowGroup.add(mesh);
    arcMeshes.push(mesh);
});

// Mouse rotation
var isDragging = false, prevMouse = { x: 0, y: 0 };
renderer.domElement.addEventListener('mousedown', function(e) { isDragging = true; prevMouse = { x: e.clientX, y: e.clientY }; });
window.addEventListener('mouseup', function() { isDragging = false; });
window.addEventListener('mousemove', function(e) {
    if (!isDragging) return;
    var dx = e.clientX - prevMouse.x, dy = e.clientY - prevMouse.y;
    earth.rotation.y += dx * 0.005;
    earth.rotation.z += dy * 0.005;
    flowGroup.rotation.y = earth.rotation.y;
    flowGroup.rotation.z = earth.rotation.z;
    prevMouse = { x: e.clientX, y: e.clientY };
});

// Listen for cluster filter from parent
window.addEventListener('message', function(e) {
    if (e.data && e.data.type === 'globe_filter') {
        var label = e.data.cluster_label;
        arcMeshes.forEach(function(mesh) {
            if (label === null) {
                mesh.material.opacity = 0.6;
                mesh.material.color.setHex(tlsColors[mesh.userData.tls] || 0x888888);
            } else {
                mesh.material.opacity = 0.06;
                mesh.material.color.setHex(0x666666);
            }
        });
    }
});

// Auto-rotate
(function animate() {
    requestAnimationFrame(animate);
    if (!isDragging) { earth.rotation.y += 0.003; flowGroup.rotation.y = earth.rotation.y; }
    renderer.render(scene, camera);
})();
</script>
</body>
</html>
