"""Build browser data from the approved cartographic SVG and original GPX files."""
from pathlib import Path
import sys
import json
import re
import math
import base64
import gzip
import io
import xml.etree.ElementTree as ET
import numpy as np
import cairosvg
from PIL import Image
from scipy.ndimage import median_filter, gaussian_filter1d
from pyproj import Geod, Transformer

ROOT = Path(sys.argv[1]).resolve()
HERE = Path(__file__).resolve().parent.parent
SVG_NS = 'http://www.w3.org/2000/svg'
ET.register_namespace('', SVG_NS)
ns = {'s': SVG_NS}
report = json.loads((ROOT/'output/map/Harita_Dogrulama.json').read_text())
fc = json.loads((ROOT/'output/map/Alanya_12_Rota_Orijinal_Koordinatlar.geojson').read_text())
svg = ET.parse(ROOT/'output/map/Alanya_Bisiklet_Rotalari_Katmanli.svg').getroot()
groups = {g.attrib['id']:g for g in svg.findall('s:g', ns)}
image_path = HERE/'kaynak/arazi.webp'
with Image.open(ROOT/'output/map/Alanya_Arazi_Taban.png') as im:
    im.resize((3600,1584), Image.Resampling.LANCZOS).save(image_path, 'WEBP',quality=87,method=6)
terrain = base64.b64encode(image_path.read_bytes()).decode()
background = '<image x="0" y="0" width="3000" height="1320" href="data:image/webp;base64,'+terrain+'"/>'
background += '<g transform="translate(0 -245)">'
for name in ['02_Sular_ve_dogal_alanlar','03_Yollar']:
    background += ET.tostring(groups[name],encoding='unicode')
background += '</g>'
# Keep the fixed terrain, roads and water in one raster layer so that mobile
# cameras and the cycling animation only redraw the interactive route vectors.
fixed_svg='<svg xmlns="http://www.w3.org/2000/svg" width="3600" height="1584" viewBox="0 0 3000 1320">'+background+'</svg>'
fixed_png=cairosvg.svg2png(bytestring=fixed_svg.encode())
with Image.open(io.BytesIO(fixed_png)) as im:
    im.convert('RGB').save(HERE/'kaynak/zemin.webp','WEBP',quality=90,method=6)
fixed=base64.b64encode((HERE/'kaynak/zemin.webp').read_bytes()).decode()
background='<image x="0" y="0" width="3000" height="1320" href="data:image/webp;base64,'+fixed+'"/>'
geod = Geod(ellps='WGS84')
fwd = Transformer.from_crs(4326,32636,always_xy=True)
x0,y0 = fwd.transform(32,36.48)
c,s = math.cos(math.radians(35)),math.sin(math.radians(35))
umin,vmin,umax,vmax = report['map_bounds_utm_rotated']
scale = 3000/(umax-umin)
def project(lon,lat):
    x,y=fwd.transform(lon,lat);x-=x0;y-=y0
    u,v=c*x-s*y,s*x+c*y
    return [round((u-umin)*scale,3),round((vmax-v)*scale,3)]
data={'width':3000,'height':1320,'rotation':35,'basemap':background,'towns':[], 'pois':[], 'routes':[]}
for town in report['towns']:
    data['towns'].append({'name':town['name'],'x':town['xy'][0],'y':town['xy'][1]-245,'major':town['name'] in ['Alanya','Gazipaşa','Gündoğmuş']})
keys=['alara','castle','cave','canyon','syedra','reservoir','airport']
for key,poi in zip(keys, report['pois']):
    data['pois'].append({'key':key,'x':poi['xy'][0],'y':poi['xy'][1]-245})
for feature,record in zip(fc['features'],report['routes']):
    prop=feature['properties'];rid=prop['route']
    xyz=np.asarray(feature['geometry']['coordinates'])
    _,_,steps=geod.inv(xyz[:-1,0],xyz[:-1,1],xyz[1:,0],xyz[1:,1])
    km=np.r_[0,np.cumsum(steps)]/1000
    # Distance-domain smoothing removes short GPS elevation spikes.
    grid=np.linspace(0,km[-1],max(2,int(km[-1]*1000/25)+1))
    elev=np.interp(grid,km,xyz[:,2])
    smooth=gaussian_filter1d(median_filter(elev,size=7,mode='nearest'),2)
    delta=np.diff(smooth)
    ascent=int(round(np.maximum(delta,0).sum()/10)*10)
    descent=int(round(-np.minimum(delta,0).sum()/10)*10)
    profile_x=np.linspace(0,km[-1],240)
    profile_y=np.interp(profile_x,grid,smooth)
    bywidth={};points=[]
    for path in groups[f'04_Rota_{rid:02d}'].findall('s:path',ns):
        d=path.attrib['d'];w=path.attrib['stroke-width']
        bywidth.setdefault(w,[]).append(d)
        nums=[float(x) for x in re.findall(r'-?\d+(?:\.\d+)?',d)]
        for x,y in zip(nums[::2],nums[1::2]):
            q=[x,y-245]
            if not points or math.hypot(q[0]-points[-1][0],q[1]-points[-1][1])>.02:
                points.append(q)
    # Shift the source SVG paths into the map-only viewport without reprojecting.
    segments=[{'width':float(w),'d':' '.join(parts)} for w,parts in bywidth.items()]
    p=np.array(points)
    bounds=[float(p[:,0].min()),float(p[:,1].min()),float(p[:,0].max()),float(p[:,1].max())]
    dists=np.r_[0,np.cumsum(np.linalg.norm(np.diff(p,axis=0),axis=1))]
    animation=[[round(x,2),round(y,2),round(float(z/dists[-1]),6)] for (x,y),z in zip(points,dists)]
    # Preserve every original source byte in the downloadable GPX.
    gpx_name=prop['source_gpx'];gpx=(ROOT/'upload'/gpx_name).read_bytes()
    (HERE/'rotalar'/f'rota-{rid:02d}.gpx').write_bytes(gpx)
    data['routes'].append({'id':rid,'name':prop['name'],'color':prop['colour'],'km':round(float(km[-1]),3),
                          'ascent':ascent,'descent':descent,'min':round(float(xyz[:,2].min())),
                          'max':round(float(xyz[:,2].max())),'pointCount':len(xyz),'bounds':bounds,
                          'segments':segments,'points':animation,'start':project(*xyz[0,:2]),
                          'end':project(*xyz[-1,:2]),'badge':[record['badge'][0],record['badge'][1]-245],
                          'profile':[[round(float(x),3),round(float(y),1)] for x,y in zip(profile_x,profile_y)],
                          'gpxName':gpx_name,'gpxGzip':base64.b64encode(gzip.compress(gpx,mtime=0)).decode()})
(HERE/'kaynak/rota_verisi.json').write_text(json.dumps(data,ensure_ascii=False,separators=(',',':')),encoding='utf-8')
print('Web data:',len(data['routes']),'routes;',len(background),'background characters')
print('Elevation statistics:',[(r['id'],r['ascent'],r['descent']) for r in data['routes']])
