"""Draw a labeled quadrilateral sequence diagram for the plane depth sequencing paper."""
import matplotlib.pyplot as plt
import matplotlib.patches as patches
from matplotlib.patches import FancyArrowPatch
import numpy as np

sequence_data = [
    {"vertices": [-2, -1, 3, 7], "ordered": [-2, 7, -1, 3], "depths": [0, 1, 0, 1], "move": "Q_1", "type": "deep_diamond"},
    {"vertices": [-1, 3, 4, 8], "ordered": [3, -1, 4, 8], "depths": [1, 0, 1, 2], "move": "Q_2^{AD}", "type": "s_quad"},
    {"vertices": [2, 4, 5, 8], "ordered": [4, 2, 5, 8], "depths": [1, 0, 1, 2], "move": "Q_3^{AD}", "type": "s_quad"},
    {"vertices": [-1, 2, 4, 6], "ordered": [4, 2, 6, -1], "depths": [1, 0, 1, 0], "move": "Q_4^{LA}", "type": "shallow_diamond"},
]

COLORS = {
    "deep_diamond": "#B2DFDB",
    "shallow_diamond": "#FFE0B2",
    "s_quad": "#F8BBD0",
}

def draw_sequence_diagram(sequence_data: list[dict], output_path: str = "quad_sequence_diagram.png"):
    """Draw the full quadrilateral sequence diagram."""
    fig, ax = plt.subplots(figsize=(16, 5.5))

    step_width = 4.0
    y_center = 1.0
    x_positions = [i * step_width for i in range(len(sequence_data))]

    # Store vertex positions for connecting matching vertices
    quad_vertex_positions = []

    for i, quad_data in enumerate(sequence_data):
        x = x_positions[i]
        quad_type = quad_data["type"]
        ordered_verts = quad_data["ordered"]
        depths = quad_data["depths"]

        # Standard positions: top, right, bottom, left (or tweezers variant)
        if quad_type == "s_quad":
            # Tweezers shape: top, left-pinched-upper, right, left-pinched-lower
            r = 0.45
            pinch = 0.25
            std_positions = [
                (x, y_center + r),                    # e1: top
                (x - r + pinch, y_center + r * 0.35),  # a: left-upper
                (x + r, y_center),                    # e2: right
                (x - r + pinch, y_center - r * 0.35),  # b: left-lower
            ]
        else:
            # Diamond shape: top, right, bottom, left
            r = 0.45
            std_positions = [
                (x, y_center + r),        # e1: top
                (x + r, y_center),        # a: right
                (x, y_center - r),        # e2: bottom
                (x - r, y_center),        # b: left
            ]

        # Draw polygon
        polygon = patches.Polygon(
            std_positions,
            closed=True,
            facecolor=COLORS.get(quad_type, "#CCCCCC"),
            edgecolor="black",
            linewidth=1.5,
            alpha=0.75,
        )
        ax.add_patch(polygon)

        # Draw vertices and labels
        vertex_positions = {}
        for vert_idx, (vertex_id, (vx, vy), depth) in enumerate(zip(ordered_verts, std_positions, depths)):
            # Draw vertex point
            ax.plot(vx, vy, "ko", markersize=7, zorder=5)
            vertex_positions[vertex_id] = (vx, vy)

            # Draw vertex label below
            ax.text(vx, vy - 0.2, str(vertex_id), ha="center", va="top", fontsize=8, fontweight="bold",
                   bbox=dict(boxstyle="round,pad=0.15", facecolor="lightyellow", alpha=0.9, edgecolor="none"))

        # Draw quad label in center
        center_x = np.mean([p[0] for p in std_positions])
        center_y = np.mean([p[1] for p in std_positions])
        ax.text(center_x, center_y, f"${quad_data['move']}$", ha="center", va="center",
               fontsize=11, fontweight="bold",
               bbox=dict(boxstyle="round,pad=0.3", facecolor="white", edgecolor="black", alpha=0.85))

        # Draw depth labels on the left (only for first quad)
        if i == 0:
            unique_depths = sorted(set(depths))
            for d in unique_depths:
                label_y = y_center + 0.5 - d * 0.5
                ax.text(x - 1.0, label_y, f"d={d}", ha="right", va="center", fontsize=9, style="italic")

        quad_vertex_positions.append(vertex_positions)

        # Draw arrows connecting matching vertices to next quad
        if i < len(sequence_data) - 1:
            next_quad_verts = set(sequence_data[i + 1]["vertices"])
            for vertex_id, (vx, vy) in vertex_positions.items():
                if vertex_id in next_quad_verts:
                    next_quad_data = sequence_data[i + 1]
                    next_x = x_positions[i + 1]
                    next_idx = next_quad_data["ordered"].index(vertex_id)

                    if next_quad_data["type"] == "s_quad":
                        r = 0.45
                        pinch = 0.25
                        next_std_positions = [
                            (next_x, y_center + r),
                            (next_x - r + pinch, y_center + r * 0.35),
                            (next_x + r, y_center),
                            (next_x - r + pinch, y_center - r * 0.35),
                        ]
                    else:
                        r = 0.45
                        next_std_positions = [
                            (next_x, y_center + r),
                            (next_x + r, y_center),
                            (next_x, y_center - r),
                            (next_x - r, y_center),
                        ]

                    next_vx, next_vy = next_std_positions[next_idx]

                    # Draw connecting arrow
                    arrow = FancyArrowPatch(
                        (vx, vy), (next_vx, next_vy),
                        arrowstyle="->", color="gray", linewidth=0.8, alpha=0.5, zorder=1,
                        connectionstyle="arc3,rad=0.15"
                    )
                    ax.add_patch(arrow)

    # Set axis properties
    ax.set_xlim(-1.8, x_positions[-1] + 1.2)
    ax.set_ylim(-1.0, 2.2)
    ax.set_aspect("equal")
    ax.axis("off")

    plt.tight_layout()
    plt.savefig(output_path, dpi=150, bbox_inches="tight")
    print(f"Saved diagram to {output_path}")
    plt.show()


if __name__ == "__main__":
    draw_sequence_diagram(sequence_data, "quad_sequence_diagram.png")
