import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.animation import FuncAnimation
from matplotlib.widgets import Button

cube_points = np.array([
    [-1, -1, -1],
    [-1, -1,  1],
    [-1,  1, -1],
    [-1,  1,  1],
    [ 1, -1, -1],
    [ 1, -1,  1],
    [ 1,  1, -1],
    [ 1,  1,  1]
])

edges = [
    (0, 1), (0, 2), (0, 4),
    (1, 3), (1, 5),
    (2, 3), (2, 6),
    (3, 7),
    (4, 5), (4, 6),
    (5, 7),
    (6, 7)
]

def rotation_y(theta):
    return np.array([
        [np.cos(theta), 0, np.sin(theta)],
        [0, 1, 0], # 0,1,0
        [-np.sin(theta), 0, np.cos(theta)]
    ])


def rotation_z(theta):
    return np.array([
        [np.cos(theta), -np.sin(theta), 0],
        [np.sin(theta),  np.cos(theta), 0],
        [0, 0, 1]
    ])


fig = plt.figure(figsize=(8, 6))
ax = fig.add_subplot(111, projection='3d')
ax.set_xlim(-2, 2)
ax.set_ylim(-2, 2)
ax.set_zlim(-2, 2)
ax.set_box_aspect([1, 1, 1])
ax.set_title("Matrixmultiplikation", pad=20)

lines = [ax.plot([], [], [], color='red')[0] for _ in edges]
text = ax.text2D(1.02, 0.5, "", transform=ax.transAxes,
                 fontsize=10, color='black', family='monospace', va='center')

paused = False
highlight = False
points_scat = None
labels = []


def update(frame):
    global points_scat, labels

    if paused:
        return lines + [text]

    theta = np.radians(frame)
    R_y = rotation_y(theta)
    R_z = rotation_z(theta * 0.7)
    R = R_y @ R_z

    rotated = cube_points @ R.T

    # Update edges
    for line, (i1, i2) in zip(lines, edges):
        p1, p2 = rotated[i1], rotated[i2]
        line.set_data([p1[0], p2[0]], [p1[1], p2[1]])
        line.set_3d_properties([p1[2], p2[2]])

    # Update rotation matrices display
    matrix_str_y = "\n".join(
        ["[" + " ".join(f"{val:+.2f}" for val in row) + "]" for row in R_y]
    )
    matrix_str_z = "\n".join(
        ["[" + " ".join(f"{val:+.2f}" for val in row) + "]" for row in R_z]
    )
    matrix_str_R = "\n".join(
        ["[" + " ".join(f"{val:+.2f}" for val in row) + "]" for row in R]
    )

    text.set_text(
        f"θ = {np.degrees(theta):6.2f}°\n"
        f"sin(θ) = {np.sin(theta): .3f}\n"
        f"cos(θ) = {np.cos(theta): .3f}\n\n"
        f"R_y(θ):\n{matrix_str_y}\n\n"
        f"R_z(0.7θ):\n{matrix_str_z}\n\n"
        f"R = R_y · R_z:\n{matrix_str_R}"
    )

    # Always show vertex points and labels
    if points_scat is None:
        points_scat = ax.scatter([], [], [], color='blue', s=40)

    points_scat._offsets3d = (
        rotated[:, 0], rotated[:, 1], rotated[:, 2]
    )

    for label in labels:
        label.remove()
    labels.clear()

    for i, p in enumerate(rotated):
        labels.append(ax.text(p[0], p[1], p[2], f"P{i}",
                              color='black', fontsize=8))

    return lines + [text]



axpause = plt.axes([0.4, 0.02, 0.3, 0.05])
bpause = Button(axpause, 'Pause / Resume')


def toggle_pause(event):
    global paused
    paused = not paused


def toggle_highlight(event):
    global highlight
    highlight = not highlight


bpause.on_clicked(toggle_pause)

ani = FuncAnimation(
    fig, update, frames=np.arange(0, 360, 2),
    interval=50, blit=False
)

plt.show()
