import pygame
import math
import random

# ==========================================
# INITIALISATION & CONSTANTES
# ==========================================
pygame.init()
WIDTH, HEIGHT = 1100, 850
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Construction Géométrique Interactive")
clock = pygame.time.Clock()

# Couleurs
BLACK    = (0, 0, 0)
BLUE     = (30, 144, 255)
ORANGE   = (255, 165, 0)
RED      = (220, 20, 60)
MAUVE    = (224, 176, 255)
GRAY     = (130, 130, 160)
WHITE    = (255, 255, 255)
FOCUS_BG = (25, 35, 55)
FOCUS_BORDER = (70, 110, 200)

# Système de caméra / Focus
cam_x, cam_y = 0.0, 0.0
zoom = 1.0
dragging = False
drag_start = (0, 0)

# Géométrie
v1, v2, theta = None, None, None
pts = {}
show_trisection = True

# ==========================================
# FONCTIONS MATHÉMATIQUES & TRANSFORMATIONS
# ==========================================
def screen_to_world(sx, sy):
    """Convertit coordonnées écran -> monde"""
    return ((sx - WIDTH/2) / zoom + cam_x, (sy - HEIGHT/2) / zoom + cam_y)

def world_to_screen(wx, wy):
    """Convertit coordonnées monde -> écran"""
    return (int((wx - cam_x) * zoom + WIDTH/2), int((wy - cam_y) * zoom + HEIGHT/2))

def calculate_geometry():
    """Génère ou recalcule toute la construction"""
    global v1, v2, theta, pts
    v1 = random.randint(140, 320)
    v2 = random.randint(60, 2*v1 - 1)
    theta = 2 * math.asin(v2 / (2 * v1))

    # Points de base
    O = (0, 0)
    A = (v1, 0)
    B = (v1 * math.cos(theta), v1 * math.sin(theta))

    # Trisection de l'arc
    T1 = (v1 * math.cos(theta/3), v1 * math.sin(theta/3))
    T2 = (v1 * math.cos(2*theta/3), v1 * math.sin(2*theta/3))

    # Bissectrice
    phi = theta / 2
    dir_ = (math.cos(phi), math.sin(phi))
    M = (v1 * dir_[0], v1 * dir_[1])
    # Prolongement + double du côté : OM + 2*OA = 3*OA
    P = (3 * v1 * dir_[0], 3 * v1 * dir_[1])

    # O' : intersection cercle(M, v1) et bissectrice
    O_prime = (2 * v1 * dir_[0], 2 * v1 * dir_[1])

    # M' : intersection arc(O', v1) et prolongement bissectrice (loin de O)
    M_prime = (3 * v1 * dir_[0], 3 * v1 * dir_[1])

    # Distance MA
    MA = math.hypot(A[0] - M[0], A[1] - M[1])

    # Intersections : cercle(O', v1) ∩ cercle(M', MA)
    d = math.hypot(M_prime[0] - O_prime[0], M_prime[1] - O_prime[1])  # Doit valoir v1
    a = (v1**2 - MA**2 + d**2) / (2 * d)
    h = math.sqrt(max(0, v1**2 - a**2))
    
    mid_x = O_prime[0] + a * dir_[0]
    mid_y = O_prime[1] + a * dir_[1]
    
    perp = (-dir_[1], dir_[0])
    A_prime = (mid_x + h * perp[0], mid_y + h * perp[1])
    B_prime = (mid_x - h * perp[0], mid_y - h * perp[1])

    pts = {
        'O': O, 'A': A, 'B': B, 'T1': T1, 'T2': T2,
        'M': M, 'P': P, 'O\'': O_prime, 'M\'': M_prime,
        'A\'': A_prime, 'B\'': B_prime
    }

calculate_geometry()

# ==========================================
# FONCTIONS DE DESSIN
# ==========================================
def draw_line(color, p1, p2, width=2):
    s1, s2 = world_to_screen(p1[0], p1[1]), world_to_screen(p2[0], p2[1])
    pygame.draw.line(screen, color, s1, s2, width)

def draw_circle(color, center, radius, width=2):
    s = world_to_screen(center[0], center[1])
    pygame.draw.circle(screen, color, s, max(1, int(radius * zoom)), width)

def draw_arc_math(color, center, radius, start, end, width=2):
    """Dessine un arc avec angles mathématiques (0=Est, +CCW)"""
    sx, sy = world_to_screen(center[0], center[1])
    r = max(1, int(radius * zoom))
    rect = pygame.Rect(sx - r, sy - r, 2 * r, 2 * r)
    # Pygame: angles croissants CW, Y inversé. On inverse les angles.
    pygame.draw.arc(screen, color, rect, -end, -start, width)

def draw_point(color, pos, radius=5):
    s = world_to_screen(pos[0], pos[1])
    pygame.draw.circle(screen, color, s, radius)

def draw_label(text, pos, color=WHITE, offset=(10, -10), size=20):
    font = pygame.font.SysFont(None, size)
    surf = font.render(text, True, color)
    s = world_to_screen(pos[0], pos[1])
    screen.blit(surf, (s[0] + offset[0], s[1] + offset[1]))

def draw_focus_indicator():
    """Indicateur visuel du centre de zoom/focus"""
    cx, cy = WIDTH//2, HEIGHT//2
    size = 200
    # Fond subtil
    focus_surf = pygame.Surface((size, size), pygame.SRCALPHA)
    focus_surf.fill((*FOCUS_BG[:3], 40))
    screen.blit(focus_surf, (cx - size//2, cy - size//2))
    # Bordure & croix
    pygame.draw.rect(screen, FOCUS_BORDER, (cx - size//2, cy - size//2, size, size), 2)
    pygame.draw.line(screen, FOCUS_BORDER, (cx - 15, cy), (cx + 15, cy), 2)
    pygame.draw.line(screen, FOCUS_BORDER, (cx, cy - 15), (cx, cy + 15), 2)

# ==========================================
# BOUCLE PRINCIPALE
# ==========================================
running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_t:
                show_trisection = not show_trisection
            elif event.key == pygame.K_r:
                calculate_geometry()
            elif event.key == pygame.K_f:
                # Reset focus au centre
                cam_x, cam_y, zoom = 0.0, 0.0, 1.0
                
        elif event.type == pygame.MOUSEBUTTONDOWN:
            if event.button == 1:
                dragging = True
                drag_start = event.pos
            elif event.button == 4:  # Molette haut
                wx, wy = screen_to_world(event.pos[0], event.pos[1])
                zoom = min(5.0, zoom * 1.25)
                cam_x = wx - (event.pos[0] - WIDTH/2) / zoom
                cam_y = wy - (event.pos[1] - HEIGHT/2) / zoom
            elif event.button == 5:  # Molette bas
                wx, wy = screen_to_world(event.pos[0], event.pos[1])
                zoom = max(0.2, zoom / 1.25)
                cam_x = wx - (event.pos[0] - WIDTH/2) / zoom
                cam_y = wy - (event.pos[1] - HEIGHT/2) / zoom

        elif event.type == pygame.MOUSEMOTION:
            if dragging:
                dx = event.pos[0] - drag_start[0]
                dy = event.pos[1] - drag_start[1]
                cam_x -= dx / zoom
                cam_y -= dy / zoom
                drag_start = event.pos

        elif event.type == pygame.MOUSEBUTTONUP:
            if event.button == 1:
                dragging = False

    # ---- DESSIN ----
    screen.fill(BLACK)
    draw_focus_indicator()

    # 1) Bissectrice (orange) & point M
    draw_line(ORANGE, pts['O'], pts['P'], 3)
    draw_point(ORANGE, pts['M'], 6)
    draw_label("M", pts['M'], ORANGE)

    # 2) Cercle de centre M (rouge) & point O'
    draw_circle(RED, pts['M'], v1, 2)
    draw_point(RED, pts['O\''], 7)
    draw_label("O'", pts['O\''], RED)

    # 3) Arc reporté (mauve), M', cercle M', et points A', B'
    draw_arc_math(MAUVE, pts['O\''], v1, 0, theta, 2)
    draw_point(MAUVE, pts['M\''], 6)
    draw_label("M'", pts['M\''], MAUVE)
    
    MA = math.hypot(pts['A'][0]-pts['M'][0], pts['A'][1]-pts['M'][1])
    draw_circle(MAUVE, pts['M\''], MA, 2)
    draw_point(MAUVE, pts['A\''], 6)
    draw_point(MAUVE, pts['B\''], 6)
    draw_label("A'", pts['A\''], MAUVE)
    draw_label("B'", pts['B\''], MAUVE)

    # 4) Segments OA' et OB' (mauve)
    draw_line(MAUVE, pts['O'], pts['A\''], 3)
    draw_line(MAUVE, pts['O'], pts['B\''], 3)

    # Angle de base (bleu)
    draw_line(BLUE, pts['O'], pts['A'], 3)
    draw_line(BLUE, pts['O'], pts['B'], 3)
    draw_arc_math(BLUE, pts['O'], v1, 0, theta, 3)
    draw_point(BLUE, pts['A'], 6)
    draw_point(BLUE, pts['B'], 6)
    draw_label("O", pts['O'], WHITE, offset=(10, 10))
    draw_label("A", pts['A'], BLUE)
    draw_label("B", pts['B'], BLUE)

    # Cercles de trisection (toggle T)
    if show_trisection:
        draw_circle(GRAY, pts['T1'], v1, 1)
        draw_circle(GRAY, pts['T2'], v1, 1)
        draw_point(GRAY, pts['T1'], 4)
        draw_point(GRAY, pts['T2'], 4)
        draw_label("T1", pts['T1'], GRAY, offset=(-20, -10))
        draw_label("T2", pts['T2'], GRAY, offset=(-20, -10))

    # UI & Infos
    font_ui = pygame.font.SysFont(None, 22)
    info = font_ui.render(f"OA={v1} | AB={v2} | θ={math.degrees(theta):.1f}° | Zoom={zoom:.2f}x", True, WHITE)
    screen.blit(info, (15, HEIGHT - 45))
    
    help_text = font_ui.render("🖱️ Clic+Glisser: Déplacer | 🖱️ Molette: Zoom | T: Cercles | R: Random | F: Reset", True, (120, 140, 180))
    screen.blit(help_text, (15, HEIGHT - 20))

    pygame.display.flip()
    clock.tick(60)

pygame.quit()