"""Generate a multi-page PDF showing the L_k state after each edge
switch, on the n=40 stuck example."""
import os
import sys
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import math
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon
from matplotlib.backends.backend_pdf import PdfPages
from leaf_distance_algorithm import compute_x
from stress_test_termination import (
    compute_depths, apply_switch, check_balanced, face_edges,
    random_triangulation
)

OUT_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), os.pardir)


def neighbour_at_edge(faces, F_idx, e):
    for j, fj in enumerate(faces):
        if j != F_idx and e in face_edges(fj):
            return j
    return None


def positions(n):
    return {i: (math.cos(math.radians(90 - i * 360 / n)),
                math.sin(math.radians(90 - i * 360 / n)))
            for i in range(n)}


def draw_state(ax, faces, n, outer_set, switched_edge=None,
               action=None, step=None):
    POS = positions(n)
    depth = compute_depths(faces, outer_set)
    palette = {0: '#86efac', 1: '#fde68a', 2: '#fca5a5'}
    edge_pal = {0: '#16a34a', 1: '#d97706', 2: '#dc2626'}
    for f in faces:
        d = depth.get(faces.index(f), 0)
        # Find depth by iterating
        for i, ff in enumerate(faces):
            if ff == f:
                d = depth[i]
                break
        poly = Polygon([POS[v] for v in f], closed=True,
                       facecolor=palette.get(d, '#ddd'),
                       edgecolor=edge_pal.get(d, '#333'),
                       linewidth=0.7, alpha=0.6, zorder=0)
        ax.add_patch(poly)
    # Draw all edges
    all_edges = set()
    for f in faces:
        all_edges.update(face_edges(f))
    outer_edges = [tuple(e) for e in all_edges if e in outer_set]
    chord_edges = [tuple(e) for e in all_edges if e not in outer_set]
    for (a, b) in outer_edges + chord_edges:
        color = '#333'; lw = 0.5
        if switched_edge and {a, b} == set(switched_edge):
            color = '#dc2626' if action == 'preprocess' else '#16a34a'
            lw = 2.5
        ax.plot([POS[a][0], POS[b][0]], [POS[a][1], POS[b][1]],
                color=color, linewidth=lw, zorder=1)
    # Vertices
    for i, (x, y) in POS.items():
        ax.scatter([x], [y], s=70, c='#1f2937', edgecolors='black',
                   linewidths=0.3, zorder=2)
        ax.text(x, y, str(i), ha='center', va='center',
                fontsize=5, color='white', fontweight='bold', zorder=3)
    ax.set_aspect('equal'); ax.axis('off')
    ax.set_xlim(-1.2, 1.2); ax.set_ylim(-1.2, 1.2)
    d_max = max(depth.values())
    n_d1 = sum(1 for d in depth.values() if d == 1)
    n_d2 = sum(1 for d in depth.values() if d == 2)
    title = f'step {step}'
    if action and switched_edge:
        title += f': {action} on {tuple(sorted(switched_edge))}'
    title += f'  (max={d_max}, #d1={n_d1}, #d2={n_d2})'
    ax.set_title(title, fontsize=9)


def run_and_record(faces, outer_set, n, max_steps=80):
    """Run algorithm, capturing the state and the action at each step.
    Returns list of (faces_at_start_of_step, action, edge)."""
    records = []
    for step in range(max_steps):
        depth = compute_depths(faces, outer_set)
        d_max = max(depth.values())
        if d_max == 0:
            records.append((list(faces), None, None))
            return records, faces

        max_d_faces = [i for i, d in depth.items() if d == d_max]
        F_idx = max_d_faces[0]
        F = faces[F_idx]
        ok, _, fp_idx, e = check_balanced(F_idx, faces, depth, outer_set)
        if ok:
            action = 'BALANCED'
        else:
            x_vals, _ = compute_x(faces, outer_set)
            nb_choices = []
            for e_test in face_edges(F):
                if e_test in outer_set:
                    continue
                nb_idx = neighbour_at_edge(faces, F_idx, e_test)
                if nb_idx is None:
                    continue
                nb_choices.append((x_vals[nb_idx], e_test, nb_idx))
            if not nb_choices:
                records.append((list(faces), 'STUCK', None))
                return records, faces
            nb_choices.sort()
            _, e, fp_idx = nb_choices[0]
            action = 'preprocess'

        Fp = faces[fp_idx]
        u, v = tuple(e)
        w = [vert for vert in F if vert not in (u, v)][0]
        x = [vert for vert in Fp if vert not in (u, v)][0]
        records.append((list(faces), action, (u, v)))
        faces = apply_switch(faces, (u, v), (w, x))
    return records, faces


seed = 94476710
outer, _, faces = random_triangulation(40, seed=seed)
outer_set = {frozenset(e) for e in outer}

print('recording trajectory...')
records, _ = run_and_record(faces, outer_set, 40, max_steps=80)
print(f'recorded {len(records)} steps')

out_pdf = os.path.join(OUT_DIR, 'fig_n40_every_step.pdf')
with PdfPages(out_pdf) as pdf:
    for step, (state_faces, action, edge) in enumerate(records):
        fig, ax = plt.subplots(figsize=(7, 7))
        draw_state(ax, state_faces, 40, outer_set,
                   switched_edge=edge, action=action, step=step)
        pdf.savefig(fig, dpi=120, bbox_inches='tight')
        plt.close(fig)
print(f'wrote {out_pdf}')
