import asyncio
import pygame
import random
import sys
import os

from pygame import FULLSCREEN

# === Konstanten ===
SCREEN_WIDTH = 1920
SCREEN_HEIGHT = 1080
FPS = 60

TOP_BAR_HEIGHT = SCREEN_HEIGHT // 8
GAME_AREA_HEIGHT = SCREEN_HEIGHT // 1.6
BOTTOM_BAR_HEIGHT = SCREEN_HEIGHT // 4

TRAILER_WIDTH = int(SCREEN_WIDTH * 0.885416666667)
TRAILER_HEIGHT = int(SCREEN_HEIGHT * 0.2778)
TRAILER_X = (SCREEN_WIDTH - TRAILER_WIDTH) // 4
TRAILER_Y = TOP_BAR_HEIGHT + 200

CASE_SPEED_SLOW = 3
CASE_SPEED_FAST = 8
CASE_COLORS = [(200, 0, 0), (0, 200, 0), (0, 0, 200)]
SNAP_THRESHOLD = 60
FAST_TO_SLOW_DISTANCE = 120

UNIT_LENGTH = TRAILER_HEIGHT // 3
UNIT_HEIGHT = TRAILER_HEIGHT // 3

# === Globale Variablen ===
current_case_visible = True
transition_fps = 60
transition_counter = 0
collision_x = TRAILER_X
stacked_cases = []
case_sequence = []
case_index = 0
current_case = None
next_queue = []
running = True
can_use_tilt = True
can_use_on_top = True
game_finished = False
shake_timer = 0
state = 1  # PLAYING
prepared_form = None
ready_to_submit = False


def init_sounds():
    global sound_fail, sound_place, sound_roll, sound_tut1, sound_tut2, sound_tuuut, zip_sound
    pygame.mixer.init()
    sound_fail = pygame.mixer.Sound("sounds/fail.ogg")
    sound_fail.set_volume(0.6)
    sound_place = pygame.mixer.Sound("sounds/place.ogg")
    sound_place.set_volume(0.6)
    sound_roll = pygame.mixer.Sound("sounds/roll.ogg")
    sound_roll.set_volume(0.3)
    sound_tut1 = pygame.mixer.Sound("sounds/tut1.ogg")
    sound_tut1.set_volume(1)
    sound_tut2 = pygame.mixer.Sound("sounds/tut2.ogg")
    sound_tut2.set_volume(1)
    sound_tuuut = pygame.mixer.Sound("sounds/tuuut.ogg")
    sound_tuuut.set_volume(1)
    zip_sound = pygame.mixer.Sound("sounds/zip.ogg")
    zip_sound.set_volume(1)
    print("Sounds erfolgreich geladen.")

case_images = {}

async def init_game():
    global screen, clock, font, controls
    pygame.init()
    pygame.joystick.init()
    init_sounds()

    if pygame.joystick.get_count() > 0:
        joystick = pygame.joystick.Joystick(0)
        joystick.init()
        print(f"Controller erkannt: {joystick.get_name()}")
        controls = "tutorial_controller"
    else:
        controls = "tutorial"
        print("Kein Controller gefunden.")

    screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
    pygame.display.set_caption("Habegger Loader Game")
    clock = pygame.time.Clock()
    font = pygame.font.SysFont(None, 48)

class Case:
    def __init__(self, size=None):
        global case_index
        self.rotated = False
        if not case_sequence:
            generate_fill_sequence()
        if size is None:
            size = case_sequence[case_index % len(case_sequence)]
            case_index += 1
        self.length_units, self.height_units = size['w'], size['h']
        if size['rotated']:
            self.length_units, self.height_units = self.height_units, self.length_units
        elif self.height_units * UNIT_LENGTH <= TRAILER_WIDTH and self.length_units * UNIT_HEIGHT <= TRAILER_HEIGHT and random.choice(
                [True, False]):
            self.length_units, self.height_units = self.height_units, self.length_units
        self.width = self.length_units * UNIT_LENGTH
        self.height = self.height_units * UNIT_HEIGHT
        self.x = SCREEN_WIDTH
        self.spawn_y = TRAILER_Y + TRAILER_HEIGHT - self.height
        self.y = self.spawn_y
        self.entry_angle = 0
        self.color = random.choice(CASE_COLORS)
        self.placed = False
        self.allow_snap = False
        self.snapped = False
        self.occupied = False
        self.rotated = False   # <==== WICHTIG: HINZUFÜGEN!!!
        global current_case_visible
        current_case_visible = True

    def move(self):
        global collision_x
        if not self.placed and not pygame.mixer.Channel(1).get_busy():
            if sound_roll:
                pygame.mixer.Channel(1).play(sound_roll, loops=-1)
        distance_to_obstacle = self.x - collision_x
        speed = CASE_SPEED_FAST if distance_to_obstacle > FAST_TO_SLOW_DISTANCE else CASE_SPEED_SLOW
        self.x -= speed
        if self.x <= collision_x + SNAP_THRESHOLD:
            self.allow_snap = True

    async def fail_snap(self):
        global collision_x
        steps = 5
        bounce_distance = 20  # Pixel zurückspringen

        for _ in range(steps):
            self.x += bounce_distance / steps
            draw_game()
            pygame.display.flip()
            await asyncio.sleep(0)

        self.placed = True
        self.snapped = True
        collision_x = self.x + self.width

    def can_place_on_top(self):
        if self.placed:
            return False
        for base in reversed(stacked_cases):
            if base.y <= TRAILER_Y:
                continue
            height_ok = base.y - self.height >= TRAILER_Y
            width_ok = self.width <= base.width
            aligned_with_barrier = abs(base.x + base.width - collision_x) < 5
            space_free = all(not (
                    other.y + other.height == base.y and
                    other.x < base.x + base.width and
                    other.x + other.width > base.x
            ) for other in stacked_cases)
            if height_ok and width_ok and aligned_with_barrier and space_free:
                return True
        return False

    def collides_with_barrier(self):
        return not self.snapped and self.x <= collision_x

    def draw(self, surface=None):
        global screen
        surface = surface or screen
        angle = getattr(self, 'entry_angle', 0)
        if self.rotated:
            key = f"case_w{self.height_units}_h{self.length_units}"
        else:
            key = f"case_w{self.length_units}_h{self.height_units}"
        image = case_images.get(key)
        if image:
            if self.rotated:
                image_to_draw = pygame.transform.scale(image, (self.height, self.width))
            else:
                image_to_draw = pygame.transform.scale(image, (self.width, self.height))
            if self.rotated:
                image_to_draw = pygame.transform.rotate(image_to_draw, 90)
            if angle != 0:
                image_to_draw = pygame.transform.rotate(image_to_draw, angle)
                rect = image_to_draw.get_rect(center=(self.x + self.width // 2, self.y + self.height // 3))
                surface.blit(image_to_draw, rect)
            else:
                surface.blit(image_to_draw, (self.x, self.y))
        else:
            pygame.draw.rect(surface, self.color, (self.x, self.y, self.width, self.height))
    async def animate_snap(self):
        if sound_roll:
            pygame.mixer.Channel(1).stop()
        global collision_x
        target_x = collision_x
        steps = 10
        delta = (self.x - target_x) / steps
        for _ in range(steps):
            self.x -= delta
            draw_game()
            pygame.display.flip()
            await asyncio.sleep(0)
        self.x = target_x
        self.placed = True
        self.snapped = True
        collision_x = self.x + self.width

    async def animate_tip(self):
        global current_case_visible
        current_case_visible = False
        key = f"case_w{self.length_units}_h{self.height_units}"
        image = case_images.get(key)
        if image:
            original = pygame.transform.scale(image, (self.width, self.height)).convert_alpha()
        else:
            original = pygame.Surface((self.width, self.height), pygame.SRCALPHA)
            pygame.draw.rect(original, self.color, (0, 0, self.width, self.height))
        steps = 10
        for step in range(steps + 1):
            angle = step * (90 / steps)
            rotated = pygame.transform.rotate(original, angle)
            new_rect = rotated.get_rect(bottomleft=(self.x, self.y + self.height))
            draw_game()
            screen.blit(rotated, new_rect)
            pygame.display.flip()
            await asyncio.sleep(0)
        self.width, self.height = self.height, self.width
        self.length_units, self.height_units = self.height_units, self.length_units
        self.rotated = not getattr(self, 'rotated', False)
        self.x = max(collision_x + SNAP_THRESHOLD // 2, self.x)
        self.y = TRAILER_Y + TRAILER_HEIGHT - self.height
        current_case_visible = True

    async def animate_place_on_top(self):
        if sound_roll:
            pygame.mixer.Channel(1).stop()
        global current_case_visible, collision_x
        for base in reversed(stacked_cases):
            height_ok = base.y - self.height >= TRAILER_Y
            width_ok = self.width <= base.width
            space_free = all(not (
                other.y + other.height == base.y and
                other.x < base.x + base.width and
                other.x + other.width > base.x
            ) for other in stacked_cases)
            if height_ok and width_ok and space_free:
                start_y = self.y
                target_y = base.y - self.height
                steps = 10
                for step in range(steps):
                    self.y = start_y - ((start_y - target_y) * (step + 1) / steps)
                    draw_game()
                    pygame.display.flip()
                    await asyncio.sleep(0)
                self.y = target_y
                start_x = self.x
                target_x = base.x + base.width - self.width
                for step in range(steps):
                    self.x = start_x - ((start_x - target_x) * (step + 1) / steps)
                    draw_game()
                    pygame.display.flip()
                    await asyncio.sleep(0)
                self.x = target_x
                self.placed = True
                self.snapped = True
                collision_x = self.x + self.width
                return True
        current_case_visible = True
        return False

def load_case_images():
    global case_images
    for w in range(1, 5):
        for h in range(1, 4):
            key = f"case_w{w}_h{h}"
            path = f"images/{key}.png"
            if os.path.exists(path):
                case_images[key] = pygame.image.load(path).convert_alpha()

async def show_start_sequence():
    global zip_sound
    center_x = TRAILER_X + TRAILER_WIDTH // 2
    center_y = TRAILER_Y + TRAILER_HEIGHT // 2
    colors = [(255, 0, 0), (255, 200, 0), (0, 255, 0)]
    delays = [1000, 1000, 1000]  # ms pro Punkt
    sounds = [sound_tut1, sound_tut2, sound_tuuut]
    radius = 90

    for i in range(3):
        await asyncio.sleep(0)
        draw_game(hide_trailer=True)
        pygame.draw.circle(screen, colors[i], (center_x, center_y), radius)
        sounds[i].play()
        pygame.display.flip()
        pygame.time.delay(delays[i])

    # Reißverschluss-Effekt
    zip_sound.play()
    duration = 200
    steps = 60
    for step in range(steps + 1):
        await asyncio.sleep(0)
        width = int(TRAILER_WIDTH * (step / steps))
        draw_game(hide_trailer=True)
        pygame.draw.rect(screen, (100, 100, 100), (TRAILER_X, TRAILER_Y, width, TRAILER_HEIGHT))
        pygame.display.flip()
        pygame.time.delay(duration // steps)

def load_instruction_image():
    global controls
    try:
        image = pygame.image.load("images/"+controls+".png")
        return pygame.transform.scale(image, (SCREEN_WIDTH, SCREEN_HEIGHT))
    except:
        return None

def load_background():
    try:
        image = pygame.image.load("images/background.png")
        return pygame.transform.scale(image, (SCREEN_WIDTH, GAME_AREA_HEIGHT))
    except:
        return None

def draw_game(hide_trailer=False):
    global shake_timer
    offset_x = random.randint(-5, 5) if shake_timer > 0 else 0
    offset_y = random.randint(-5, 5) if shake_timer > 0 else 0
    if shake_timer > 0:
        shake_timer -= 1
    screen.fill((0, 0, 0))
    pygame.draw.rect(screen, (0, 0, 0), (0, 0, SCREEN_WIDTH, TOP_BAR_HEIGHT))
    title_font = pygame.font.SysFont(None, 64)
    title_surface = title_font.render("Habegger Loader Game", True, (255, 255, 255))
    screen.blit(title_surface, (50, 50))

    # === Ladebalken zeichnen ===
    percent_full = calculate_load_percent()
    bar_width = 400
    bar_height = 30
    bar_x = SCREEN_WIDTH - bar_width - 50
    bar_y = 50
    pygame.draw.rect(screen, (80, 80, 80), (bar_x, bar_y, bar_width, bar_height))
    pygame.draw.rect(screen, (0, 255, 0), (bar_x, bar_y, bar_width * (percent_full / 100), bar_height))
    bar_font = pygame.font.SysFont(None, 36)
    bar_text = bar_font.render(f"{int(percent_full)}% geladen", True, (255, 255, 255))
    screen.blit(bar_text, (bar_x + bar_width // 2 - bar_text.get_width() // 2, bar_y + bar_height // 2 - bar_text.get_height() // 2))

    # === Vorschau nächstes Case (nur Text) ===
    preview_box_width = 150
    preview_box_height = 80
    preview_box_x = (SCREEN_WIDTH - preview_box_width) // 2
    preview_box_y = SCREEN_HEIGHT - preview_box_height - 20

    # Grauer Hintergrundkasten
    pygame.draw.rect(screen, (80, 80, 80), (preview_box_x, preview_box_y, preview_box_width, preview_box_height),
                     border_radius=12)

    # Überschrift: "B x H"
    title_font = pygame.font.SysFont(None, 28)
    title_surface = title_font.render("Next: (B x H)", True, (200, 200, 200))
    title_x = preview_box_x + (preview_box_width - title_surface.get_width()) // 2
    title_y = preview_box_y + 5
    screen.blit(title_surface, (title_x, title_y))

    # Text für nächstes Case (z.B. "2x1")
    if next_queue:
        next_case = next_queue[0]
        preview_text = f"{next_case['w']}x{next_case['h']}"
        preview_font = pygame.font.SysFont(None, 48)
        text_surface = preview_font.render(preview_text, True, (255, 255, 255))
        text_x = preview_box_x + (preview_box_width - text_surface.get_width()) // 2
        text_y = title_y + title_surface.get_height() + 5
        screen.blit(text_surface, (text_x, text_y))

    # === Echte Vorschau der nächsten 3 Cases (horizontal in Top-Bar) ===
    preview_start_x = 50
    preview_start_y = SCREEN_HEIGHT - 200
    spacing_between_cases = 80  # Fester Abstand zwischen den Vorschau-Cases
    title_next = title_font.render("Next 3:", True, (200, 200, 200))
    title_x = 50
    title_y = SCREEN_HEIGHT - 250
    screen.blit(title_next, (title_x, title_y))

    current_x = preview_start_x

    for i, case_data in enumerate(next_queue[:3]):
        preview_width = case_data['w'] * (UNIT_LENGTH // 2)
        preview_height = case_data['h'] * (UNIT_HEIGHT // 2)
        preview_case_surface = pygame.Surface((preview_width, preview_height), pygame.SRCALPHA)

        # Bild laden oder Rechteck zeichnen
        key = f"case_w{case_data['w']}_h{case_data['h']}"
        image = case_images.get(key)

        if image:
            image = pygame.transform.scale(image, (preview_width, preview_height))
            preview_case_surface.blit(image, (0, 0))
        else:
            pygame.draw.rect(preview_case_surface, (180, 180, 180), (0, 0, preview_width, preview_height))

        preview_y = preview_start_y

        # Blit auf Hauptscreen
        screen.blit(preview_case_surface, (current_x, preview_y))

        # Nächste X-Position anpassen
        current_x += preview_width + spacing_between_cases

    if background_image:
        screen.blit(background_image, (0, TOP_BAR_HEIGHT))
    else:
        pygame.draw.rect(screen, (0, 0, 0), (0, TOP_BAR_HEIGHT, SCREEN_WIDTH, GAME_AREA_HEIGHT))
    if not hide_trailer:
        pygame.draw.rect(screen, (100, 100, 100),
                         (TRAILER_X + offset_x, TRAILER_Y + offset_y, TRAILER_WIDTH, TRAILER_HEIGHT))
    for c in stacked_cases:
        c.draw(screen)
    if current_case and current_case_visible:
        current_case.draw(screen)

async def show_instruction_screen(image):
    if not image:
        return
    waiting = True
    while waiting:
        screen.blit(image, (0, 0))
        pygame.display.flip()
        await asyncio.sleep(0)
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
            elif (event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE) or \
                 (event.type == pygame.JOYBUTTONDOWN and event.button == 0):
                waiting = False

async def fail_current_case():
    global can_use_tilt, can_use_on_top, game_finished, shake_timer
    await current_case.fail_snap()
    if current_case.x + current_case.width > TRAILER_X + TRAILER_WIDTH:
        if stacked_cases and stacked_cases[-1] == current_case:
            stacked_cases.pop()
    else:
        stacked_cases.append(current_case)
    can_use_tilt = True
    can_use_on_top = True
    if sound_fail:
        sound_fail.play()
    spawn_case_in_game()
    shake_timer = 10

def generate_fill_sequence():
    global case_sequence
    try:
        with open("case_sequences.txt", "r") as f:
            sets = f.read().strip().split("\n\n")
            chosen = random.choice(sets).strip().split("\n")
            sequence = []
            for line in chosen:
                parts = line.strip().split(",")
                if len(parts) == 3:
                    w, h, rot = int(parts[0]), int(parts[1]), parts[2].strip().lower() == 'true'
                    sequence.append({'w': w, 'h': h, 'rotated': rot})
            case_sequence = sequence
    except:
        case_sequence = [{'w': 2, 'h': 1, 'rotated': False}, {'w': 1, 'h': 2, 'rotated': False}]

def spawn_case_in_game():
    global current_case, next_queue, case_index
    if not case_sequence:
        generate_fill_sequence()

    while len(next_queue) < 3:
        index = (case_index + len(next_queue)) % len(case_sequence)
        next_queue.append(case_sequence[index])

    if case_index >= len(case_sequence):
        current_case = None
        return
    case_data = case_sequence[case_index % len(case_sequence)]
    case_index += 1

    if next_queue:
        next_queue.pop(0)
    while len(next_queue) < 3:
        index = (case_index + len(next_queue)) % len(case_sequence)
        next_queue.append(case_sequence[index])

    current_case = Case(size=case_data)

async def show_game_over(score):
    import sys
    import urllib.parse

    try:
        import js
    except ImportError:
        js = None
    username = "Spieler"
    data = {
        "name": username,
        "score": int(score),
        "api_key": "hag-trailer-8051"
    }

    def is_browser():
        return sys.platform in ("emscripten", "wasi")

    if is_browser():
        global prepared_form, ready_to_submit

        if not is_browser() or js is None:
            return


        try:
            query = js.window.location.search
            params = urllib.parse.parse_qs(query[1:])
            username = params.get("name", ["Spieler"])[0]
        except Exception:
            pass

        try:
            form = js.document.createElement("form")
            form.method = "POST"
            form.action = "https://hag-game.carabella.ch/submit_points.php"

            for key, value in data.items():
                input_field = js.document.createElement("input")
                input_field.type = "hidden"
                input_field.name = key
                input_field.value = str(value)
                form.appendChild(input_field)

            js.document.body.appendChild(form)

            prepared_form = form
            ready_to_submit = True

            print("Formular vorbereitet. Warten auf Bestätigung (SPACE oder Klick).")

        except Exception as e:
            print("Fehler beim Formular vorbereiten:", e)


    else:
        try:
            import urllib.request
            body = urllib.parse.urlencode(data).encode()
            req = urllib.request.Request(
                url="https://hag-game.carabella.ch/submit_points.php",
                data=body,
                method="POST",
                headers={
                    "Content-Type": "application/x-www-form-urlencoded",
                }
            )
            with urllib.request.urlopen(req) as response:
                if response.getcode() != 200:
                    raise Exception("HTTP error")
            print("Punkte erfolgreich lokal gesendet!")
        except Exception as e:
            print("Fehler beim lokalen Senden:", e)

    pygame.mixer.stop()
    overlay = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT))
    overlay.set_alpha(220)
    overlay.fill((0, 0, 0))
    screen.blit(overlay, (0, 0))
    font_big = pygame.font.SysFont(None, 72)
    message = f"Spiel beendet! Punkte: {int(score)}%"
    text = font_big.render(message, True, (255, 255, 255))
    screen.blit(text, (SCREEN_WIDTH // 2 - text.get_width() // 2, SCREEN_HEIGHT // 2 - text.get_height() // 2))
    pygame.display.flip()

    waiting = True
    while waiting:
        await asyncio.sleep(0)
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
            elif (
                    (event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE) or
                    (event.type == pygame.JOYBUTTONDOWN and event.button == 0)
            ):
                if ready_to_submit and prepared_form:
                    print("Benutzer bestätigt, Punkte werden jetzt gesendet.")
                    prepared_form.submit()
                pygame.quit()
                sys.exit()

def calculate_load_percent():
    total_volume = TRAILER_WIDTH * TRAILER_HEIGHT
    used_volume = sum([c.width * c.height for c in stacked_cases])
    return min(100, (used_volume / total_volume) * 100)

async def main():
    global running, can_use_tilt, can_use_on_top, game_finished
    global current_case, transition_counter, state
    global screen, clock, font, instruction_image, background_image

    await init_game()
    load_case_images()
    instruction_image = load_instruction_image()
    background_image = load_background()
    generate_fill_sequence()

    await asyncio.sleep(1)
    await show_instruction_screen(instruction_image)
    await show_start_sequence()
    spawn_case_in_game()

    running = True
    can_use_tilt = True
    can_use_on_top = True
    game_finished = False

    while running:
        if not game_finished and collision_x > TRAILER_X + TRAILER_WIDTH:
            await show_game_over(calculate_load_percent())
            game_finished = True
            continue
        if not game_finished and current_case is None and case_index >= len(case_sequence):
            await show_game_over(calculate_load_percent())
            game_finished = True
            continue

        clock.tick(FPS)
        await asyncio.sleep(0)

        if transition_counter > 0:
            transition_counter -= 1
            draw_game()
            pygame.display.flip()
            await asyncio.sleep(0)
            continue

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
            elif event.type == pygame.KEYDOWN or event.type == pygame.JOYBUTTONDOWN or event.type == pygame.JOYHATMOTION:
                if state == 1:  # PLAYING
                    if ((event.type == pygame.KEYDOWN and event.key == pygame.K_LEFT) or
                        (event.type == pygame.JOYHATMOTION and event.value[0] == -1) or
                        (event.type == pygame.JOYBUTTONDOWN and event.button == 2)):
                        can_tip = current_case.length_units * UNIT_HEIGHT <= TRAILER_HEIGHT
                        if current_case and current_case.allow_snap and can_use_tilt and can_tip:
                            await current_case.animate_tip()
                            can_use_tilt = False
                    elif ((event.type == pygame.KEYDOWN and event.key == pygame.K_UP) or
                          (event.type == pygame.JOYHATMOTION and event.value[1] == 1) or
                          (event.type == pygame.JOYBUTTONDOWN and event.button == 3)):
                        if current_case and current_case.allow_snap and can_use_on_top and current_case.can_place_on_top():
                            if await current_case.animate_place_on_top():
                                transition_counter = transition_fps
                                if sound_place:
                                    sound_place.play()
                                stacked_cases.append(current_case)
                                spawn_case_in_game()
                                can_use_tilt = True
                                can_use_on_top = True
                            else:
                                await current_case.fail_snap()
                                if sound_place:
                                    sound_place.play()
                                stacked_cases.append(current_case)
                                spawn_case_in_game()
                                shake_timer = 10
                    elif ((event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE) or
                          (event.type == pygame.JOYBUTTONDOWN and event.button == 0)):
                        if current_case and current_case.allow_snap:
                            await current_case.animate_snap()
                            can_use_tilt = True
                            can_use_on_top = True
                            if sound_place:
                                sound_place.play()
                            transition_counter = transition_fps
                            stacked_cases.append(current_case)
                            spawn_case_in_game()

        if state == 1:  # PLAYING
            if current_case is None:
                continue
            if current_case:
                current_case.move()
                if current_case.collides_with_barrier():
                    await fail_current_case()
                if not game_finished:
                    can_tip = current_case.length_units * UNIT_HEIGHT <= TRAILER_HEIGHT
                    if collision_x + current_case.width > TRAILER_X + TRAILER_WIDTH and not current_case.can_place_on_top() and not current_case.allow_snap and not can_tip:
                        await show_game_over(calculate_load_percent())
                        game_finished = True
                        continue
                    if calculate_load_percent() >= 99.9 and case_index >= len(case_sequence):
                        await show_game_over(100)
                        game_finished = True
                        continue
                if not current_case.placed and current_case.collides_with_barrier():
                    await current_case.fail_snap()
                    if current_case.x + current_case.width > TRAILER_X + TRAILER_WIDTH:
                        if stacked_cases and stacked_cases[-1] == current_case:
                            stacked_cases.pop()
                    else:
                        stacked_cases.append(current_case)
                    can_use_tilt = True
                    can_use_on_top = True
                    if sound_fail:
                        sound_fail.play()
                    spawn_case_in_game()
                    shake_timer = 10
            draw_game()

        pygame.display.flip()

    pygame.quit()
    sys.exit()

# === Spielstart ===
if __name__ == "__main__":
    asyncio.run(main())
