"""Counterexample showing that a surface switch on the edge between a
depth-d face F and a depth-(d-1) face F' can create a new face of depth
d (not d-1) when the depth-0 neighbor of F' lies on only one side of
the shared edge.

14-vertex maximal outerplanar L_k. Outer cycle order:
   u, p1, p2, p3, x, q1, v, b1, b2, b3, w, a1, a2, a3 -> u

Central triangle F = (u, v, w) has depth 2.
F' = (u, v, x) has depth 1 (its depth-0 neighbor is the q1-ear on the
v-side of x; its u-side neighbor A_ux is depth 1).
A_uw = (u, a2, w), A_vw = (v, b2, w) are both depth 1.

Surface switch on uv: flip uv -> wx. New faces are
   A = (u, w, x) and B = (v, w, x).
B inherits the depth-0 q1-ear, so depth(B) = 1.
A's neighbors are A_uw (1), A_ux (1), B (1), so depth(A) = 2 = d. BAD.
"""
import os
import math
import networkx as nx
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon

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

OUTER = ['u', 'p1', 'p2', 'p3', 'x', 'q1', 'v',
         'b1', 'b2', 'b3', 'w', 'a1', 'a2', 'a3']
n = len(OUTER)

POS = {}
for i, name in enumerate(OUTER):
    a = math.radians(90 - i * (360 / n))
    POS[name] = (math.cos(a), math.sin(a))

OUTER_EDGES = [(OUTER[i], OUTER[(i + 1) % n]) for i in range(n)]

CHORDS_BEFORE = [
    ('u', 'v'), ('u', 'w'), ('v', 'w'),       # central F = (u,v,w)
    ('u', 'x'), ('v', 'x'),                    # F' = (u,v,x)
    ('u', 'a2'), ('a2', 'w'),                  # A_uw = (u,a2,w)
    ('v', 'b2'), ('b2', 'w'),                  # A_vw = (v,b2,w)
    ('u', 'p2'), ('p2', 'x'),                  # A_ux = (u,p2,x)
]

CHORDS_AFTER = [c for c in CHORDS_BEFORE if set(c) != {'u', 'v'}] + [('w', 'x')]

FACES_BEFORE = [
    ('u', 'v', 'w'),       # F        (depth 2 -- bad)
    ('u', 'v', 'x'),       # F'       (depth 1)
    ('u', 'a2', 'w'),      # A_uw     (depth 1)
    ('v', 'b2', 'w'),      # A_vw     (depth 1)
    ('u', 'p2', 'x'),      # A_ux     (depth 1)
    ('v', 'q1', 'x'),      # A_vx     (depth 0)
    ('u', 'a1', 'a2'),     # depth 0
    ('a2', 'a3', 'w'),     # depth 0
    ('v', 'b1', 'b2'),     # depth 0
    ('b2', 'b3', 'w'),     # depth 0
    ('u', 'p1', 'p2'),     # depth 0
    ('p2', 'p3', 'x'),     # depth 0
]

FACES_AFTER = [
    ('u', 'w', 'x'),       # A        (depth 2 -- still bad!)
    ('v', 'w', 'x'),       # B        (depth 1)
    ('u', 'a2', 'w'),
    ('v', 'b2', 'w'),
    ('u', 'p2', 'x'),
    ('v', 'q1', 'x'),
    ('u', 'a1', 'a2'),
    ('a2', 'a3', 'w'),
    ('v', 'b1', 'b2'),
    ('b2', 'b3', 'w'),
    ('u', 'p1', 'p2'),
    ('p2', 'p3', 'x'),
]


def compute_depths(faces, chords):
    """Compute facial depth for each face using threshold-1 definition."""
    outer_set = {frozenset(e) for e in OUTER_EDGES}

    def face_edges(f):
        return {frozenset((f[0], f[1])), frozenset((f[1], f[2])),
                frozenset((f[0], f[2]))}

    # Build inner-face dual
    D = nx.Graph()
    D.add_nodes_from(range(len(faces)))
    for i, fi in enumerate(faces):
        for j, fj in enumerate(faces):
            if i < j and face_edges(fi) & face_edges(fj):
                D.add_edge(i, j)

    B = [i for i, f in enumerate(faces)
         if len(face_edges(f) & outer_set) >= 1]

    depth = {}
    for i in range(len(faces)):
        if not B:
            depth[i] = float('inf')
            continue
        depth[i] = min(nx.shortest_path_length(D, i, b) for b in B)
    return depth


def draw_panel(ax, faces, chords, depth, title, highlight_edge=None,
               highlight_face=None):
    palette = {0: '#86efac', 1: '#fde68a', 2: '#fca5a5'}
    edge_pal = {0: '#16a34a', 1: '#d97706', 2: '#dc2626'}

    for i, f in enumerate(faces):
        d = depth[i]
        face_color = palette.get(d, '#ddd')
        face_edge = edge_pal.get(d, '#333')
        lw = 1.4
        if highlight_face is not None and i == highlight_face:
            face_edge = '#7c2d12'
            lw = 3.0
        poly = Polygon([POS[v] for v in f], closed=True,
                       facecolor=face_color, edgecolor=face_edge,
                       linewidth=lw, alpha=0.7, zorder=0)
        ax.add_patch(poly)
        cx = sum(POS[v][0] for v in f) / 3
        cy = sum(POS[v][1] for v in f) / 3
        ax.text(cx, cy, str(d), ha='center', va='center', fontsize=11,
                color=edge_pal.get(d, '#333'), fontweight='bold')

    # Draw edges
    all_edges = OUTER_EDGES + list(chords)
    for (a, b) in all_edges:
        color = '#333'; lw = 1.2
        if highlight_edge is not None and {a, b} == set(highlight_edge):
            color = '#dc2626'; lw = 3.5
        elif (a, b) == ('w', 'x') or (a, b) == ('x', 'w'):
            color = '#16a34a'; lw = 3.0
        ax.plot([POS[a][0], POS[b][0]], [POS[a][1], POS[b][1]],
                color=color, linewidth=lw, zorder=1)

    # Draw vertices
    for name, (x, y) in POS.items():
        ax.scatter([x], [y], s=260, c='#1f2937', edgecolors='black',
                   linewidths=0.8, zorder=2)
        ax.text(x, y, name, ha='center', va='center',
                fontsize=8, color='white', fontweight='bold', zorder=3)

    ax.set_aspect('equal'); ax.axis('off')
    ax.set_xlim(-1.35, 1.35); ax.set_ylim(-1.35, 1.35)
    ax.set_title(title, fontsize=12)


def main():
    depth_before = compute_depths(FACES_BEFORE, CHORDS_BEFORE)
    depth_after = compute_depths(FACES_AFTER, CHORDS_AFTER)

    print("BEFORE:")
    for i, f in enumerate(FACES_BEFORE):
        print(f"  {f} -> depth {depth_before[i]}")
    print("AFTER:")
    for i, f in enumerate(FACES_AFTER):
        print(f"  {f} -> depth {depth_after[i]}")

    fig, axes = plt.subplots(1, 2, figsize=(15, 7.5))
    draw_panel(axes[0], FACES_BEFORE, CHORDS_BEFORE, depth_before,
               r'Before: $F=(u,v,w)$ depth 2, $F\'=(u,v,x)$ depth 1',
               highlight_edge=('u', 'v'), highlight_face=0)
    draw_panel(axes[1], FACES_AFTER, CHORDS_AFTER, depth_after,
               r'After surface switch on $uv$: $A=(u,w,x)$ still depth 2!',
               highlight_face=0)

    fig.tight_layout()
    out = os.path.join(OUT_DIR, 'fig_counterexample_surface_switch.png')
    fig.savefig(out, dpi=180, bbox_inches='tight')
    plt.close(fig)
    print(f'wrote {out}')


if __name__ == '__main__':
    main()
