"""Instruction sheets for all three constructions, one consistent style."""
import sys, os, textwrap, numpy as np
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import matplotlib; matplotlib.use('Agg')
import matplotlib.pyplot as plt
import trimesh
import shellkit as S, sheet as SH, isodraw as iso

NAME, NUM = 'Grubb 150', '150'
P = S.S
F, I = SH.FIELD, SH.INK

of, oi   = S.build_one_piece(NAME, NUM)
gaf, gab = S.build_glue_half(NAME, NUM, 'left')
gbf, gbb = S.build_glue_half(NAME, NUM, 'right')
sf, si   = S.build_shell(NAME, NUM)
inf, inb = S.build_insert(NAME, NUM)


def flipped(*ms):
    """Turn a tag over the way a hand does -- about the VERTICAL axis.

    Rotating about X instead flips it top-to-bottom, which leaves the back
    artwork mirrored on screen and makes the tag look like it reads backwards.
    """
    out = []
    for m in ms:
        c = m.copy()
        c.apply_transform(trimesh.transformations.rotation_matrix(np.pi, [0, 1, 0]))
        c.apply_translation([0, 0, -c.bounds[0][2]])
        out.append(c)
    return out


gbf2, gbb2 = flipped(gbf, gbb)
off2, oi2  = flipped(of, oi)

FIG_W, FIG_H = 16.4, 6.4
PANEL_H = 0.63


def make_sheet(path, title, subtitle, steps, note):
    """steps: [(draw(ax, ext), heading, body)] where ext(halfx) sizes the view."""
    fig = plt.figure(figsize=(FIG_W, FIG_H))
    fig.patch.set_facecolor(SH.BG)
    SH.background(fig)
    n = len(steps)
    w = 0.94 / n
    ar = ((w - 0.016) * FIG_W) / (PANEL_H * FIG_H)   # panel aspect, w/h

    def ext(halfx, cx=0.0, cy=0.0):
        hy = halfx / ar
        return (cx - halfx, cx + halfx, cy - hy, cy + hy)

    for k, (draw, head, body) in enumerate(steps):
        L = 0.03 + k * w
        ax = SH.panel(fig, L + 0.008, w - 0.016, bottom=0.185, height=PANEL_H)
        draw(ax, ext)
        ax.set_aspect('equal')
        fig.text(L + 0.020, 0.845, str(k + 1), color='#0F1A21', fontsize=14.5,
                 fontweight='bold', ha='center', va='center', zorder=12,
                 bbox=dict(boxstyle='circle,pad=0.40', fc=SH.ACC, ec='none'))
        fig.text(L + 0.040, 0.845, head, color=SH.TXT, fontsize=16.5,
                 fontweight='bold', ha='left', va='center', zorder=12)
        fs = 10.6 if n <= 3 else 9.9
        chars = int(((w - 0.020) * FIG_W) / (fs * 0.0083))
        wrapped = '\n'.join(textwrap.wrap(' '.join(body.split()), chars))
        fig.text(L + 0.008, 0.155, wrapped, color=SH.DIM, fontsize=fs, ha='left',
                 va='top', linespacing=1.6, zorder=12)
        if k:
            SH.divider(fig, L - 0.004)
    fig.text(0.03, 0.955, title, color=SH.TXT, fontsize=21, fontweight='bold',
             ha='left', va='top')
    fig.text(0.03, 0.905, subtitle, color=SH.ACC, fontsize=11.5, ha='left', va='top')
    fig.text(0.97, 0.038, note, color=SH.DIM, fontsize=10, ha='right', va='bottom')
    plt.savefig(path, dpi=140, facecolor=SH.BG)
    plt.close(fig)
    print('  ', os.path.basename(path))


SEP = (15.0, -12.0)


def label(ax, offset, text, y):
    """Captions share one baseline at the foot of the panel; hanging them under
    each part put the back one straight behind the front part."""
    p, _ = iso.project(np.array([float(offset[0]), float(offset[1]), 0.0]),
                       SH.AZ, SH.EL)
    ax.text(p[0], y, text, color=SH.DIM, fontsize=10.5, ha='center',
            va='bottom', zorder=12)


def pair(ax, ext, a, b, halfx=37.0, names=None):
    """Two parts staggered on the diagonal, back one up-left."""
    oa = (-SEP[0], -SEP[1], 0)
    ob = (SEP[0], SEP[1], 0)
    sc = SH.Scene()
    sc.add(a, offset=oa)
    sc.add(b, offset=ob)
    SH.render(ax, sc, extent=ext(halfx))
    if names:
        e = ext(halfx)
        label(ax, oa, names[0], e[2] + 2.0)
        label(ax, ob, names[1], e[2] + 2.0)


def solo(ax, ext, a, halfx=26.0):
    SH.render(ax, SH.Scene().add(a), extent=ext(halfx))


def stack(ax, ext, base, top, lift=13.0, halfx=26.0, marks=None):
    sc = SH.Scene()
    sc.add(base)
    sc.add(top, offset=(0, 0, lift), shade=False)
    SH.render(ax, sc, extent=ext(halfx))
    a, _ = iso.project(np.array([0., 0., lift - 0.8]), SH.AZ, SH.EL)
    b, _ = iso.project(np.array([0., 0., base[0][0].bounds[1][2] + 0.8]), SH.AZ, SH.EL)
    SH.arrow(ax, tuple(a), tuple(b), lw=5.0)


# ---------------------------------------------------------------- one piece
def one_a(ax, ext):
    solo(ax, ext, [(of, F), (oi, I)])

def one_b(ax, ext):
    pair(ax, ext, [(of, F), (oi, I)], [(off2, F), (oi2, I)],
         names=('front', 'back'))

def ring_view(ax, ext, meshes, thick, halfx=27.0):
    e = ext(halfx, cx=-4.5)
    sc = SH.Scene()
    for m in meshes:
        sc.add(*m)
    SH.render(ax, sc, extent=e)
    SH.keyring(ax, P, thick=thick)
    SH.tick(ax, e[1] - 5.5, e[2] + 5.5)


def one_c(ax, ext):
    ring_view(ax, ext, [([(of, F), (oi, I)],)], S.O.thick)


# ---------------------------------------------------------------- glue
ASSY = [(gbf2, F), (gbb2, I)]                       # back half, artwork down
TOPH = [(gaf, F), (gab, I)]                         # front half, artwork up


def glue_a(ax, ext):
    pair(ax, ext, [(gaf, F), (gab, I)], [(gbf, F), (gbb, I)],
         names=('front half', 'back half'))

def glue_b(ax, ext):
    """One half turned over so the glue pockets are the thing you look at."""
    solo(ax, ext, ASSY, halfx=25.0)
    for (x, y) in S.G.key_pos:
        p, _ = iso.project(np.array([float(x), float(y), S.G.half_t]), SH.AZ, SH.EL)
        ax.add_patch(plt.Circle(tuple(p), 2.6, facecolor='none', edgecolor=SH.ACC,
                                lw=2.2, zorder=8))

def glue_c(ax, ext):
    stack(ax, ext, ASSY, TOPH, lift=12.0, halfx=25.0)

def glue_d(ax, ext):
    ring_view(ax, ext, [(ASSY,), (TOPH, (0, 0, S.G.half_t), False)],
              S.G.half_t * 2)


# ---------------------------------------------------------------- snap
def snap_a(ax, ext):
    pair(ax, ext, [(sf, F), (si, I)], [(inf, F), (inb, I)],
         names=('tray', 'insert'))

def snap_b(ax, ext):
    stack(ax, ext, [(sf, F), (si, I)], [(inf, F), (inb, I)], lift=12.0)

def snap_c(ax, ext):
    ring_view(ax, ext, [([(sf, F), (si, I)],),
                        ([(inf, F), (inb, I)], (0, 0, P.floor), False)], P.height)


OUT = '/home/claude/build/sheets'
os.makedirs(OUT, exist_ok=True)
print('rendering:')
make_sheet(f'{OUT}/how-to-one-piece.png',
           'One piece — nothing to assemble',
           'Print it. That is the whole build.',
           [(one_a, 'Print', 'One part, one plate, two filaments. The artwork goes\ninto both faces as it prints. No supports.'),
            (one_b, 'Both faces read', 'Front and back are the same tag. Nothing to glue,\nnothing to press in, no second part to lose.'),
            (one_c, 'Ring it', 'Straight onto the bag. 3.2 mm thick and solid all\nthe way through — the toughest of the three.')],
           '35 × 35 × 3.2 mm  ·  two filaments  ·  verdantride.com')

make_sheet(f'{OUT}/how-to-glue.png',
           'Two halves — glued',
           'Fewest colour changes. Two parts, one bead of hot glue.',
           [(glue_a, 'Print', 'Two halves, one plate. Same colour-change height,\nso they print together happily.'),
            (glue_b, 'Glue', 'Turn one half over. Five shallow pockets in the flat\nback — a dab of hot glue in each.'),
            (glue_c, 'Press', 'Backs touching, artwork facing out. Line the outside\nedges up by feel before the glue sets.'),
            (glue_d, 'Ring it', 'The holes line up because the back half carries its\nhole in the opposite corner.')],
           '35 × 35 × 3.2 mm assembled  ·  two filaments  ·  verdantride.com')

make_sheet(f'{OUT}/how-to-snap.png',
           'Tray and insert — snap together',
           'No glue at all. The keyring is what holds it shut.',
           [(snap_a, 'Print', 'A tray and an insert. Give them separate plates —\nsame-colour layers print far better together.'),
            (snap_b, 'Press it in', 'Artwork up. The lead-in chamfer starts it, then it\npresses down until it bottoms out on the floor.'),
            (snap_c, 'Ring it', 'The ring threads both parts at once. It cannot push\nin any further and it cannot lift back out.')],
           '35 × 35 × 2.4 mm  ·  0.20 mm press fit  ·  verdantride.com')
