#!/usr/bin/env python3
"""
Build a Leaflet.js web viewer for the Nerdhalla Valheim world.
"""
import sqlite3, json, os

DB_PATH = "/tmp/valheim_poi.db"
OUTPUT_DIR = "/tmp/valheim_map_output"
VIEWER_PATH = os.path.join(OUTPUT_DIR, "index.html")

WORLD_SIZE = 24000
PIXELS_TOTAL = 4096

def valheim_to_pixel(vx, vz):
    px = (vx + WORLD_SIZE/2) / WORLD_SIZE * PIXELS_TOTAL
    py = (vz + WORLD_SIZE/2) / WORLD_SIZE * PIXELS_TOTAL
    return px, py

def query_pois():
    conn = sqlite3.connect(DB_PATH)
    c = conn.cursor()
    c.execute("""
        SELECT id, prefab_name, category, pos_x, pos_z, has_dungeon, has_important
        FROM locations WHERE pos_x IS NOT NULL AND pos_z IS NOT NULL
        ORDER BY category, prefab_name
    """)
    pois = []
    for row in c.fetchall():
        loc_id, name, cat, x, z, has_dungeon, has_important = row
        px, py = valheim_to_pixel(x, z)
        pois.append({
            'id': loc_id, 'name': name, 'category': cat,
            'x': round(x, 1), 'z': round(z, 1),
            'px': round(px, 1), 'py': round(py, 1),
            'has_dungeon': bool(has_dungeon), 'has_important': bool(has_important),
        })
    
    c.execute("""
        SELECT l.id, l.prefab_name, l.pos_x, l.pos_z, ic.friendly_name, ic.count
        FROM locations l JOIN important_contents ic ON l.id = ic.location_id
        WHERE l.pos_x IS NOT NULL AND l.pos_z IS NOT NULL
        ORDER BY ic.count DESC
    """)
    contents = []
    for row in c.fetchall():
        loc_id, name, x, z, fname, count = row
        px, py = valheim_to_pixel(x, z)
        contents.append({
            'loc_id': loc_id, 'loc_name': name, 'item': fname, 'count': count,
            'x': round(x, 1), 'z': round(z, 1),
            'px': round(px, 1), 'py': round(py, 1),
        })
    conn.close()
    return pois, contents

def build_viewer(pois, contents):
    pois_json = json.dumps(pois)
    contents_json = json.dumps(contents)
    
    cat_colors = {
        'boss': '#ff4444', 'dungeon': '#ff8800', 'vegvisir': '#44aaff',
        'altar': '#aa88ff', 'vendor': '#ffdd00', 'structure': '#88cc88',
        'resource': '#ff66aa', 'spawner': '#ff44ff', 'point_of_interest': '#44ffaa',
        'other': '#888888',
    }
    cat_colors_json = json.dumps(cat_colors)
    
    cat_labels = {
        'boss': 'Boss Altars', 'dungeon': 'Dungeons', 'vegvisir': 'Vegvisirs',
        'vendor': 'Vendors', 'altar': 'Altars / Stones', 'structure': 'Structures',
        'resource': 'Resources', 'spawner': 'Spawners', 'point_of_interest': 'POIs',
        'other': 'Other',
    }
    cat_labels_json = json.dumps(cat_labels)
    
    # Build category counts
    cat_counts = {}
    for p in pois:
        cat_counts[p['category']] = cat_counts.get(p['category'], 0) + 1
    cat_counts_json = json.dumps(cat_counts)
    
    html = """<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Nerdhalla — Valheim World Map</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body { height: 100%; font-family: 'Segoe UI', system-ui, sans-serif; }
#map { height: 100vh; width: 100vw; }
.leaflet-container { background: #1a1a2e; }
.info-panel {
    position: absolute; top: 10px; right: 10px; z-index: 1000;
    background: rgba(20, 20, 35, 0.92); color: #ddd;
    padding: 12px 16px; border-radius: 8px; font-size: 13px;
    max-width: 280px; max-height: 80vh; overflow-y: auto;
    border: 1px solid #444; backdrop-filter: blur(8px);
}
.info-panel h3 { color: #ffd700; margin-bottom: 6px; font-size: 15px; }
.info-panel .stat { display: flex; justify-content: space-between; padding: 2px 0; }
.info-panel .stat span:last-child { color: #aaa; }
.legend {
    position: absolute; bottom: 30px; right: 10px; z-index: 1000;
    background: rgba(20, 20, 35, 0.92); color: #ddd;
    padding: 10px 14px; border-radius: 8px; font-size: 12px;
    border: 1px solid #444; backdrop-filter: blur(8px);
    max-height: 50vh; overflow-y: auto;
}
.legend-item { display: flex; align-items: center; gap: 6px; padding: 2px 0; cursor: pointer; }
.legend-item .dot { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; }
.legend-item .count { color: #888; margin-left: auto; font-size: 11px; }
.legend-item.active { color: #fff; }
.legend-item.inactive { opacity: 0.4; }
.search-box {
    position: absolute; top: 10px; left: 10px; z-index: 1000;
    background: rgba(20, 20, 35, 0.92); border: 1px solid #444;
    border-radius: 8px; padding: 8px 12px; backdrop-filter: blur(8px);
    display: flex; gap: 6px; align-items: center;
}
.search-box input {
    background: transparent; border: none; color: #ddd; font-size: 13px;
    outline: none; width: 180px;
}
.search-box input::placeholder { color: #666; }
.search-box .results {
    position: absolute; top: 100%; left: 0; right: 0;
    background: rgba(20, 20, 35, 0.95); border: 1px solid #444;
    border-radius: 0 0 8px 8px; max-height: 200px; overflow-y: auto;
    display: none;
}
.search-box .results div { padding: 6px 12px; cursor: pointer; font-size: 12px; border-bottom: 1px solid #333; }
.search-box .results div:hover { background: rgba(255,255,255,0.1); }
.search-box .results div .cat { color: #888; font-size: 10px; }
.popup-content { font-size: 12px; line-height: 1.5; }
.popup-content .name { font-weight: bold; font-size: 14px; color: #ffd700; }
.popup-content .coords { color: #888; }
.popup-content .badge { display: inline-block; padding: 1px 6px; border-radius: 3px; font-size: 10px; margin: 2px 2px 0 0; }
.popup-content .badge-dungeon { background: #ff8800; color: #000; }
.popup-content .badge-treasure { background: #ffd700; color: #000; }
.toggle-layer {
    position: absolute; bottom: 30px; left: 10px; z-index: 1000;
    display: flex; flex-direction: column; gap: 4px;
}
.toggle-layer button {
    background: rgba(20, 20, 35, 0.92); color: #ddd; border: 1px solid #444;
    padding: 6px 12px; border-radius: 6px; cursor: pointer; font-size: 11px;
    backdrop-filter: blur(8px); text-align: left;
}
.toggle-layer button.active { border-color: #ffd700; color: #ffd700; }
.toggle-layer button:hover { background: rgba(40, 40, 60, 0.95); }
@media (max-width: 768px) { .info-panel { display: none; } .search-box input { width: 120px; } }
</style>
</head>
<body>
<div id="map"></div>

<div class="search-box">
    <span style="color:#666;">&#x1F50D;</span>
    <input type="text" id="search" placeholder="Search POIs..." oninput="doSearch(this.value)">
    <div class="results" id="searchResults"></div>
</div>

<div class="info-panel" id="infoPanel">
    <h3>&#x1F30D; Nerdhalla</h3>
    <div class="stat"><span>Seed</span><span>yzZ5fr2tGa</span></div>
    <div class="stat"><span>World</span><span>24000 x 24000</span></div>
    <div class="stat"><span>POIs</span><span>""" + str(len(pois)) + """</span></div>
    <div class="stat"><span>Items</span><span>""" + str(len(contents)) + """</span></div>
    <div class="stat"><span>Biomes</span><span>9</span></div>
    <hr style="border-color:#333;margin:6px 0;">
    <div style="font-size:11px;color:#888;">
        Click a marker for details<br>
        Search by name or category
    </div>
</div>

<div class="toggle-layer">
    <button class="active" onclick="toggleLayer('composite')" id="btn-composite">&#x1F5FA; Composite</button>
    <button onclick="toggleLayer('biome')" id="btn-biome">&#x1F33F; Biome</button>
    <button onclick="toggleLayer('height')" id="btn-height">&#x26F0; Height</button>
</div>

<div class="legend" id="legend"></div>

<script>
const POIS = """ + pois_json + """;
const CONTENTS = """ + contents_json + """;
const CAT_COLORS = """ + cat_colors_json + """;
const CAT_LABELS = """ + cat_labels_json + """;
const CAT_COUNTS = """ + cat_counts_json + """;

const map = L.map('map', {
    center: [2048, 2048],
    zoom: -2,
    minZoom: -3,
    maxZoom: 3,
    crs: L.CRS.Simple,
    zoomSnap: 0.5,
    zoomDelta: 0.5,
});

const bounds = [[0, 0], [4096, 4096]];

const layers = {};
layers.composite = L.imageOverlay('world_composite.png', bounds, { opacity: 1.0 }).addTo(map);
layers.biome = L.imageOverlay('world_biome.png', bounds, { opacity: 0.0 }).addTo(map);
layers.height = L.imageOverlay('world_height.png', bounds, { opacity: 0.0 }).addTo(map);

let activeLayer = 'composite';

function toggleLayer(name) {
    if (name === activeLayer) return;
    layers[activeLayer].setOpacity(0.0);
    layers[name].setOpacity(1.0);
    document.getElementById('btn-' + activeLayer).classList.remove('active');
    document.getElementById('btn-' + name).classList.add('active');
    activeLayer = name;
}

let markerLayer = L.layerGroup().addTo(map);
let allMarkers = [];
let activeCategories = new Set(Object.keys(CAT_COUNTS));

function createMarkers() {
    markerLayer.clearLayers();
    allMarkers = [];
    
    POIS.forEach(function(p) {
        if (!activeCategories.has(p.category)) return;
        
        const color = CAT_COLORS[p.category] || '#888';
        const icon = L.divIcon({
            className: '',
            html: '<div style="width:10px;height:10px;border-radius:50%;background:' + color + ';border:2px solid rgba(255,255,255,0.5);box-shadow:0 0 4px rgba(0,0,0,0.5);"></div>',
            iconSize: [10, 10],
            iconAnchor: [5, 5],
        });
        
        const marker = L.marker([p.py, p.px], { icon: icon }).addTo(markerLayer);
        
        let popup = '<div class="popup-content">';
        popup += '<div class="name">' + p.name + '</div>';
        popup += '<div class="coords">(' + p.x + ', ' + p.z + ')</div>';
        popup += '<div style="color:' + color + ';">' + p.category + '</div>';
        if (p.has_dungeon) popup += '<span class="badge badge-dungeon">Dungeon</span>';
        if (p.has_important) popup += '<span class="badge badge-treasure">Has Loot</span>';
        
        const items = CONTENTS.filter(function(c) { return c.loc_id === p.id; });
        if (items.length > 0) {
            popup += '<hr style="border-color:#333;margin:4px 0;"><div style="font-size:11px;">';
            for (let i = 0; i < Math.min(5, items.length); i++) {
                popup += '<div>&bull; ' + items[i].item + ' x' + items[i].count + '</div>';
            }
            if (items.length > 5) {
                popup += '<div style="color:#888;">... +' + (items.length - 5) + ' more</div>';
            }
            popup += '</div>';
        }
        
        popup += '</div>';
        marker.bindPopup(popup);
        allMarkers.push({ marker: marker, p: p });
    });
}

function buildLegend() {
    const legend = document.getElementById('legend');
    const order = ['boss', 'dungeon', 'vegvisir', 'vendor', 'resource', 'altar', 'structure', 'spawner', 'point_of_interest', 'other'];
    let html = '';
    order.forEach(function(cat) {
        if (!CAT_COUNTS[cat]) return;
        const color = CAT_COLORS[cat] || '#888';
        const active = activeCategories.has(cat) ? 'active' : 'inactive';
        html += '<div class="legend-item ' + active + '" onclick="toggleCategory(\\'' + cat + '\\')">';
        html += '<span class="dot" style="background:' + color + '"></span>';
        html += '<span>' + (CAT_LABELS[cat] || cat) + '</span>';
        html += '<span class="count">' + CAT_COUNTS[cat] + '</span></div>';
    });
    legend.innerHTML = html;
}

function toggleCategory(cat) {
    if (activeCategories.has(cat)) {
        activeCategories.delete(cat);
    } else {
        activeCategories.add(cat);
    }
    createMarkers();
    buildLegend();
}

function doSearch(query) {
    const results = document.getElementById('searchResults');
    if (!query || query.length < 2) { results.style.display = 'none'; return; }
    
    const q = query.toLowerCase();
    const matches = POIS.filter(function(p) {
        return p.name.toLowerCase().indexOf(q) !== -1 || p.category.toLowerCase().indexOf(q) !== -1;
    }).slice(0, 20);
    
    if (matches.length === 0) { results.style.display = 'none'; return; }
    
    let html = '';
    matches.forEach(function(p) {
        html += '<div onclick="flyTo(' + p.px + ', ' + p.py + ', \\'' + p.name + '\\')">';
        html += '<span style="color:' + (CAT_COLORS[p.category] || '#888') + ';">&#x25CF;</span> ';
        html += p.name + ' <span class="cat">' + p.category + '</span></div>';
    });
    results.innerHTML = html;
    results.style.display = 'block';
}

function flyTo(px, py, name) {
    map.flyTo([py, px], 1, { duration: 0.5 });
    document.getElementById('searchResults').style.display = 'none';
    document.getElementById('search').value = name;
}

document.addEventListener('click', function(e) {
    if (!e.target.closest('.search-box')) {
        document.getElementById('searchResults').style.display = 'none';
    }
});

createMarkers();
buildLegend();
map.fitBounds(bounds);
</script>
</body>
</html>"""
    
    with open(VIEWER_PATH, 'w') as f:
        f.write(html)
    print(f"Viewer written to {VIEWER_PATH}")

if __name__ == '__main__':
    print("Building Valheim map viewer...")
    pois, contents = query_pois()
    print(f"Loaded {len(pois)} POIs and {len(contents)} important contents")
    build_viewer(pois, contents)
    print("Done!")
