"""Everything, for every wing: three constructions, STLs, previews, plate files."""
import sys, os, re, csv, json, time
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import trimesh
import matplotlib; matplotlib.use('Agg')
import matplotlib.pyplot as plt
from shapely.geometry import Point
import shellkit as S, build as B, tagkit as tk

OUT = os.path.abspath('../v5')
slug = lambda n: re.sub(r'[^a-z0-9]+', '-', n.lower()).strip('-')
FIELD, INK = 2, 1
YELLOW, GREY, PAPER = '#E8B923', '#4A5257', '#F4F6F7'


def weld(*ms):
    """Boolean union, not concatenate -- concatenate leaves the seam unwelded and
    the result fails the watertight check in every slicer that bothers to look."""
    return trimesh.boolean.union(list(ms), engine='manifold')


def preview(path, name, number):
    out = tk.rrect(S.S.size, S.S.size, S.S.corner_r)
    art = S.artwork(name, number).intersection(out)
    hl = Point(-S.S.hole_off, -S.S.hole_off).buffer(S.S.hole_d / 2, quad_segs=32)
    body = out.difference(hl)
    fig, ax = plt.subplots(figsize=(2.6, 2.6))
    fig.patch.set_facecolor(PAPER)
    for g in (body.geoms if hasattr(body, 'geoms') else [body]):
        ax.fill(*g.exterior.xy, color=YELLOW, zorder=1)
        for i in g.interiors:
            ax.fill(*i.xy, color=PAPER, zorder=2)
    a2 = art.difference(hl)
    for g in (a2.geoms if hasattr(a2, 'geoms') else [a2]):
        ax.fill(*g.exterior.xy, color=GREY, zorder=3)
        for i in g.interiors:
            ax.fill(*i.xy, color=YELLOW, zorder=4)
    ax.set_xlim(-18.6, 18.6); ax.set_ylim(-18.6, 18.6)
    ax.set_aspect('equal'); ax.axis('off')
    fig.subplots_adjust(0, 0, 1, 1)
    fig.savefig(path, dpi=108, facecolor=PAPER)
    plt.close(fig)


rows = [r for r in csv.DictReader(open('wings.csv')) if r['name'].strip()]
for d in ('print-all', 'previews'):
    os.makedirs(os.path.join(OUT, d), exist_ok=True)

one_all, glue_all, shell_all, ins_all, index = [], [], [], [], []
t0 = time.time()
for i, r in enumerate(rows, 1):
    nm, num, sl = r['name'], r['number'], slug(r['name'])
    d = os.path.join(OUT, 'tags', sl)
    os.makedirs(d, exist_ok=True)

    of, oi = S.build_one_piece(nm, num)
    B.write_3mf(os.path.join(d, f'{sl}-one-piece.3mf'),
                [(f'{nm} - one piece', [('artwork', oi, INK), ('face', of, FIELD)], (128., 128.))],
                f'{nm} gear tag (one piece)', nm)
    weld(of, oi).export(os.path.join(d, f'{sl}-one-piece.stl'))
    one_all.append((nm, [('artwork', oi, INK), ('face', of, FIELD)]))

    af, ab = S.build_glue_half(nm, num, 'left')
    bf, bb = S.build_glue_half(nm, num, 'right')
    B.write_3mf(os.path.join(d, f'{sl}-glue-pair.3mf'),
                [(f'{nm} - front half', [('artwork', ab, INK), ('face', af, FIELD)], (108., 128.)),
                 (f'{nm} - back half',  [('artwork', bb, INK), ('face', bf, FIELD)], (150., 128.))],
                f'{nm} gear tag (glued halves)', nm)
    weld(af, ab).export(os.path.join(d, f'{sl}-glue-front-half.stl'))
    weld(bf, bb).export(os.path.join(d, f'{sl}-glue-back-half.stl'))
    glue_all.append((f'{nm} front', [('artwork', ab, INK), ('face', af, FIELD)]))
    glue_all.append((f'{nm} back',  [('artwork', bb, INK), ('face', bf, FIELD)]))

    sf, si = S.build_shell(nm, num)
    inf, inb = S.build_insert(nm, num)
    B.write_3mf(os.path.join(d, f'{sl}-snap.3mf'),
                [(f'{nm} - tray', [('artwork', si, INK), ('face', sf, FIELD)], (108., 128.)),
                 (f'{nm} - insert', [('artwork', inb, INK), ('face', inf, FIELD)], (150., 128.))],
                f'{nm} gear tag (snap together)', nm)
    weld(sf, si).export(os.path.join(d, f'{sl}-snap-tray.stl'))
    weld(inf, inb).export(os.path.join(d, f'{sl}-snap-insert.stl'))
    shell_all.append((f'{nm} tray', [('artwork', si, INK), ('face', sf, FIELD)]))
    ins_all.append((f'{nm} insert', [('artwork', inb, INK), ('face', inf, FIELD)]))

    preview(os.path.join(OUT, 'previews', f'{sl}.png'), nm, num)
    index.append({**{k: r[k] for k in r}, 'slug': sl, 'dir': f'tags/{sl}'})
    if i % 10 == 0:
        print(f'  {i}/{len(rows)}  {time.time()-t0:.0f}s', flush=True)

per_plate, slots = B.plate_slots()


def lay(groups):
    plates = []
    for k in range(0, len(groups), per_plate):
        chunk = groups[k:k + per_plate]
        plates.append([(lab, parts, slots[j]) for j, (lab, parts) in enumerate(chunk)])
    return plates


B.write_plates(os.path.join(OUT, 'print-all', 'all-49-one-piece.3mf'),
               lay(one_all), 'Verdant Ride gear tags - one piece, all 49')
B.write_plates(os.path.join(OUT, 'print-all', 'all-49-glue-halves.3mf'),
               lay(glue_all), 'Verdant Ride gear tags - glued halves, all 49')
B.write_plates(os.path.join(OUT, 'print-all', 'all-49-snap.3mf'),
               lay(shell_all) + lay(ins_all),
               'Verdant Ride gear tags - snap together, all 49')

json.dump(index, open(os.path.join(OUT, 'tags.json'), 'w'), indent=1)
print(f'\n{len(rows)} wings in {time.time()-t0:.0f}s')
print(f'plates: one {len(lay(one_all))}  glue {len(lay(glue_all))}  '
      f'snap {len(lay(shell_all))}+{len(lay(ins_all))}')
tot = sum(os.path.getsize(os.path.join(rt, f))
          for rt, _, fs in os.walk(OUT) for f in fs)
print(f'  total {tot/1e6:.1f} MB')
