from manim import *
import numpy as np
import random

class OSKVLogoReveal(Scene):
    def construct(self):
        self.camera.background_color = "#050510"
        
        # === PART 1: Particles converge to center ===
        particles = VGroup()
        for _ in range(100):
            dot = Dot(
                point=np.array([
                    random.uniform(-8, 8),
                    random.uniform(-5, 5),
                    0
                ]),
                radius=random.uniform(0.02, 0.06),
                color=random.choice([WHITE, BLUE_B, PURPLE_B])
            )
            dot.set_opacity(random.uniform(0.3, 0.8))
            particles.add(dot)
        
        self.add(particles)
        
        # Converge to center
        self.play(
            *[dot.animate.move_to(
                np.array([random.uniform(-0.5, 0.5), random.uniform(-0.2, 0.2), 0])
            ).set_opacity(0) for dot in particles],
            run_time=1.5,
            rate_func=rush_into
        )
        self.remove(particles)
        
        # === PART 2: Flash and logo image appears ===
        # White flash
        flash = Rectangle(
            width=16, height=10,
            fill_color=WHITE,
            fill_opacity=0.8,
            stroke_width=0
        )
        self.play(FadeIn(flash, run_time=0.1))
        self.play(FadeOut(flash, run_time=0.3))
        
        # Load the actual OSKV logo
        logo = ImageMobject("/Users/jakeshore/.clawdbot/workspace/oskv_logo.png")
        logo.scale_to_fit_width(8)
        logo.set_opacity(0)
        self.add(logo)
        
        # Fade in the logo
        self.play(logo.animate.set_opacity(1), run_time=1)
        
        # === PART 3: Geometric frame builds around logo ===
        # Inner frame
        inner_frame = RoundedRectangle(
            width=9, height=3,
            corner_radius=0.3,
            color=WHITE,
            stroke_width=1.5
        )
        inner_frame.set_stroke(opacity=0.5)
        
        # Outer frame  
        outer_frame = RoundedRectangle(
            width=10, height=3.8,
            corner_radius=0.4,
            color=BLUE_B,
            stroke_width=1
        )
        outer_frame.set_stroke(opacity=0.3)
        
        self.play(
            Create(inner_frame),
            run_time=0.8
        )
        self.play(
            Create(outer_frame),
            run_time=0.6
        )
        
        # === PART 4: Orbiting dots ===
        orbit_dots = VGroup()
        orbit_trails = VGroup()
        
        n_orbits = 6
        for i in range(n_orbits):
            dot = Dot(radius=0.06, color=[BLUE, PURPLE, TEAL, WHITE, BLUE_B, PURPLE_B][i])
            dot.set_glow_factor(1.5)
            
            angle = i * TAU / n_orbits
            radius_x = 5.5
            radius_y = 2.2
            dot.move_to([
                radius_x * np.cos(angle),
                radius_y * np.sin(angle),
                0
            ])
            orbit_dots.add(dot)
        
        self.play(
            LaggedStart(*[GrowFromCenter(d) for d in orbit_dots], lag_ratio=0.1),
            run_time=0.6
        )
        
        # Orbit around the logo
        orbit_anims = []
        for i, dot in enumerate(orbit_dots):
            orbit_anims.append(
                Rotate(dot, TAU * 0.4, about_point=ORIGIN)
            )
        
        self.play(*orbit_anims, run_time=2, rate_func=smooth)
        
        # === PART 5: Pulse effect ===
        # Scale pulse
        logo_group = VGroup(inner_frame, outer_frame)
        
        self.play(
            logo.animate.scale(1.08),
            logo_group.animate.scale(1.08),
            *[dot.animate.scale(1.5).set_opacity(0.5) for dot in orbit_dots],
            rate_func=there_and_back,
            run_time=0.6
        )
        
        # === PART 6: Expanding ring waves ===
        for _ in range(2):
            ring = Circle(radius=0.5, color=BLUE_B, stroke_width=3)
            ring.set_stroke(opacity=0.8)
            self.add(ring)
            self.play(
                ring.animate.scale(12).set_stroke(opacity=0),
                run_time=0.8,
                rate_func=rush_from
            )
            self.remove(ring)
        
        # === PART 7: Corner accents ===
        corners = VGroup()
        corner_positions = [
            (UP + LEFT, 0), (UP + RIGHT, -PI/2),
            (DOWN + RIGHT, PI), (DOWN + LEFT, PI/2)
        ]
        
        for pos, rot in corner_positions:
            corner = VGroup(
                Line(ORIGIN, RIGHT * 0.5, color=TEAL, stroke_width=2),
                Line(ORIGIN, UP * 0.5, color=TEAL, stroke_width=2)
            )
            corner.rotate(rot)
            corner.move_to(pos * np.array([4.8, 1.8, 0]))
            corners.add(corner)
        
        self.play(
            LaggedStart(*[Create(c) for c in corners], lag_ratio=0.1),
            run_time=0.6
        )
        
        # === PART 8: Ambient floating particles ===
        ambient = VGroup()
        for _ in range(40):
            p = Dot(
                radius=random.uniform(0.01, 0.04),
                color=random.choice([WHITE, BLUE_B, PURPLE_B, TEAL])
            )
            p.set_opacity(random.uniform(0.15, 0.4))
            p.move_to([random.uniform(-7, 7), random.uniform(-4, 4), 0])
            ambient.add(p)
        
        self.play(FadeIn(ambient), run_time=0.3)
        
        # Gentle upward drift
        drift_anims = [
            p.animate.shift(UP * random.uniform(0.3, 0.8)).set_opacity(0)
            for p in ambient
        ]
        
        # Second pulse while particles drift
        self.play(
            *drift_anims,
            logo.animate.scale(1.05),
            logo_group.animate.scale(1.05),
            rate_func=there_and_back,
            run_time=2
        )
        
        # === PART 9: Final hold with slow orbit ===
        final_orbit = [
            Rotate(dot, TAU * 0.15, about_point=ORIGIN)
            for dot in orbit_dots
        ]
        
        self.play(*final_orbit, run_time=2, rate_func=linear)
        
        # Hold
        self.wait(1)
        
        # === PART 10: Fade to black ===
        everything = VGroup(logo_group, orbit_dots, corners)
        self.play(
            FadeOut(everything),
            logo.animate.set_opacity(0),
            FadeOut(ambient),
            run_time=1.5
        )
