#!/usr/bin/env python3
"""Build two more HTML table fragments for the report from the terrain and radiocarbon outputs.
Writes frag/table_routes.html (from data/terrain_tables.md, Table A, itself generated by code/report_tables.py
from data/routes_output.csv and data/viewshed_output.json) and frag/table_c14.html (from
data/radiocarbon_calibration.md, generated by code/calibrate.py)."""
import pathlib, re, html
here = pathlib.Path(__file__).resolve().parent
data = here.parent / 'data'
frag = here / 'frag'; frag.mkdir(exist_ok=True)

def md_table(text, heading_regex):
    """Return (header, rows) of the first markdown table after a heading matching heading_regex."""
    m = re.search(heading_regex, text)
    assert m, heading_regex
    lines = text[m.end():].split('\n')
    tbl = [l for l in lines[:400] if l.startswith('|')]
    # stop at first blank after table
    out = []
    started = False
    for l in lines:
        if l.startswith('|'):
            out.append(l); started = True
        elif started:
            break
    cells = [[c.strip() for c in l.strip().strip('|').split('|')] for l in out]
    header = cells[0]; rows = [r for r in cells[2:]]
    return header, rows

# ---- routes table (Table A)
t = (data / 'terrain_tables.md').read_text(encoding='utf-8')
header, rows = md_table(t, r'### Table A\.[^\n]*\n')
# columns: route | col (map/DEM) | Island->gate km (stades) | gate->col km (climb) | km/day 9 d | col->plain km | km/day 4 d (3 d) | gate->plain km (stades) | km/day 13 d | steepest 500 m | plain visible
names = {
 'R1': ('Mont-Cenis', 'Isère, Arc, Mont-Cenis'),
 'R2': ('Clapier', 'Isère, Arc, Savine, Clapier'),
 'R2b': ('Petit Mont-Cenis', 'Isère, Arc, Ambin, Petit Mont-Cenis'),
 'R3': ('Petit St Bernard', 'Isère, Tarentaise, Petit St Bernard, Aosta'),
 'R4': ('Montgenèvre', 'Durance, Briançon, Montgenèvre'),
 'R4c': ('Montgenèvre (Drôme approach)', 'Rhône, Drôme, Cabre, Gap, Durance, Montgenèvre'),
 'R5': ('Traversette', 'Durance, Guil, Traversette'),
 'R5g': ('Traversette (de Beer)', 'Rhône, Drôme, Grimone, Gap, Durance, Guil, Traversette'),
 'R5c': ('Traversette (Cabre approach)', 'Rhône, Drôme, Cabre, Gap, Durance, Guil, Traversette'),
 'R6': ('Larche', 'Durance, Ubaye, Larche, Stura'),
 'R6v': ('Larche (via Vars)', 'Durance, Guillestre, Vars, Ubaye, Larche'),
 'R7': ('Grand St Bernard', 'Rhône, Lyon, Geneva, Valais, Grand St Bernard'),
}
out = ['<div class="tablewrap"><table><thead><tr><th>Route</th><th>Col (map m)</th><th>Gate</th><th class="num">Island to gate, km (stades)</th><th class="num">Gate to col, km (climb m)</th><th class="num">km/day over 9 days</th><th class="num">Col to plain, km</th><th class="num">km/day over 4 days</th><th class="num">Gate to plain, km (stades)</th><th class="num">Steepest 500 m of descent</th><th>Po plain seen from the col</th></tr></thead><tbody>']
for r in rows:
    rid = r[0].split()[0]
    short, longname = names.get(rid, (r[0], r[0]))
    col = r[1].split('/')[0].strip()
    col = re.sub(r'^(du |de la |de )', '', col)
    gate_m = re.match(r'(\S+) (\d+) \((\d+) / (\d+)\)', r[2])
    gate, bkm, bst = gate_m.group(1), gate_m.group(2), gate_m.group(3)
    gc = r[3]; kd9 = r[4]; cp = r[5]; kd4 = r[6].split()[0]; gp = re.match(r'(\d+) \((\d+) / (\d+)\)', r[7]); steep = r[9]; view = r[10]
    view = view.replace('no (0.0%)', 'no').replace('yes (0.6%)', 'a 2° sliver, 80 km off').replace('yes (17.3%)', 'yes, 17% of bearings')
    steep = steep.replace(' at ', ' at ')
    hl = ' class="hl"' if rid in ('R2', 'R5') else ''
    out.append(f'<tr{hl}><td><b>{html.escape(short)}</b><br><span class="sub">{html.escape(longname)}</span></td><td>{html.escape(col)}</td><td>{html.escape(gate)}</td><td class="num">{bkm} ({bst})</td><td class="num">{html.escape(gc)}</td><td class="num">{kd9}</td><td class="num">{cp}</td><td class="num">{kd4}</td><td class="num">{gp.group(1)} ({gp.group(2)})</td><td class="num">{html.escape(steep)}</td><td>{html.escape(view)}</td></tr>')
out.append('</tbody></table></div>')
(frag / 'table_routes.html').write_text('\n'.join(out), encoding='utf-8')

# ---- radiocarbon table
c = (data / 'radiocarbon_calibration.md').read_text(encoding='utf-8')
header, rows = md_table(c, r'# IntCal20 calibration[^\n]*\n')
def strip_bp(s):
    s = re.sub(r'\s*\(cal BP [^)]*\)', '', s)
    return s
out = ['<div class="tablewrap"><table><thead><tr><th>Lab code</th><th class="num">¹⁴C age BP</th><th class="num">Depth cm</th><th>Bed (authors)</th><th>95.4% range, IntCal20</th><th class="num">Median</th><th class="num">P(230 to 200 BC)</th><th>Authors\' 2σ (IntCal13)</th></tr></thead><tbody>']
for r in rows:
    lab, bp, depth, bed, r68, r95, med, p1, p2, auth = r[:10]
    # keep only ranges with >= 3 % mass, then drop the cal BP parentheses
    parts = [p.strip() for p in r95.split(';')]
    keep = []
    for p in parts:
        m = re.search(r'([\d.]+) %\)\s*$', p)
        if m and float(m.group(1)) < 3: continue
        keep.append(strip_bp(p))
    r95 = '; '.join(keep)
    auth = re.sub(r'\s*--.*$', '', auth)
    auth = re.sub(r'^0-0.*$', 'not given', auth)
    auth = re.sub(r'^(\d+)-(\d+) \((.*)\)$', r'\3', auth)
    hl = ' class="hl"' if lab in ('UBA-24875', 'Ta-3021', 'UBA-30330') else ''
    out.append(f'<tr{hl}><td>{html.escape(lab)}</td><td class="num">{html.escape(bp)}</td><td class="num">{depth}</td><td>{html.escape(bed)}</td><td>{html.escape(r95)}</td><td class="num">{html.escape(med)}</td><td class="num">{html.escape(p1)}</td><td>{html.escape(auth)}</td></tr>')
out.append('</tbody></table></div>')
(frag / 'table_c14.html').write_text('\n'.join(out), encoding='utf-8')
print('written', (frag/'table_routes.html').stat().st_size, (frag/'table_c14.html').stat().st_size)
