import pygame
import sys
import time
import math
import random
import colorsys  # For generating rainbow colors

from pygame.locals import *

# Initialize Pygame
pygame.init()
pygame.mixer.init()

# Window dimensions
WIDTH, HEIGHT = 800, 600
WINDOW = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Bombenentwaffnungs-Spiel")

# Colors
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (200, 0, 0)
GREEN = (0, 200, 0)
DARK_RED = (150, 0, 0)
DARK_GREEN = (0, 150, 0)
GRAY = (50, 50, 50)
SEGMENT_ON = (255, 0, 0)      # Red for countdown
SEGMENT_OFF = (20, 20, 20)

# Fonts
FONT = pygame.font.SysFont('Courier', 30)
LCD_FONT = pygame.font.SysFont('Courier', 40, bold=True)
BUMM_FONT = pygame.font.SysFont('Courier', 80, bold=True)  # For "BUMM" text
SUCCESS_FONT = pygame.font.SysFont('Courier', 20, bold=True)  # For success message

# Timer settings
TOTAL_TIME = 180  # Seconds (3 minutes)

# Secret code (Example: "1234")
SECRET_CODE = "1234"

# Num pad settings
NUM_PAD = [
    ['1', '2', '3'],
    ['4', '5', '6'],
    ['7', '8', '9'],
    ['CLR', '0', 'ENT']
]

BUTTON_WIDTH, BUTTON_HEIGHT = 100, 60
BUTTON_PADDING = 20
START_X = (WIDTH - (3 * BUTTON_WIDTH + 2 * BUTTON_PADDING)) // 2
START_Y = HEIGHT - (4 * (BUTTON_HEIGHT + BUTTON_PADDING)) - 50

# Game status
STATUS_COUNTDOWN = 0
STATUS_EXPLOSION = 1
STATUS_SUCCESS = 2

# Explosion settings
EXPLOSION_DURATION = 2  # Seconds

# 7-Segment Display Configuration
SEGMENT_LENGTH = 40
SEGMENT_WIDTH = 10
SEGMENTS = {
    'A': ((20, 0), (60, 0), (50, 10), (30, 10)),
    'B': ((60, 0), (70, 10), (70, 50), (60, 40)),
    'C': ((60, 60), (70, 70), (70, 110), (60, 100)),
    'D': ((20, 100), (60, 100), (50, 110), (30, 110)),
    'E': ((10, 60), (20, 70), (20, 110), (10, 100)),
    'F': ((10, 0), (20, 10), (20, 50), (10, 40)),
    'G': ((20, 50), (60, 50), (50, 60), (30, 60)),
}

# Each digit defines which segments are on
DIGITS = {
    '0': ['A', 'B', 'C', 'D', 'E', 'F'],
    '1': ['B', 'C'],
    '2': ['A', 'B', 'G', 'E', 'D'],
    '3': ['A', 'B', 'G', 'C', 'D'],
    '4': ['F', 'G', 'B', 'C'],
    '5': ['A', 'F', 'G', 'C', 'D'],
    '6': ['A', 'F', 'G', 'C', 'D', 'E'],
    '7': ['A', 'B', 'C'],
    '8': ['A', 'B', 'C', 'D', 'E', 'F', 'G'],
    '9': ['A', 'B', 'C', 'D', 'F', 'G'],
    ':': []  # For the separator between minutes and seconds
}

# Load Sound Effects
def load_sound(file_name):
    """Loads a sound file."""
    try:
        sound = pygame.mixer.Sound(file_name)
        return sound
    except pygame.error as e:
        print(f"Failed to load sound {file_name}: {e}")
        return None

# Ensure you have these sound files in the same directory as your script
BUTTON_PRESS_SOUND = load_sound('button_press.mp3')  # Sound for button presses
EXPLOSION_SOUND = load_sound('explosion.mp3')      # Sound for explosions
SUCCESS_SOUND = load_sound('success_sound.mp3')          # Sound for successful defuse
COUNTDOWN_BEEP_SOUND = load_sound('countdown_beep.mp3')  # Countdown beep sound

# Function to draw a segment
def draw_segment(surface, segment, position, color):
    """Draws a single segment at the given position."""
    translated = [(x + position[0], y + position[1]) for (x, y) in segment]
    pygame.draw.polygon(surface, color, translated)

# Function to draw a digit
def draw_digit(surface, digit, top_left):
    """Draws a single digit in 7-segment style."""
    if digit not in DIGITS:
        digit = ' '  # Empty space for unknown characters

    # Character area
    digit_surface = pygame.Surface((80, 120), pygame.SRCALPHA)
    digit_surface.fill(BLACK)

    # Draw segments
    for seg in SEGMENTS:
        if seg in DIGITS[digit]:
            color = SEGMENT_ON
        else:
            color = SEGMENT_OFF
        draw_segment(digit_surface, SEGMENTS[seg], (0, 0), color)

    surface.blit(digit_surface, top_left)

# Function to draw the LCD time display
def draw_lcd_time(surface, time_str, pos):
    """Draws the time as a 7-segment LCD."""
    x, y = pos
    for char in time_str:
        if char == ':':
            # Draw a simple dot as separator
            pygame.draw.circle(surface, SEGMENT_ON, (x + 40, y + 60), 5)
            x += 20  # Space between characters
            continue
        draw_digit(surface, char, (x, y))
        x += 80  # Space between digits

# Function to draw the number pad and input field
def draw_buttons(current_input):
    """Draws the number pad and the input field."""
    for row_idx, row in enumerate(NUM_PAD):
        for col_idx, label in enumerate(row):
            x = START_X + col_idx * (BUTTON_WIDTH + BUTTON_PADDING)
            y = START_Y + row_idx * (BUTTON_HEIGHT + BUTTON_PADDING)
            rect = pygame.Rect(x, y, BUTTON_WIDTH, BUTTON_HEIGHT)
            pygame.draw.rect(WINDOW, GRAY, rect, border_radius=5)
            # Button text
            if label in ['CLR', 'ENT']:
                text_color = RED if label == 'CLR' else GREEN
            else:
                text_color = WHITE
            text = FONT.render(label, True, text_color)
            text_rect = text.get_rect(center=rect.center)
            WINDOW.blit(text, text_rect)
    
    # Input field
    input_rect = pygame.Rect(START_X, START_Y - 70, 3 * BUTTON_WIDTH + 2 * BUTTON_PADDING, 60)  # Adjusted position
    pygame.draw.rect(WINDOW, GRAY, input_rect, border_radius=5)
    
    # Prompt text
    prompt_text = FONT.render("Code:", True, WHITE)  # Removed "(4)"
    WINDOW.blit(prompt_text, (input_rect.x + 10, input_rect.y + 10))
    
    # Prepare input display with underscores for missing digits
    display_input = current_input.ljust(4, '_')
    input_text = FONT.render(display_input, True, WHITE)
    WINDOW.blit(input_text, (input_rect.x + 100, input_rect.y + 10))  # Adjusted position to prevent overlay

# Particle class for confetti effect
class Particle:
    """A simple particle class for confetti effect."""
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.size = random.randint(5, 10)
        self.color = random.choice([
            (255, 0, 0),    # Red
            (0, 255, 0),    # Green
            (0, 0, 255),    # Blue
            (255, 255, 0),  # Yellow
            (255, 0, 255),  # Magenta
            (0, 255, 255),  # Cyan
            (255, 165, 0),  # Orange
            (128, 0, 128),  # Purple
        ])
        self.vel_x = random.uniform(-3, 3)
        self.vel_y = random.uniform(-5, -1)
        self.lifetime = random.randint(50, 100)  # Frames
        self.age = 0

    def update(self):
        """Updates the particle's position and age."""
        self.x += self.vel_x
        self.y += self.vel_y
        self.age += 1
        # Apply gravity
        self.vel_y += 0.1
        # Fade out
        if self.age >= self.lifetime:
            return False  # Particle is dead
        return True

    def draw(self, surface):
        """Draws the particle on the given surface."""
        alpha = max(255 - int((self.age / self.lifetime) * 255), 0)
        surface_with_alpha = pygame.Surface((self.size, self.size), pygame.SRCALPHA)
        pygame.draw.rect(surface_with_alpha, (*self.color, alpha), (0, 0, self.size, self.size))
        surface.blit(surface_with_alpha, (self.x, self.y))

# Function to show explosion animation with sound
def show_explosion():
    """Simple explosion animation with 'BUMM' overlay and explosion sound."""
    if EXPLOSION_SOUND:
        EXPLOSION_SOUND.play()  # Play explosion sound

    start_time = time.time()

    while time.time() - start_time < EXPLOSION_DURATION:
        elapsed = time.time() - start_time
        WINDOW.fill(BLACK)
        
        # Explosion circles expanding and fading
        alpha = max(255 - int((elapsed / EXPLOSION_DURATION) * 255), 0)
        explosion_color = (255, alpha // 2, 0)  # Orange with fading
        
        for i in range(1, 6):
            radius = int(50 + i * 40 + elapsed * 100)  # Adjusted expansion speed
            if radius > max(WIDTH, HEIGHT):
                continue
            temp_surface = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
            pygame.draw.circle(temp_surface, explosion_color, (WIDTH//2, HEIGHT//2), radius)
            WINDOW.blit(temp_surface, (0, 0))
        
        # Render "BUMM" with animated fire colors
        color_cycle = [(255, 69, 0), (255, 140, 0), (255, 215, 0), (255, 255, 0)]  # Fire-like colors
        color_index = int(elapsed * 10) % len(color_cycle)
        bumm_color = color_cycle[color_index]
        bumm_text = BUMM_FONT.render("BUMM", True, bumm_color)
        bumm_rect = bumm_text.get_rect(center=(WIDTH//2, HEIGHT//2))
        WINDOW.blit(bumm_text, bumm_rect)
        
        pygame.display.flip()
        
        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit()
        
        pygame.time.delay(30)

# Function to reset the game to initial state
def reset_game():
    """Resets the game to initial state."""
    global countdown, current_input, status, countdown_active
    countdown = TOTAL_TIME
    current_input = ""
    status = STATUS_COUNTDOWN
    countdown_active = True

# Function to load all sound effects
def load_sounds():
    """Loads all required sound effects."""
    sounds = {
        'button_press': load_sound('button_press.mp3'),
        'explosion': load_sound('explosion.mp3'),
        'success': load_sound('success_sound.mp3'),
        'countdown_beep': load_sound('countdown_beep.mp3')
    }
    return sounds

# Load all sounds
SOUNDS = load_sounds()

# Function to play a sound with a check
def play_sound(sound):
    """Plays a sound if it exists."""
    if sound:
        sound.play()

# Initialize game variables
countdown = TOTAL_TIME
current_input = ""
status = STATUS_COUNTDOWN
countdown_active = True
last_tick = time.time()

# Variables for countdown beeps
last_beep = time.time()

# Variables for success message
confetti_particles = []
success_start_time = None
flash_timer = 0
flash_interval = 0.1  # Seconds
hue = 0

# Main game loop
while True:
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()
        elif event.type == MOUSEBUTTONDOWN and status == STATUS_COUNTDOWN:
            mouse_x, mouse_y = event.pos
            # Check if a button was pressed
            for row_idx, row in enumerate(NUM_PAD):
                for col_idx, label in enumerate(row):
                    x = START_X + col_idx * (BUTTON_WIDTH + BUTTON_PADDING)
                    y = START_Y + row_idx * (BUTTON_HEIGHT + BUTTON_PADDING)
                    rect = pygame.Rect(x, y, BUTTON_WIDTH, BUTTON_HEIGHT)
                    if rect.collidepoint(mouse_x, mouse_y):
                        # Play button press sound
                        play_sound(SOUNDS['button_press'])
                        
                        if label == 'CLR':
                            current_input = ""
                        elif label == 'ENT':
                            if len(current_input) == 4:
                                if current_input == SECRET_CODE:
                                    # Correct code entered
                                    print("Code korrekt! Countdown gestoppt.")
                                    play_sound(SOUNDS['success'])  # Play success sound
                                    status = STATUS_SUCCESS
                                    success_start_time = time.time()
                                    # Initialize confetti particles
                                    for _ in range(100):  # Number of confetti particles
                                        confetti_particles.append(Particle(WIDTH//2, HEIGHT//2))
                                else:
                                    # Incorrect code entered
                                    show_explosion()
                                    reset_game()
                        else:
                            if len(current_input) < 4:
                                current_input += label
                        break

    if status == STATUS_COUNTDOWN:
        current_time = time.time()
        # Play countdown beep every second
        if SOUNDS['countdown_beep'] and (current_time - last_beep >= 1):
            play_sound(SOUNDS['countdown_beep'])
            last_beep = current_time

        # Update the countdown every second if active
        if countdown_active and (current_time - last_tick >= 1):
            countdown -= 1
            last_tick = current_time
            if countdown <= 0:
                # Time's up
                show_explosion()
                reset_game()
        
        # Draw the screen
        WINDOW.fill(BLACK)
        
        # Draw the countdown
        minutes = countdown // 60
        seconds = countdown % 60
        time_str = f"{minutes:02}:{seconds:02}"
        draw_lcd_time(WINDOW, time_str, (WIDTH//2 - 160, 50))
        
        # Draw the number pad and input field
        draw_buttons(current_input)

    elif status == STATUS_SUCCESS:
        # Calculate elapsed time since success
        elapsed_success = time.time() - success_start_time
        
        # Update confetti particles
        confetti_particles = [p for p in confetti_particles if p.update()]
        
        # Spawn new particles
        for _ in range(5):  # Number of new particles per frame
            confetti_particles.append(Particle(WIDTH//2, HEIGHT//2))
        
        # Handle flashing rainbow colors
        flash_timer += 1 / 60  # Assuming 60 FPS
        if flash_timer >= flash_interval:
            flash_timer = 0
            hue = (hue + 0.05) % 1  # Increment hue
        rainbow_color = colorsys.hsv_to_rgb(hue, 1, 1)
        rainbow_color = tuple(int(c * 255) for c in rainbow_color)
        
        # Draw the screen
        WINDOW.fill(BLACK)
        
        # Draw confetti
        for particle in confetti_particles:
            particle.draw(WINDOW)
        
        # Render success message with flashing color
        success_text = "Herzlichen Glückwunsch, ihr habt die Bombe entschärft!"
        success_surface = SUCCESS_FONT.render(success_text, True, rainbow_color)
        success_rect = success_surface.get_rect(center=(WIDTH//2, HEIGHT//2))
        WINDOW.blit(success_surface, success_rect)
        
    elif status == STATUS_EXPLOSION:
        # Explosion status is handled within the show_explosion function
        pass  # No additional handling needed here

    pygame.display.flip()
    pygame.time.Clock().tick(60)  # 60 FPS


When the success message is displayed in above game code. In the line below, display 7  bold arrows pointing in the following direction: down, up, down, up, left, right, left.