"""Apply preprocessing surface switches to the 21-vertex depth-2 example
and check whether the new depth-2 face admits a balanced surface switch.
If not, iterate."""
import sys, os
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import math
import networkx as nx
from d2_balanced_existence import (
    POS, n, OUTER_EDGES, outer_set, face_edges, compute_depths,
    check_balanced, FACES as FACES0, CHORDS as CHORDS0
)


def apply_switch(faces, chords, uv, ux_vx):
    """Apply an edge switch removing edge uv and inserting edge wx.
    `ux_vx` = (third-vertex-of-F = w, third-vertex-of-F' = x).
    Returns (new_faces, new_chords).
    Replaces the two old triangles uvw, uvx with the two new triangles
    uwx, vwx.
    """
    u, v = uv
    w, x = ux_vx
    new_chords = [c for c in chords if set(c) != {u, v}] + [tuple(sorted((w, x)))]
    new_faces = []
    for f in faces:
        if set(f) == {u, v, w} or set(f) == {u, v, x}:
            continue
        new_faces.append(f)
    new_faces.append(tuple(sorted((u, w, x))))
    new_faces.append(tuple(sorted((v, w, x))))
    return new_faces, new_chords


def find_third_vertices(faces, uv):
    """Find the two faces containing edge uv; return their third vertices."""
    u, v = uv
    thirds = []
    for f in faces:
        if u in f and v in f:
            for vert in f:
                if vert not in (u, v):
                    thirds.append(vert)
                    break
    return thirds


def find_max_depth_face(faces, depth):
    max_d = max(depth.values())
    return [i for i, d in depth.items() if d == max_d][0], max_d


# Initial state
faces = list(FACES0)
chords = list(CHORDS0)
depth, _ = compute_depths(faces, outer_set)
F_idx, d = find_max_depth_face(faces, depth)
F = faces[F_idx]
print(f'Iter 0: F = {F}, depth = {d}')

for step in range(8):
    ok, _, fp_idx, e = check_balanced(F_idx, faces, depth, outer_set)
    if ok:
        Fp = faces[fp_idx]
        print(f'  -> balanced switch exists on edge {tuple(e)}, F = {F}, '
              f'F\' = {Fp}. STOP.')
        break

    # Pick any depth-(d-1) neighbour for preprocessing.
    F_set = set(F)
    fp_choice = None
    for e_test in [frozenset((F[0], F[1])), frozenset((F[1], F[2])),
                   frozenset((F[0], F[2]))]:
        if e_test in outer_set:
            continue
        cands = [j for j, fj in enumerate(faces)
                 if j != F_idx and e_test in face_edges(fj)]
        if cands and depth[cands[0]] == d - 1:
            fp_choice = (e_test, cands[0])
            break
    if fp_choice is None:
        print('  no depth-(d-1) neighbour; cannot preprocess.')
        break

    e, fp_idx = fp_choice
    Fp = faces[fp_idx]
    u, v = tuple(e)
    w = [vert for vert in F if vert != u and vert != v][0]
    x = [vert for vert in Fp if vert != u and vert != v][0]
    print(f'  preprocessing switch: uv = ({u},{v}), w = {w}, x = {x}, '
          f'F\' = {Fp}')

    faces, chords = apply_switch(faces, chords, (u, v), (w, x))
    depth, _ = compute_depths(faces, outer_set)
    F_idx, d = find_max_depth_face(faces, depth)
    F = faces[F_idx]
    print(f'Iter {step + 1}: max-depth face = {F}, depth = {d}')

print('Done.')
