import sys, json, math, numpy as np
sys.path.insert(0, '/home/claude/loop/lib'); sys.path.insert(0, '/home/claude/dv/cards')
import render3 as r, g1
from PIL import Image, ImageDraw
from pyproj import Transformer
r.W, r.H = 1080, 1350; SS = 4
BASE = dict(r.DEFAULT, field_L=17, field_hue=270, field_C=16, land_L=23, land_hue=268, land_C=14, land_mode='fill',
            subject_mode='stack', stack_from=1979, stack_L_hi=97, stack_C_hi=0, stack_bands=8, stack_paint_year=False,
            ref_mode='none', median_line=False, line79=False, smooth_km=12, crop_km=5000, center_km=[-460, 0])
tr = Transformer.from_crs('EPSG:4326', 'EPSG:3411', always_xy=True)
NAVY = np.array(r.col(17, 270, 16), np.float32); WHITE = np.array([247,247,250], np.float32)
ON_LIGHT = np.array(r.col(58, 265, 12), np.float32); ON_DARK = np.array(r.col(40, 265, 14), np.float32)
PLACES = [('GREENLAND', -41, 74), ('SVALBARD', 22, 80.3), ('ALASKA', -150, 65.2), ('SIBERIA', 112, 69.8), ('CANADA', -105, 68), ('FRANZ JOSEF LAND', 52, 81.6), ('SEVERNAYA ZEMLYA', 96, 80.3)]
SEAS = [('BEAUFORT SEA', -140, 72.5), ('CHUKCHI SEA', -170, 70.5), ('EAST SIBERIAN SEA', 160, 73.5), ('LAPTEV SEA', 125, 76), ('KARA SEA', 72, 76.5), ('BARENTS SEA', 26, 72.6), ('GREENLAND SEA', -8, 76)]
STRAITS = [('FRAM STRAIT', -2, 79.2), ('BERING STRAIT', -169, 65.8)]
TOWNS = [('LONGYEARBYEN', 15.63, 78.22), ('UTQIAĠVIK', -156.79, 71.29), ('NUUK', -51.72, 64.18), ('TIKSI', 128.87, 71.64), ('IQALUIT', -68.5, 63.75),
         ('ALERT', -62.35, 82.50), ('QAANAAQ', -69.23, 77.47), ('RESOLUTE', -94.83, 74.70), ('CAMBRIDGE BAY', -105.05, 69.12), ('INUVIK', -133.72, 68.36),
         ('NOME', -165.41, 64.50), ('ANADYR', 177.51, 64.73), ('PEVEK', 170.31, 69.70), ('KHATANGA', 102.47, 71.98), ('DIKSON', 80.52, 73.51),
         ('NORILSK', 88.20, 69.35), ('SALEKHARD', 66.60, 66.53), ('VORKUTA', 64.05, 67.50), ('NARYAN-MAR', 53.01, 67.64), ('HAMMERFEST', 23.68, 70.66),
         ('TROMSØ', 18.96, 69.65), ('ILULISSAT', -51.10, 69.22), ('TASIILAQ', -37.64, 65.61), ('REYKJAVÍK', -21.94, 64.15), ('NY-ÅLESUND', 11.93, 78.92)]
RIVERS = ['Ob', 'Yenisey', 'Lena', 'Mackenzie', 'Kolyma', 'Yukon', 'Pechora', 'Indigirka', 'Khatanga']
def lstar(rgb):
    x = rgb.astype(np.float32)/255; y = 0.2126*x[...,0]+0.7152*x[...,1]+0.0722*x[...,2]; return 116*np.cbrt(y)-16
def aa_mask(size, fn):
    big = Image.new('L', (size[0]*SS, size[1]*SS), 0); d = ImageDraw.Draw(big); fn(d, SS)
    return np.array(big.resize(size, Image.LANCZOS)).astype(np.float32)/255
def comp_rel(im, alpha, kd, kl):
    base = np.array(im).astype(np.float32); L = lstar(base)
    toward = np.where((L > 55)[..., None], base*(1-kl) + NAVY*kl, base*(1-kd) + WHITE*kd)
    a = alpha[..., None]; return Image.fromarray(np.clip(base*(1-a) + toward*a, 0, 255).astype(np.uint8))
def comp_label(im, alpha, tone_light=ON_LIGHT, tone_dark=ON_DARK):
    base = np.array(im).astype(np.float32); L = lstar(base); sel = alpha > 0.05
    tone = tone_light if (sel.any() and L[sel].mean() > 55) else tone_dark
    a = alpha[..., None]; return Image.fromarray(np.clip(base*(1-a) + tone*a, 0, 255).astype(np.uint8))
def dashed(d, pts, width, s, dash):
    on, off = dash[0]*s, dash[1]*s; draw = True; acc = 0.0
    for (x0,y0),(x1,y1) in zip(pts[:-1], pts[1:]):
        seg = math.hypot(x1-x0, y1-y0); t = 0.0
        while t < seg:
            step = min(seg-t, (on if draw else off)-acc)
            if draw: d.line([(x0+(x1-x0)*t/seg, y0+(y1-y0)*t/seg), (x0+(x1-x0)*(t+step)/seg, y0+(y1-y0)*(t+step)/seg)], fill=255, width=int(width*s))
            t += step; acc += step
            if acc >= (on if draw else off)-1e-6: draw = not draw; acc = 0.0
def P(sc, lon, lat, s=1): x, y = sc.to_px(*tr.transform(lon, lat)); return (x*s, y*s)
def grat(sc, dash=(2.0,5.0)):
    def fn(d, s):
        for lat in (60,70,80): dashed(d, [P(sc,lon,lat,s) for lon in range(-180,181,1)], 1.0, s, dash)
        for lon in range(-180,180,30): dashed(d, [P(sc,lon,lat,s) for lat in np.arange(55,90.01,0.5)], 1.0, s, dash)
    return fn
def circle66(sc):   # 北極圏 66.6°N: 間隔の違う点線
    def fn(d, s): dashed(d, [P(sc,lon,66.56,s) for lon in range(-180,181,1)], 1.0, s, (1.0, 2.5))
    return fn
LEFT = {'NY-ÅLESUND', 'RESOLUTE', 'ALERT'}
def text_fn(sc, item, size, track=0.16, dot=0):
    def fn(d, s):
        f = g1.font('Regular', size*s); name, lon, lat = item; x, y = P(sc, lon, lat, s)
        if dot:
            d.ellipse([x-dot*s, y-dot*s, x+dot*s, y+dot*s], fill=255); y -= size*s*0.55
            if name in LEFT:
                wsum = sum(d.textlength(ch, font=f) + size*track*s for ch in name); x -= (dot+6)*s + wsum
            else: x += (dot+6)*s
        for ch in name: d.text((x, y), ch, font=f, fill=255); x += d.textlength(ch, font=f) + size*track*s
    return fn
def rivers_fn(sc, land_mask):
    g = json.load(open('/home/claude/loop/data/raw/ne_50m_rivers_lake_centerlines.geojson'))
    def fn(d, s):
        for f in g['features']:
            if (f['properties'].get('name') or '') not in RIVERS: continue
            geom = f['geometry']; lines = geom['coordinates'] if geom['type'] == 'LineString' else [l for l in geom['coordinates']]
            for line in lines:
                pts = [P(sc, lon, lat, s) for lon, lat in line if lat > 50]
                if len(pts) > 1: d.line(pts, fill=255, width=int(1.0*s))
    return fn
def lat_labels(sc, lon=-118):
    def fn(d, s):
        f = g1.font('Regular', 15*s)
        for lat, txt, ln in ((60,'60°N',lon),(66.56,'66.6°N',-162),(70,'70°N',lon),(80,'80°N',lon)):
            x, y = P(sc, ln, lat, s); d.text((x+4*s, y-18*s), txt, font=f, fill=255)
    return fn
def lon_labels(sc, lat=61.5):
    def fn(d, s):
        f = g1.font('Regular', 15*s)
        for lon in range(-180,180,30):
            txt = ('0°' if lon == 0 else f'{abs(lon)}°{"E" if lon>0 else "W"}') if lon != -180 else '180°'
            x, y = P(sc, lon, lat, s); d.text((x+4*s, y-18*s), txt, font=f, fill=255)
    return fn
import os
out = r.render(BASE, '/home/claude/loop/human/v3card/pure4_base')
sc = r.Scene(BASE['crop_km'], (BASE['center_km'][0]*1000.0, BASE['center_km'][1]*1000.0))
land = np.array(Image.open(f'{out}/mask-land.png')) > 127
if not os.environ.get('PURE4_NO_MAIN'):
    im = Image.open(f'{out}/render.png'); size = im.size
    im = comp_rel(im, aa_mask(size, grat(sc)), 0.22, 0.16)
    im = comp_rel(im, aa_mask(size, circle66(sc)), 0.22, 0.16)
    # 河川: 陸の上だけ、陸より一段明るく

    im = comp_rel(im, aa_mask(size, lat_labels(sc)), 0.30, 0.20)

    for it in PLACES: im = comp_label(im, aa_mask(size, text_fn(sc, it, 20)))


    for it in TOWNS: im = comp_label(im, aa_mask(size, text_fn(sc, it, 14, 0.10, dot=2.2)))
    im.save('/home/claude/loop/human/v3card/Z2.png'); im.crop((150,150,750,750)).save('/home/claude/loop/human/v3card/Z2_detail.png')
    print('ok')
