# Prompt: Create ghosts with click-to-select and WASD movement controls,
# plus a spooky background atmosphere

# Global variables to store all our ghosts and game state
ghosts = []
selected_ghost = None  # Which ghost is currently selected
stars = []  # Background stars
move_speed = 3  # How fast selected ghost moves with WASD

def setup():
    """
    This function runs once when the program starts
    Sets up our canvas, creates the first ghost, and generates background elements
    """
    size(800, 600)  # Create a window 800 pixels wide, 600 pixels tall
    
    # Create the first ghost in the center of the screen
    first_ghost = create_ghost(width/2, height/2)
    ghosts.append(first_ghost)
    
    # Generate background stars for atmosphere
    create_background_stars()
    
    print("Click on a ghost to select it, then use WASD to move it around!")
    print("Click empty space to add new ghosts!")

def create_background_stars():
    """
    Creates twinkling stars for the spooky background
    """
    import random
    
    # Create random stars across the screen
    for i in range(80):
        star = {
            'x': random.uniform(0, width),
            'y': random.uniform(0, height),
            'brightness': random.uniform(100, 255),
            'twinkle_speed': random.uniform(0.01, 0.03),
            'twinkle_offset': random.uniform(0, TWO_PI)
        }
        stars.append(star)

def draw():
    """
    This function runs continuously (about 60 times per second)
    It draws everything on the screen
    """
    # Draw the spooky background
    draw_background()
    
    # Update and draw all ghosts
    for ghost in ghosts:
        update_ghost(ghost)
        draw_ghost(ghost)
    
    # Draw selection indicator around selected ghost
    if selected_ghost is not None:
        draw_selection_indicator(selected_ghost)
    
    # Draw instructions
    draw_instructions()

def draw_background():
    """
    Creates a spooky nighttime background with twinkling stars
    """
    # Create gradient background from dark blue to black
    for y in range(height):
        # Calculate color based on height (darker at bottom)
        inter = map(y, 0, height, 0, 1)
        c = lerpColor(color(25, 25, 60), color(10, 10, 30), inter)
        stroke(c)
        line(0, y, width, y)
    
    # Draw twinkling stars
    for star in stars:
        # Update twinkle animation
        star['twinkle_offset'] += star['twinkle_speed']
        
        # Calculate current brightness with twinkling effect
        current_brightness = star['brightness'] + sin(star['twinkle_offset']) * 50
        current_brightness = constrain(current_brightness, 50, 255)
        
        # Draw the star
        stroke(255, 255, 200, current_brightness)
        strokeWeight(1)
        point(star['x'], star['y'])
        
        # Add a subtle glow for brighter stars
        if current_brightness > 200:
            stroke(255, 255, 150, current_brightness * 0.3)
            strokeWeight(2)
            point(star['x'], star['y'])

def create_ghost(x, y):
    """
    Creates a new ghost dictionary with all its properties
    """
    import random
    
    ghost = {
        'x': x,                           # Ghost's x position
        'y': y,                           # Ghost's y position
        'size': random.uniform(60, 100),  # Random size for variety
        'float_offset': random.uniform(0, TWO_PI),  # For floating animation
        'original_y': y,                  # Remember starting position for floating
        'blink_timer': 0,                 # For blinking animation
        'is_blinking': False,             # Whether ghost is currently blinking
        'wave_offset': random.uniform(0, TWO_PI),  # For bottom wave animation
        'is_selected': False              # Whether this ghost is selected
    }
    return ghost

def update_ghost(ghost):
    """
    Updates the ghost's animation properties each frame
    """
    # Only float if ghost is not selected (selected ghosts move with WASD)
    if not ghost['is_selected']:
        # Make the ghost float up and down smoothly
        ghost['float_offset'] += 0.015
        ghost['y'] = ghost['original_y'] + sin(ghost['float_offset']) * 12
    else:
        # Update the original_y for selected ghost so it floats from new position
        ghost['original_y'] = ghost['y']
    
    # Animate the wavy bottom
    ghost['wave_offset'] += 0.03
    
    # Random blinking system
    ghost['blink_timer'] += 1
    if ghost['blink_timer'] > 120:  # Every 2 seconds
        import random
        if random.random() < 0.08:   # 8% chance to blink
            ghost['is_blinking'] = True
            ghost['blink_timer'] = 0
    
    # Stop blinking after a short time
    if ghost['is_blinking'] and ghost['blink_timer'] > 8:
        ghost['is_blinking'] = False

def draw_ghost(ghost):
    """
    Draws a single ghost with body shape
    """
    # Save current drawing settings
    pushMatrix()
    pushStyle()
    
    # Move to ghost's position
    translate(ghost['x'], ghost['y'])
    
    # Draw a subtle glow effect (brighter if selected)
    draw_glow_effect(ghost)
    
    # Draw the main ghost body
    draw_ghost_body(ghost)
    
    # Draw the eyes that follow the mouse
    draw_eyes(ghost)
    
    # Draw a cute mouth
    draw_mouth(ghost)
    
    # Restore drawing settings
    popStyle()
    popMatrix()

def draw_glow_effect(ghost):
    """
    Creates a subtle glow around the ghost (enhanced if selected)
    """
    noStroke()
    glow_layers = 4 if ghost['is_selected'] else 3
    base_alpha = 60 if ghost['is_selected'] else 40
    
    for i in range(glow_layers):
        alpha = base_alpha - (i * 12)
        if alpha > 0:
            if ghost['is_selected']:
                fill(150, 200, 255, alpha)  # Blue glow for selected ghost
            else:
                fill(200, 220, 255, alpha)  # Normal white-blue glow
            ellipse(0, -ghost['size'] * 0.1, 
                   ghost['size'] + (i * 15), 
                   ghost['size'] * 1.4 + (i * 15))

def draw_ghost_body(ghost):
    """
    Draws the main ghost body 
    """
    # Slightly different color if selected
    if ghost['is_selected']:
        fill(255, 255, 255, 240)  # More opaque when selected
        stroke(150, 200, 255, 180)  # Blue outline when selected
        strokeWeight(2)
    else:
        fill(255, 255, 255, 220)  # Normal transparency
        stroke(220, 230, 255, 150)  # Normal outline
        strokeWeight(1.5)
    
    # Calculate body dimensions
    body_width = ghost['size']
    body_height = ghost['size'] * 1.2
    head_radius = ghost['size'] * 0.5
    
    # Draw the body using beginShape for custom shape
    beginShape()
    
    # Start from left side of the head, going clockwise
    for angle in range(180, 361, 10):
        x = cos(radians(angle)) * head_radius
        y = sin(radians(angle)) * head_radius - ghost['size'] * 0.2
        vertex(x, y)
    
    # Right side going down
    vertex(head_radius, -ghost['size'] * 0.2)
    vertex(head_radius * 0.9, ghost['size'] * 0.1)
    vertex(head_radius * 0.8, ghost['size'] * 0.4)
    
    # Bottom wavy part
    num_waves = 5
    for i in range(num_waves + 1):
        progress = float(i) / num_waves
        x = lerp(head_radius * 0.8, -head_radius * 0.8, progress)
        
        wave_height = sin(ghost['wave_offset'] + i * 0.8) * 8
        y = ghost['size'] * 0.6 + wave_height
        
        center_boost = sin(progress * PI) * 5
        y += center_boost
        
        vertex(x, y)
    
    # Left side going up
    vertex(-head_radius * 0.8, ghost['size'] * 0.4)
    vertex(-head_radius * 0.9, ghost['size'] * 0.1)
    vertex(-head_radius, -ghost['size'] * 0.2)
    
    endShape(CLOSE)

def draw_eyes(ghost):
    """
    Draws REPO-style eyes that follow the mouse cursor
    """
    eye_size = ghost['size'] * 0.18
    eye_distance = ghost['size'] * 0.22
    
    # Calculate where the eyes should look
    mouse_local_x = mouseX - ghost['x']
    mouse_local_y = mouseY - ghost['y']
    look_angle = atan2(mouse_local_y, mouse_local_x)
    max_pupil_distance = eye_size * 0.2
    pupil_x = cos(look_angle) * max_pupil_distance
    pupil_y = sin(look_angle) * max_pupil_distance
    eye_y = -ghost['size'] * 0.25
    
    # Draw eye backgrounds
    fill(255)
    stroke(180, 180, 200)
    strokeWeight(1)
    ellipse(-eye_distance, eye_y, eye_size, eye_size)
    ellipse(eye_distance, eye_y, eye_size, eye_size)
    
    # Draw pupils (unless blinking)
    if not ghost['is_blinking']:
        fill(20, 20, 40)
        noStroke()
        pupil_size = eye_size * 0.45
        
        ellipse(-eye_distance + pupil_x, eye_y + pupil_y, pupil_size, pupil_size)
        ellipse(eye_distance + pupil_x, eye_y + pupil_y, pupil_size, pupil_size)
        
        # Highlights
        fill(255, 255, 255, 200)
        highlight_size = pupil_size * 0.3
        ellipse(-eye_distance + pupil_x + 2, eye_y + pupil_y - 2, highlight_size, highlight_size)
        ellipse(eye_distance + pupil_x + 2, eye_y + pupil_y - 2, highlight_size, highlight_size)
        
        fill(255)
        ellipse(-eye_distance + pupil_x + 1, eye_y + pupil_y - 3, 2, 2)
        ellipse(eye_distance + pupil_x + 1, eye_y + pupil_y - 3, 2, 2)
    else:
        # Closed eyes
        stroke(100, 100, 120)
        strokeWeight(2)
        noFill()
        arc(-eye_distance, eye_y, eye_size * 0.8, eye_size * 0.3, 0, PI)
        arc(eye_distance, eye_y, eye_size * 0.8, eye_size * 0.3, 0, PI)

def draw_mouth(ghost):
    """
    Draws a simple mouth
    """
    stroke(80, 80, 100)
    strokeWeight(1.5)
    noFill()
    
    mouth_width = ghost['size'] * 0.12
    mouth_y = -ghost['size'] * 0.05
    arc(0, mouth_y, mouth_width, mouth_width * 0.6, 0, PI)

def draw_selection_indicator(ghost):
    """
    Draws a glowing ring around the selected ghost
    """
    pushMatrix()
    pushStyle()
    
    translate(ghost['x'], ghost['y'])
    
    # Animated selection ring
    ring_size = ghost['size'] * 1.6 + sin(millis() * 0.01) * 10
    
    noFill()
    stroke(100, 150, 255, 150)  # Blue selection color
    strokeWeight(3)
    ellipse(0, 0, ring_size, ring_size)
    
    # Inner ring
    stroke(150, 200, 255, 100)
    strokeWeight(1)
    ellipse(0, 0, ring_size - 10, ring_size - 10)
    
    popStyle()
    popMatrix()

def draw_instructions():
    """
    Draws helpful instructions on screen
    """
    fill(255, 255, 255, 180)
    textAlign(LEFT)
    textSize(12)
    
    text("Click ghost to select • WASD to move selected ghost", 10, height - 40)
    text("Click empty space to add ghost • C to clear • R to remove last", 10, height - 25)
    text("SPACE to make all blink", 10, height - 10)
    
    # Show selected ghost info
    if selected_ghost is not None:
        fill(150, 200, 255, 200)
        text("Selected ghost - use WASD to move!", 10, 25)

def mousePressed():
    """
    Handle mouse clicks - select ghosts or create new ones
    """
    global selected_ghost
    
    # Check if we clicked on any ghost
    clicked_ghost = None
    for ghost in ghosts:
        distance = dist(mouseX, mouseY, ghost['x'], ghost['y'])
        if distance < ghost['size'] * 0.7:  # Click within ghost bounds
            clicked_ghost = ghost
            break
    
    if clicked_ghost is not None:
        # Deselect previous ghost
        if selected_ghost is not None:
            selected_ghost['is_selected'] = False
        
        # Select the clicked ghost
        selected_ghost = clicked_ghost
        selected_ghost['is_selected'] = True
        print("Ghost selected! Use WASD to move it around.")
    else:
        # Deselect current ghost
        if selected_ghost is not None:
            selected_ghost['is_selected'] = False
            selected_ghost = None
        
        # Create new ghost at click location
        new_ghost = create_ghost(mouseX, mouseY)
        ghosts.append(new_ghost)
        print("New ghost created! Total ghosts: " + str(len(ghosts)))
        create_click_sparkle(mouseX, mouseY)

def create_click_sparkle(x, y):
    """
    Creates sparkle effect when creating new ghosts
    """
    import random
    
    pushMatrix()
    pushStyle()
    translate(x, y)
    
    stroke(255, 255, 150, 180)
    strokeWeight(2)
    
    for i in range(8):
        angle = (TWO_PI / 8) * i
        length = random.uniform(10, 25)
        x1 = cos(angle) * length * 0.5
        y1 = sin(angle) * length * 0.5
        x2 = cos(angle) * length
        y2 = sin(angle) * length
        line(x1, y1, x2, y2)
    
    popStyle()
def keyPressed():
    """
    Handle keyboard input for ghost controls
    """
    global selected_ghost, move_speed
    
    # WASD movement for selected ghost
    if selected_ghost is not None:
        if key == 'w' or key == 'W':
            selected_ghost['y'] -= move_speed
            selected_ghost['original_y'] = selected_ghost['y']  # Update float reference
            print("Moving ghost up")
        elif key == 's' or key == 'S':
            selected_ghost['y'] += move_speed
            selected_ghost['original_y'] = selected_ghost['y']  # Update float reference
            print("Moving ghost down")
        elif key == 'a' or key == 'A':
            selected_ghost['x'] -= move_speed
            print("Moving ghost left")
        elif key == 'd' or key == 'D':
            selected_ghost['x'] += move_speed
            print("Moving ghost right")
        
        # Keep ghost within screen bounds
        selected_ghost['x'] = constrain(selected_ghost['x'], 50, width - 50)
        selected_ghost['y'] = constrain(selected_ghost['y'], 50, height - 50)
    
    # Other keyboard controls
    if key == 'c' or key == 'C':
        # Clear all ghosts except one and deselect
        if len(ghosts) > 1:
            first_ghost = ghosts[0]
            first_ghost['is_selected'] = False
            ghosts[:] = [first_ghost]
            selected_ghost = None
            print("Ghosts cleared! Click to add more.")
    
    elif key == 'r' or key == 'R':
        # Remove the last ghost (but keep at least one)
        if len(ghosts) > 1:
            removed_ghost = ghosts.pop()
            # If we removed the selected ghost, clear selection
            if removed_ghost == selected_ghost:
                selected_ghost = None
            print("Last ghost removed! Total ghosts: " + str(len(ghosts)))
    
    elif key == ' ':  # Spacebar
        # Make all ghosts blink simultaneously
        for ghost in ghosts:
            ghost['is_blinking'] = True
            ghost['blink_timer'] = 0
        print("All ghosts blink!")
    
    # Bonus: Change movement speed with + and - keys
    elif key == '+' or key == '=':
        move_speed = min(move_speed + 1, 10)  # Max speed of 10
        print("Movement speed increased to: " + str(move_speed))
    elif key == '-' or key == '_':
        move_speed = max(move_speed - 1, 1)   # Min speed of 1
        print("Movement speed decreased to: " + str(move_speed))