"""Noah's Flow, pours step (human): describe the zones Noah drew in KiCad, exactly as saved, so
`verify-pours --plan <this> --adopt --why "poured by the human"` can adopt them as the step's proof.

Nothing here designs a pour: every value is read from his saved board (net, layers, outline, priority,
pad connection and clearance, minimum width, thermal relief, island removal, the zone's own UUID).

    python3 aiflow/design/human_pour_plan.py <board.kicad_pcb> <out.json>
"""
import json, re, sys


def parse(text):
    tok = re.findall(r'"(?:[^"\\]|\\.)*"|\(|\)|[^\s()]+', text)
    stack, cur = [], []
    for t in tok:
        if t == '(':
            stack.append(cur); cur = []
        elif t == ')':
            done = cur; cur = stack.pop(); cur.append(done)
        else:
            cur.append(t[1:-1] if t.startswith('"') else t)
    return cur[0]


def kids(n, tag):
    return [c for c in n if isinstance(c, list) and c and c[0] == tag]


def kid(n, tag):
    k = kids(n, tag)
    return k[0] if k else None


def num(x):
    v = float(x)
    return int(v) if v == int(v) and 'e' not in x.lower() and '.' not in x else v


board = parse(open(sys.argv[1], encoding='utf-8').read())
nets = {c[1]: c[2] for c in kids(board, 'net') if len(c) > 2}
zones = []
for z in kids(board, 'zone'):
    layers = (kid(z, 'layer') or kid(z, 'layers'))[1:]
    loops = []
    for p in kids(z, 'polygon'):
        pts = kid(p, 'pts')
        if any(c[0] != 'xy' for c in pts[1:]):
            sys.exit('zone polygon has an arc: describe it by hand')
        loops.append([[num(c[1]), num(c[2])] for c in pts[1:]])
    e = {'name': (kid(z, 'name') or [None, ''])[1] or None, 'layers': layers, 'boardZoneUuid': kid(z, 'uuid')[1]}
    if len(loops) == 1:
        e['polygon'] = loops[0]
    else:
        e['polygons'] = loops
    ko = kid(z, 'keepout')
    if ko:
        e['type'] = 'keepout'
        word = {'tracks': 'tracks', 'vias': 'vias', 'pads': 'pads', 'copperpour': 'copperPour', 'footprints': 'footprints'}
        e['keepout'] = {word[c[0]]: c[1] == 'not_allowed' for c in ko[1:] if isinstance(c, list) and c[0] in word}
    else:
        net = kid(z, 'net')
        e['net'] = (kid(z, 'net_name') or [None, None])[1] or nets.get(net[1], net[1])
        pr = kid(z, 'priority')
        if pr:
            e['priority'] = int(pr[1])
        cp = kid(z, 'connect_pads')
        mode = cp[1] if cp and len(cp) > 1 and not isinstance(cp[1], list) else None
        e['connection'] = {None: 'thermal', 'yes': 'solid', 'no': 'none'}.get(mode, mode)
        cl = kid(cp, 'clearance') if cp else None
        if cl:
            e['clearance'] = float(cl[1])
        mt = kid(z, 'min_thickness')
        if mt:
            e['minThickness'] = float(mt[1])
        fill = kid(z, 'fill')
        if fill:
            for k, key in (('thermal_gap', 'thermalGap'), ('thermal_bridge_width', 'thermalBridge')):
                if kid(fill, k):
                    e[key] = float(kid(fill, k)[1])
            irm = kid(fill, 'island_removal_mode')
            if irm:
                e['islandRemoval'] = {'0': 'always', '1': 'never', '2': 'area'}[irm[1]]
                if irm[1] == '2':
                    e['islandAreaMin'] = float(kid(fill, 'island_area_min')[1])
            if kid(fill, 'mode') and kid(fill, 'mode')[1] != 'solid':
                sys.exit('hatched zone: the plan cannot describe it')
    if e['name'] is None:
        del e['name']
    zones.append(e)

plan = {'source': 'zones drawn by Noah in KiCad (hands-on pours step), read from his saved board %s' % sys.argv[1],
        'zones': zones}
json.dump(plan, open(sys.argv[2], 'w'), indent=1)
for e in zones:
    print('%-10s %-12s prio %s %s' % (e.get('net', 'keepout'), '+'.join(e['layers']), e.get('priority', 0),
                                      e.get('connection', e.get('type'))))
print(len(zones), 'zones ->', sys.argv[2])
