# -*- coding: utf-8 -*-
"""Generira preview/index.html — interaktivni EDITOR trasa (Leaflet + BRouter API).

Mogućnosti u pregledniku (treba internet):
  · klik na rutu = odabir za uređivanje (prikažu se točke)
  · povlačenje točke = pomak (postaje via-točka) + živi preračun po cestama
  · klik na odabranu liniju = nova via-točka na tom mjestu
  · dvoklik na točku = brisanje (osim početne/završne)
  · 💾 Preuzmi rute.json = izvoz izmjena -> spremiti preko source/rute/rute.json
    pa pokrenuti build_rute.py + gen_preview.py za trajnu regeneraciju.
"""
import json, os

BASE = os.path.dirname(os.path.abspath(__file__))
places = json.load(open(os.path.join(BASE, "places.json"), encoding="utf-8"))
cfg = json.load(open(os.path.join(BASE, "rute.json"), encoding="utf-8"))

COLORS = {"R1": "#d62728", "R2": "#2ca02c", "R3": "#1f77b4", "R4": "#ff7f0e",
          "R5": "#9467bd", "R6": "#0e8585", "R7": "#8c564b", "R8": "#d63384"}

routes = []
for r in cfg["rute"]:
    p = os.path.join(BASE, "geojson", f"{r['id'].lower()}.geojson")
    if os.path.exists(p):
        gj = json.load(open(p, encoding="utf-8"))
        routes.append({"id": r["id"], "color": COLORS[r["id"]], "geojson": gj,
                       "props": gj["features"][0]["properties"]})

pl = {k: {"lat": v["lat"], "lon": v["lon"], "manual": v["izvor"] != "nominatim"}
      for k, v in places.items() if v["lat"]}

html = """<!DOCTYPE html>
<html lang="hr"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>BIKE CROSS — editor trasa (interni draft)</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>
<script src="https://unpkg.com/leaflet-simple-map-screenshoter@0.5.0/dist/leaflet-simple-map-screenshoter.js"></script>
<style>
  html,body{margin:0;height:100%;font-family:-apple-system,'Segoe UI',Roboto,sans-serif;background:#fff}
  body{display:flex;flex-direction:column}
  #top{flex:none;background:#1a3e5c;color:#fff;padding:7px 14px;display:flex;gap:12px;align-items:center;flex-wrap:wrap}
  #top b{font-size:15px} #top .draft{background:#c8102e;border-radius:4px;padding:2px 8px;font-size:12px}
  #routes{display:flex;gap:4px 12px;flex-wrap:wrap;font-size:13px}
  #routes label{display:flex;gap:5px;align-items:center;cursor:pointer;white-space:nowrap}
  #routes .edited::after{content:" •";color:#ffd23f;font-weight:700}
  .sw{display:inline-block;width:14px;height:5px;border-radius:2px}
  #bar{flex:none;background:#ecf1f5;border-bottom:1px solid #c9d4de;padding:5px 14px;display:flex;gap:12px;align-items:center;font-size:13px;color:#333}
  #bar button{border:1px solid #9fb2c2;background:#fff;border-radius:6px;padding:4px 12px;font-size:13px;cursor:pointer}
  #bar button:hover{background:#f2f6f9}
  #done{display:none}
  #status{color:#555}
  #map{flex:1;min-height:0}
  .wpm{background:#fff;border:3px solid #1a3e5c;border-radius:50%;box-shadow:0 0 4px rgba(0,0,0,.45);cursor:grab}
  .wpm.via{background:#ff9500;border-color:#8a5200}
  .leaflet-tooltip{font-size:12px}
</style></head><body>
<div id="top"><b>BIKE CROSS — trase</b><span class="draft">INTERNI DRAFT · uređivanje uživo</span><div id="routes"></div></div>
<div id="bar">
  <span>🖱 klik na rutu = uređivanje · povuci točku · klik na liniju = nova točka · dvoklik na točku = brisanje</span>
  <button id="done">✕ Završi uređivanje</button>
  <button id="dl">💾 Preuzmi rute.json</button>
  <button id="shot">📷 Spremi sliku</button>
  <span id="status"></span>
</div>
<div id="map"></div>
<script>
const CFG = __CFG__;
const PLACES = __PLACES__;
const ROUTES = __ROUTES__;
const PROFILE = CFG.profil || 'trekking';

const map = L.map('map', {zoomSnap: 0.25, zoomDelta: 0.25, wheelPxPerZoomLevel: 120}).setView([46.20, 17.05], 11);
const carto = L.tileLayer('https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png',
  {maxZoom:19, subdomains:'abcd', crossOrigin:'anonymous', attribution:'&copy; OpenStreetMap contributors &copy; CARTO'});
const esriTopo = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{z}/{y}/{x}',
  {maxZoom:19, crossOrigin:'anonymous', attribution:'Esri, HERE, Garmin, &copy; OpenStreetMap contributors'});
const esriSat = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
  {maxNativeZoom:18, maxZoom:19, crossOrigin:'anonymous', attribution:'Esri, Maxar, Earthstar Geographics'});
const satLabels = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/Reference/World_Boundaries_and_Places/MapServer/tile/{z}/{y}/{x}',
  {maxNativeZoom:18, maxZoom:19, crossOrigin:'anonymous', attribution:''});
carto.addTo(map);
L.control.layers({'Karta (Carto)': carto, 'Topo (Esri)': esriTopo, 'Satelit (Esri)': esriSat},
                 {'Nazivi mjesta (uz satelit)': satLabels}, {position:'topright'}).addTo(map);

const all = L.featureGroup().addTo(map);
const markers = L.layerGroup().addTo(map);
const state = {};
let selected = null, squelch = false;

function setStatus(t){ document.getElementById('status').textContent = t; }
function isManual(wp){ return wp && typeof wp === 'object' && !Array.isArray(wp) && wp.rucno; }
function wpCoord(wp){ if (isManual(wp)) return {lon: wp.rucno[0][0], lat: wp.rucno[0][1]}; if (Array.isArray(wp)) return {lon: wp[0], lat: wp[1]}; const p = PLACES[wp]; return {lon: p.lon, lat: p.lat}; }
function lonlats(wps){ return wps.map(w => { const c = wpCoord(w); return c.lon.toFixed(6) + ',' + c.lat.toFixed(6); }).join('|'); }
function segDist(p, a, b){ // udaljenost točke od segmenta (ravninska aproksimacija)
  const kx = Math.cos(46 * Math.PI / 180);
  const ax = (a.lon - p.lon) * kx, ay = a.lat - p.lat, bx = (b.lon - p.lon) * kx, by = b.lat - p.lat;
  const dx = bx - ax, dy = by - ay;
  const t = Math.max(0, Math.min(1, -(ax * dx + ay * dy) / (dx * dx + dy * dy || 1e-12)));
  const cx = ax + t * dx, cy = ay + t * dy;
  return Math.hypot(cx, cy);
}

async function recalc(id){
  const st = state[id];
  setStatus(id + ': preračunavam…');
  const url = 'https://brouter.de/brouter?lonlats=' + encodeURIComponent(lonlats(st.wps)) +
              '&profile=' + PROFILE + '&alternativeidx=0&format=geojson';
  const resp = await fetch(url);
  if (!resp.ok) throw new Error('BRouter ' + resp.status);
  const gj = await resp.json();
  const f = gj.features[0];
  st.km = Math.round(parseInt(f.properties['track-length'] || 0) / 100) / 10;
  st.asc = parseInt(f.properties['filtered ascend'] || 0);
  st.layer.clearLayers();
  st.layer.addData(gj);
  st.layer.setStyle({color: st.color, weight: selected === id ? 7 : 4, opacity: selected === id ? 1 : 0.85});
  st.edited = true;
  document.getElementById('len-' + id).textContent = st.km + ' km';
  document.getElementById('lab-' + id).classList.add('edited');
  setStatus(id + ': ' + st.km + ' km · uspon ' + st.asc + ' m ✓ (ne zaboravi 💾 na kraju)');
}

function drawMarkers(){
  markers.clearLayers();
  if (!selected) return;
  const st = state[selected];
  st.wps.forEach((wp, i) => {
    const c = wpCoord(wp);
    const isVia = Array.isArray(wp);
    const m = L.marker([c.lat, c.lon], {draggable: true,
      icon: L.divIcon({className: 'wpm' + (isVia ? ' via' : ''), iconSize: [15, 15]})}).addTo(markers);
    m.bindTooltip(isVia ? ('via ' + (i + 1)) : wp, {direction: 'top'});
    m.on('dragend', () => {
      const ll = m.getLatLng();
      const old = st.wps.slice();
      st.wps[i] = [+ll.lng.toFixed(6), +ll.lat.toFixed(6)];
      recalc(selected).then(drawMarkers).catch(e => { st.wps = old; drawMarkers(); alert('Preračun nije uspio: ' + e.message); });
    });
    m.on('dblclick', ev => {
      L.DomEvent.stop(ev);
      if (i === 0 || i === st.wps.length - 1){ setStatus('Početna/završna točka se ne briše.'); return; }
      const old = st.wps.slice();
      st.wps.splice(i, 1);
      recalc(selected).then(drawMarkers).catch(e => { st.wps = old; drawMarkers(); alert('Preračun nije uspio: ' + e.message); });
    });
  });
}

function select(id){
  if (selected === id) return;
  if (state[id].wps.some(isManual)){
    setStatus(id + ' sadrži ručno iscrtan segment (put izvan OSM-a) — uređuje se kroz rute.json, ne u pregledniku.');
    return;
  }
  deselect();
  selected = id;
  const st = state[id];
  st.layer.setStyle({weight: 7, opacity: 1});
  st.layer.bringToFront();
  map.doubleClickZoom.disable();
  document.getElementById('done').style.display = 'inline-block';
  drawMarkers();
  setStatus(id + ' — uređivanje (' + st.km + ' km)');
}
function deselect(){
  if (!selected) return;
  state[selected].layer.setStyle({weight: 4, opacity: 0.85});
  selected = null;
  markers.clearLayers();
  map.doubleClickZoom.enable();
  document.getElementById('done').style.display = 'none';
  setStatus('');
}
map.on('click', () => { if (squelch){ squelch = false; return; } deselect(); });
document.getElementById('done').onclick = deselect;

function routeClick(id, e){
  squelch = true; setTimeout(() => squelch = false, 0);
  if (selected !== id){ select(id); return; }
  const st = state[id];
  const p = {lon: e.latlng.lng, lat: e.latlng.lat};
  let best = 1, bd = 1e9;
  for (let i = 0; i < st.wps.length - 1; i++){
    const d = segDist(p, wpCoord(st.wps[i]), wpCoord(st.wps[i + 1]));
    if (d < bd){ bd = d; best = i + 1; }
  }
  const old = st.wps.slice();
  st.wps.splice(best, 0, [+p.lon.toFixed(6), +p.lat.toFixed(6)]);
  recalc(id).then(drawMarkers).catch(err => { st.wps = old; drawMarkers(); alert('Preračun nije uspio: ' + err.message); });
}

ROUTES.forEach(r => {
  const rc = CFG.rute.find(x => x.id === r.id);
  const lay = L.geoJSON(r.geojson, {style: {color: r.color, weight: 4, opacity: 0.85}})
    .on('click', e => routeClick(r.id, e))
    .on('mouseover', () => { if (selected !== r.id) lay.setStyle({weight: 6}); })
    .on('mouseout', () => { if (selected !== r.id) lay.setStyle({weight: 4}); });
  lay.addTo(all);
  state[r.id] = {wps: rc.waypoints.slice(), layer: lay, color: r.color,
                 km: r.props.duljina_km, asc: r.props.uspon_m, edited: false};
  const lb = document.createElement('label');
  lb.id = 'lab-' + r.id;
  lb.innerHTML = '<input type="checkbox" checked>' +
    '<span class="sw" style="background:' + r.color + '"></span>' + r.id +
    ' <span style="opacity:.75" id="len-' + r.id + '">' + r.props.duljina_km + ' km</span>';
  lb.title = r.props.naziv;
  lb.querySelector('input').onchange = e => {
    if (e.target.checked){ lay.addTo(all); }
    else { if (selected === r.id) deselect(); all.removeLayer(lay); }
  };
  document.getElementById('routes').appendChild(lb);
});

Object.entries(PLACES).forEach(([k, p]) => {
  L.circleMarker([p.lat, p.lon], {radius: 3.5, color: '#1a3e5c', weight: 1.5,
    fillColor: p.manual ? '#c8102e' : '#fff', fillOpacity: 0.9, opacity: 0.7, interactive: false}).addTo(map);
});

document.getElementById('dl').onclick = () => {
  const out = Object.assign({}, CFG, {rute: CFG.rute.map(r => Object.assign({}, r, {waypoints: state[r.id].wps}))});
  const blob = new Blob([JSON.stringify(out, null, 2)], {type: 'application/json'});
  const a = document.createElement('a');
  a.href = URL.createObjectURL(blob);
  a.download = 'rute.json';
  a.click();
  setStatus('Preuzeto rute.json — spremi ga preko source/rute/rute.json i javi za regeneraciju.');
};

const screenshoter = L.simpleMapScreenshoter({hidden: true}).addTo(map);
document.getElementById('shot').onclick = () => {
  setStatus('Snimam sliku…');
  screenshoter.takeScreen('blob').then(blob => {
    const a = document.createElement('a');
    a.href = URL.createObjectURL(blob);
    a.download = 'bikecross-trase-' + new Date().toISOString().slice(0, 16).replace(/[:T]/g, '-') + '.png';
    a.click();
    setStatus('Slika spremljena u Preuzimanja ✓');
  }).catch(e => { setStatus(''); alert('Snimanje nije uspjelo: ' + e); });
};

function fit(){ map.invalidateSize(); map.fitBounds(all.getBounds().pad(0.06)); }
window.addEventListener('load', () => requestAnimationFrame(fit));
window.addEventListener('resize', () => map.invalidateSize());
setTimeout(fit, 400);
</script></body></html>
"""
html = html.replace("__CFG__", json.dumps(cfg, ensure_ascii=False))
html = html.replace("__PLACES__", json.dumps(pl, ensure_ascii=False))
html = html.replace("__ROUTES__", json.dumps(routes, ensure_ascii=False))
out = os.path.join(BASE, "preview", "index.html")
with open(out, "w", encoding="utf-8") as f:
    f.write(html)
print("OK ->", out, f"({os.path.getsize(out)//1024} KB)")
