"""Two-panel illustration of Theorem (cubic contraction across a 4-face).

Left:  H near the 4-face, with the forced 3-edge-colouring
       e_0=a, e_1=b, e_2=e_3=c, w_0=w_1=b, w_2=w_3=a.
Right: H' after cubic-graph edge contraction on e_0, with the new colouring
       (e_2', e_3' both b; e_1 recoloured to c; everything else unchanged).

Produces fig_thm_cubic_contraction_4face.png.
"""
import os
import matplotlib.pyplot as plt

OUT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

DARK = '#374151'
GRAY = '#9ca3af'
GHOST = '#fca5a5'
DEG2 = '#f59e0b'

# Colour code: a=orange-ish, b=blue, c=green. Chosen colourblind-friendly.
COL_A = '#ea580c'   # 'a'
COL_B = '#2563eb'   # 'b'
COL_C = '#16a34a'   # 'c'

# Positions of the 4-face vertices and their external neighbours.
pos = {
    'v0': (0.0, 0.0),
    'v1': (2.4, 0.0),
    'v2': (2.4, 2.4),
    'v3': (0.0, 2.4),
    'u0': (-1.5, -0.8),
    'u1': ( 3.9, -0.8),
    'u2': ( 3.9,  3.2),
    'u3': (-1.5,  3.2),
}


def draw_edge(ax, p, q, color, lw=2.4, ls='-', zorder=2):
    ax.plot([p[0], q[0]], [p[1], q[1]], color=color, lw=lw, ls=ls,
            zorder=zorder, solid_capstyle='round')


def draw_vertex(ax, p, color=DARK, size=70, zorder=4):
    ax.scatter([p[0]], [p[1]], s=size, color=color, zorder=zorder)


def label_vertex(ax, p, text, offset=(0.0, 0.28), fontsize=12, color=DARK):
    ax.text(p[0] + offset[0], p[1] + offset[1], text,
            ha='center', va='center', fontsize=fontsize, color=color,
            zorder=5)


def label_edge(ax, p, q, text, offset=(0.0, 0.0), color=DARK, fontsize=11):
    mid = ((p[0] + q[0]) / 2 + offset[0], (p[1] + q[1]) / 2 + offset[1])
    ax.text(mid[0], mid[1], text, ha='center', va='center',
            fontsize=fontsize, color=color, zorder=5,
            bbox=dict(boxstyle='round,pad=0.15', facecolor='white',
                      edgecolor='none', alpha=0.85))


def shade_face(ax, vs, color='#fef9c3', alpha=0.7):
    xs = [p[0] for p in vs] + [vs[0][0]]
    ys = [p[1] for p in vs] + [vs[0][1]]
    ax.fill(xs, ys, color=color, alpha=alpha, zorder=1)


def panel_before(ax):
    # 4-face shading
    shade_face(ax, [pos['v0'], pos['v1'], pos['v2'], pos['v3']])
    # Face label
    ax.text(1.2, 1.2, '$f$', ha='center', va='center', fontsize=14,
            color=GRAY, style='italic', zorder=2)

    # Face edges
    draw_edge(ax, pos['v0'], pos['v1'], COL_A)   # e_0 = a
    draw_edge(ax, pos['v2'], pos['v3'], COL_B)   # e_1 = b
    draw_edge(ax, pos['v1'], pos['v2'], COL_C)   # e_2 = c
    draw_edge(ax, pos['v3'], pos['v0'], COL_C)   # e_3 = c
    # External edges
    draw_edge(ax, pos['v0'], pos['u0'], COL_B)   # w_0 = b
    draw_edge(ax, pos['v1'], pos['u1'], COL_B)   # w_1 = b
    draw_edge(ax, pos['v2'], pos['u2'], COL_A)   # w_2 = a
    draw_edge(ax, pos['v3'], pos['u3'], COL_A)   # w_3 = a
    # Vertices
    for v in ('v0', 'v1', 'v2', 'v3'):
        draw_vertex(ax, pos[v], DARK, size=90)
    for u in ('u0', 'u1', 'u2', 'u3'):
        draw_vertex(ax, pos[u], DARK, size=60)

    # Labels
    label_vertex(ax, pos['v0'], '$v_0$', offset=(-0.20, -0.25))
    label_vertex(ax, pos['v1'], '$v_1$', offset=( 0.20, -0.25))
    label_vertex(ax, pos['v2'], '$v_2$', offset=( 0.20,  0.25))
    label_vertex(ax, pos['v3'], '$v_3$', offset=(-0.20,  0.25))
    label_vertex(ax, pos['u0'], '$u_0$', offset=(-0.25,  0.00))
    label_vertex(ax, pos['u1'], '$u_1$', offset=( 0.25,  0.00))
    label_vertex(ax, pos['u2'], '$u_2$', offset=( 0.25,  0.00))
    label_vertex(ax, pos['u3'], '$u_3$', offset=(-0.25,  0.00))

    # Edge labels with colour
    label_edge(ax, pos['v0'], pos['v1'], r'$e_0\!=\!a$', offset=(0, -0.18),
               color=COL_A)
    label_edge(ax, pos['v2'], pos['v3'], r'$e_1\!=\!b$', offset=(0,  0.18),
               color=COL_B)
    label_edge(ax, pos['v1'], pos['v2'], r'$e_2\!=\!c$', offset=(0.30, 0.0),
               color=COL_C)
    label_edge(ax, pos['v3'], pos['v0'], r'$e_3\!=\!c$', offset=(-0.30, 0.0),
               color=COL_C)
    label_edge(ax, pos['v0'], pos['u0'], r'$w_0\!=\!b$',
               offset=(-0.05, -0.05), color=COL_B, fontsize=10)
    label_edge(ax, pos['v1'], pos['u1'], r'$w_1\!=\!b$',
               offset=( 0.05, -0.05), color=COL_B, fontsize=10)
    label_edge(ax, pos['v2'], pos['u2'], r'$w_2\!=\!a$',
               offset=( 0.05,  0.05), color=COL_A, fontsize=10)
    label_edge(ax, pos['v3'], pos['u3'], r'$w_3\!=\!a$',
               offset=(-0.05,  0.05), color=COL_A, fontsize=10)

    ax.set_title('$H$ with proper $3$-edge-colouring $\\varphi$:\n'
                 '$\\varphi(e_0)=a$, $\\varphi(e_1)=b$ (opposite, different)',
                 fontsize=11, color=DARK, pad=10)


def panel_after(ax):
    # New 'face' shading: now (v_2, v_3) connected via e_1, plus new edges
    # form a hexagonal-ish region. Just shade the area lightly.
    shade_face(ax,
               [pos['u0'], pos['v3'], pos['v2'], pos['u1'],
                (pos['u1'][0] + 0.7, pos['u1'][1] - 0.7),
                (pos['u0'][0] - 0.7, pos['u0'][1] - 0.7)],
               color='#fef9c3', alpha=0.0)  # invisible, just spacing

    # Ghost the deleted edges (e_0, e_2, e_3, w_0, w_1) and former vertices
    draw_edge(ax, pos['v0'], pos['v1'], GHOST, lw=1.5, ls=':')  # e_0
    draw_edge(ax, pos['v1'], pos['v2'], GHOST, lw=1.5, ls=':')  # e_2
    draw_edge(ax, pos['v3'], pos['v0'], GHOST, lw=1.5, ls=':')  # e_3
    draw_edge(ax, pos['v0'], pos['u0'], GHOST, lw=1.5, ls=':')  # w_0
    draw_edge(ax, pos['v1'], pos['u1'], GHOST, lw=1.5, ls=':')  # w_1

    # Surviving / recoloured edges
    draw_edge(ax, pos['v2'], pos['v3'], COL_C, lw=3.0)         # e_1 recoloured to c
    draw_edge(ax, pos['v2'], pos['u2'], COL_A)                 # w_2 = a
    draw_edge(ax, pos['v3'], pos['u3'], COL_A)                 # w_3 = a

    # Smoothed-in new edges: e_2' from v_2 to u_1, e_3' from v_3 to u_0
    draw_edge(ax, pos['v2'], pos['u1'], COL_B, lw=3.0)
    draw_edge(ax, pos['v3'], pos['u0'], COL_B, lw=3.0)

    # Vertices: v_0, v_1 removed; show their former positions faintly
    for ghost in ('v0', 'v1'):
        ax.scatter([pos[ghost][0]], [pos[ghost][1]], s=120,
                   facecolors='none', edgecolors=GRAY, lw=1.0,
                   linestyles='--', zorder=3)
    for v in ('v2', 'v3'):
        draw_vertex(ax, pos[v], DARK, size=90)
    for u in ('u0', 'u1', 'u2', 'u3'):
        draw_vertex(ax, pos[u], DARK, size=60)

    # Labels
    ax.text(pos['v0'][0], pos['v0'][1] - 0.30, '$v_0$ gone',
            ha='center', va='center', fontsize=9, color=GRAY)
    ax.text(pos['v1'][0], pos['v1'][1] - 0.30, '$v_1$ gone',
            ha='center', va='center', fontsize=9, color=GRAY)

    label_vertex(ax, pos['v2'], '$v_2$', offset=( 0.20,  0.25))
    label_vertex(ax, pos['v3'], '$v_3$', offset=(-0.20,  0.25))
    label_vertex(ax, pos['u0'], '$u_0$', offset=(-0.25,  0.00))
    label_vertex(ax, pos['u1'], '$u_1$', offset=( 0.25,  0.00))
    label_vertex(ax, pos['u2'], '$u_2$', offset=( 0.25,  0.00))
    label_vertex(ax, pos['u3'], '$u_3$', offset=(-0.25,  0.00))

    # Edge labels for the new/recoloured edges
    label_edge(ax, pos['v2'], pos['v3'], r'$e_1\!=\!c$', offset=(0, 0.18),
               color=COL_C)
    label_edge(ax, pos['v2'], pos['u1'], r"$e_2'\!=\!b$",
               offset=(0.20, 0.10), color=COL_B)
    label_edge(ax, pos['v3'], pos['u0'], r"$e_3'\!=\!b$",
               offset=(-0.20, 0.10), color=COL_B)
    label_edge(ax, pos['v2'], pos['u2'], r'$w_2\!=\!a$',
               offset=( 0.05,  0.05), color=COL_A, fontsize=10)
    label_edge(ax, pos['v3'], pos['u3'], r'$w_3\!=\!a$',
               offset=(-0.05,  0.05), color=COL_A, fontsize=10)

    ax.set_title("$H'$ after cubic contraction of $e_0$:\n"
                 r"$e_2', e_3'$ get colour $b$; $e_1$ recoloured to $c$",
                 fontsize=11, color=DARK, pad=10)


def main():
    fig, axes = plt.subplots(1, 2, figsize=(13, 5.8))
    for ax in axes:
        ax.set_xlim(-2.6, 4.8)
        ax.set_ylim(-2.0, 4.2)
        ax.set_aspect('equal')
        ax.axis('off')
    panel_before(axes[0])
    panel_after(axes[1])
    plt.subplots_adjust(left=0.02, right=0.98, top=0.92, bottom=0.04,
                        wspace=0.05)
    out = os.path.join(OUT_DIR, 'fig_thm_cubic_contraction_4face.png')
    plt.savefig(out, dpi=180, bbox_inches='tight')
    print(f"wrote {out}")


if __name__ == '__main__':
    main()
