"""
Snake_game bu Alvan
"""

import pygame
import random
import sys
import os

# -------------------------- 基础配置 --------------------------
WIDTH = 600
HEIGHT = 600
BLOCK_SIZE = 20

# 难度对应速度（数字越大蛇移动越快）
LEVEL_SPEEDS = {
    1: 10,    # Level1 最慢
    2: 15,   # Level2 正常速度
    3: 22    # Level3 较快速度
}

# 颜色定义
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
GRAY = (180, 180, 180)
OBSTACLE_COLOR = (139, 69, 19)  # 马鞍棕障碍物

# 场地总格子数量，用于胜利判定
TOTAL_BLOCKS = (WIDTH // BLOCK_SIZE) * (HEIGHT // BLOCK_SIZE)

# -------------------------- 资源加载（带容错） --------------------------
assets_dir = os.path.join(os.path.dirname(__file__), "assets")

# 四方向蛇头图片
snake_head_up = None
snake_head_down = None
snake_head_left = None
snake_head_right = None

# 蛇身、食物、障碍物图片
snake_body_img = None
food_img = None
obstacle_img = None

# 音效
eat_sound = None
game_over_sound = None
win_sound = None

def load_assets():
    """加载所有素材，加载失败不影响游戏运行"""
    global snake_head_up, snake_head_down, snake_head_left, snake_head_right
    global snake_body_img, food_img, obstacle_img, eat_sound, game_over_sound, win_sound

    # 加载图片资源
    try:
        # 四方向蛇头
        up_path = os.path.join(assets_dir, "snake_head_up.png")
        if os.path.exists(up_path):
            snake_head_up = pygame.image.load(up_path).convert_alpha()
            snake_head_up = pygame.transform.scale(snake_head_up, (BLOCK_SIZE, BLOCK_SIZE))
        
        down_path = os.path.join(assets_dir, "snake_head_down.png")
        if os.path.exists(down_path):
            snake_head_down = pygame.image.load(down_path).convert_alpha()
            snake_head_down = pygame.transform.scale(snake_head_down, (BLOCK_SIZE, BLOCK_SIZE))
        
        left_path = os.path.join(assets_dir, "snake_head_left.png")
        if os.path.exists(left_path):
            snake_head_left = pygame.image.load(left_path).convert_alpha()
            snake_head_left = pygame.transform.scale(snake_head_left, (BLOCK_SIZE, BLOCK_SIZE))
        
        right_path = os.path.join(assets_dir, "snake_head_right.png")
        if os.path.exists(right_path):
            snake_head_right = pygame.image.load(right_path).convert_alpha()
            snake_head_right = pygame.transform.scale(snake_head_right, (BLOCK_SIZE, BLOCK_SIZE))

        # 蛇身
        body_path = os.path.join(assets_dir, "snake_body.png")
        if os.path.exists(body_path):
            snake_body_img = pygame.image.load(body_path).convert_alpha()
            snake_body_img = pygame.transform.scale(snake_body_img, (BLOCK_SIZE, BLOCK_SIZE))
        
        # 食物
        food_path = os.path.join(assets_dir, "food.png")
        if os.path.exists(food_path):
            food_img = pygame.image.load(food_path).convert_alpha()
            food_img = pygame.transform.scale(food_img, (BLOCK_SIZE, BLOCK_SIZE))
        
        # 障碍物图片
        obs_path = os.path.join(assets_dir, "obstacle.png")
        if os.path.exists(obs_path):
            obstacle_img = pygame.image.load(obs_path).convert_alpha()
            obstacle_img = pygame.transform.scale(obstacle_img, (BLOCK_SIZE, BLOCK_SIZE))
    except:
        pass

    # 加载音效资源
    try:
        pygame.mixer.init()
        eat_path = os.path.join(assets_dir, "eat.wav")
        if os.path.exists(eat_path):
            eat_sound = pygame.mixer.Sound(eat_path)
        
        over_path = os.path.join(assets_dir, "game_over.wav")
        if os.path.exists(over_path):
            game_over_sound = pygame.mixer.Sound(over_path)
            
        win_path = os.path.join(assets_dir, "win.wav")
        if os.path.exists(win_path):
            win_sound = pygame.mixer.Sound(win_path)
    except:
        pass

# -------------------------- 游戏核心类 --------------------------
class SnakeGame:
    def __init__(self):
        pygame.init()
        # 关闭系统自带键盘长按延迟（关键优化1）
        pygame.key.set_repeat(0, 0)
        self.screen = pygame.display.set_mode((WIDTH, HEIGHT))
        pygame.display.set_caption("贪吃蛇 - WASD控制")
        self.clock = pygame.time.Clock()
        
        # 多级字体：标题、大标题、普通文本
        self.title_font = pygame.font.Font(None, 64)
        self.big_font = pygame.font.Font(None, 48)
        self.menu_font = pygame.font.Font(None, 32)
        self.font = pygame.font.Font(None, 25)
        
        # 加载素材
        load_assets()
        
        # 游戏状态：menu(菜单选难度) / playing(游戏中) / game_over(结束) / win(胜利)
        self.state = 'menu'
        # 默认难度为Level2
        self.current_level = 2
        self.speed = LEVEL_SPEEDS[self.current_level]
        
        # 障碍物列表、吃食物计数（每2个生成一组三连障碍）
        self.obstacles = []
        self.eat_count_for_obs = 0
        
        # 初始化游戏数据（菜单状态下不运行逻辑）
        self.reset_game()

    def reset_game(self):
        """重置游戏状态，保留当前难度，清空障碍物"""
        self.snake = [
            (WIDTH // 2, HEIGHT // 2),
            (WIDTH // 2 - BLOCK_SIZE, HEIGHT // 2),
            (WIDTH // 2 - 2 * BLOCK_SIZE, HEIGHT // 2)
        ]
        # 初始方向向右
        self.dx = BLOCK_SIZE
        self.dy = 0
        self.obstacles = []
        self.eat_count_for_obs = 0
        self.spawn_food()
        self.score = 0

    def get_all_occupied_pos(self):
        """获取所有被占用的格子：蛇身 + 障碍物"""
        occupied = set(self.snake)
        occupied.update(set(self.obstacles))
        return occupied

    def spawn_food(self):
        """随机生成食物，避开蛇身和障碍物；无空位则触发胜利"""
        occupied = self.get_all_occupied_pos()
        # 所有格子都被占满 → 游戏胜利
        if len(occupied) >= TOTAL_BLOCKS:
            self.state = "win"
            if win_sound:
                win_sound.play()
            return
        
        # 正常刷新食物
        while True:
            x = random.randrange(0, WIDTH - BLOCK_SIZE, BLOCK_SIZE)
            y = random.randrange(0, HEIGHT - BLOCK_SIZE, BLOCK_SIZE)
            pos = (x, y)
            if pos not in occupied:
                self.food = pos
                break

    def spawn_triple_obstacle(self):
        """生成一组3块相连障碍物，随机形状：横条/竖条/L型"""
        occupied = self.get_all_occupied_pos()
        occupied.add(self.food)
        grid_w = WIDTH // BLOCK_SIZE
        grid_h = HEIGHT // BLOCK_SIZE

        # 三种三连障碍物形状偏移量
        shapes = [
            [(0,0), (BLOCK_SIZE,0), (BLOCK_SIZE*2,0)],    # 横向直线
            [(0,0), (0,BLOCK_SIZE), (0,BLOCK_SIZE*2)],    # 纵向直线
            [(0,0), (BLOCK_SIZE,0), (0,BLOCK_SIZE)]       # L型拐角
        ]
        shape = random.choice(shapes)

        # 循环找合法生成点
        while True:
            # 随机基础坐标（保证不贴边界）
            base_x = random.randrange(BLOCK_SIZE, WIDTH - BLOCK_SIZE*2, BLOCK_SIZE)
            base_y = random.randrange(BLOCK_SIZE, HEIGHT - BLOCK_SIZE*2, BLOCK_SIZE)
            block_group = []
            conflict = False

            for dx, dy in shape:
                px = base_x + dx
                py = base_y + dy
                pos = (px, py)
                if pos in occupied:
                    conflict = True
                    break
                block_group.append(pos)
            
            if not conflict:
                # 无冲突，全部加入障碍物列表
                self.obstacles.extend(block_group)
                break

    def check_wall_collision(self, head_x, head_y):
        """穿墙逻辑：边界穿越"""
        if head_x < 0:
            head_x = WIDTH - BLOCK_SIZE
        elif head_x >= WIDTH:
            head_x = 0
        
        if head_y < 0:
            head_y = HEIGHT - BLOCK_SIZE
        elif head_y >= HEIGHT:
            head_y = 0
        
        return head_x, head_y

    def check_collision(self, head):
        """碰撞检测：自身、障碍物"""
        # 撞到自己
        if head in self.snake[1:]:
            return True
        # 撞到障碍物
        if head in self.obstacles:
            return True
        return False

    def handle_input(self):
        """修复无延迟输入：分事件+实时按键采样"""
        event_list = pygame.event.get()
        keys = pygame.key.get_pressed() # 实时读取所有按住的按键（消除长按延迟）

        # 全局退出事件
        for event in event_list:
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
            # 一次性按键触发（菜单/重试用）
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    pygame.quit()
                    sys.exit()
                # 菜单选难度
                if self.state == 'menu':
                    if event.key == pygame.K_1:
                        self.current_level = 1
                        self.speed = LEVEL_SPEEDS[1]
                        self.reset_game()
                        self.state = 'playing'
                    elif event.key == pygame.K_2:
                        self.current_level = 2
                        self.speed = LEVEL_SPEEDS[2]
                        self.reset_game()
                        self.state = 'playing'
                    elif event.key == pygame.K_3:
                        self.current_level = 3
                        self.speed = LEVEL_SPEEDS[3]
                        self.reset_game()
                        self.state = 'playing'
                # 结束/胜利界面重试
                elif self.state in ["game_over", "win"]:
                    if event.key == pygame.K_r:
                        self.reset_game()
                        self.state = 'playing'

        # 游戏中实时WASD控制（核心修复：实时采样无延迟）
        if self.state == "playing":
            # W上，禁止反向向下
            if keys[pygame.K_w] and self.dy != BLOCK_SIZE:
                self.dy = -BLOCK_SIZE
                self.dx = 0
            # S下，禁止反向向上
            elif keys[pygame.K_s] and self.dy != -BLOCK_SIZE:
                self.dy = BLOCK_SIZE
                self.dx = 0
            # A左，禁止反向向右
            elif keys[pygame.K_a] and self.dx != BLOCK_SIZE:
                self.dx = -BLOCK_SIZE
                self.dy = 0
            # D右，禁止反向向左
            elif keys[pygame.K_d] and self.dx != -BLOCK_SIZE:
                self.dx = BLOCK_SIZE
                self.dy = 0

    def update(self):
        """更新游戏逻辑，仅游戏中运行"""
        if self.state != 'playing':
            return

        head_x, head_y = self.snake[0]
        new_head = (head_x + self.dx, head_y + self.dy)
        new_head = self.check_wall_collision(*new_head)
        
        # 撞到自身/障碍物 → Game Over
        if self.check_collision(new_head):
            self.state = 'game_over'
            if game_over_sound:
                game_over_sound.play()
            return
        
        self.snake.insert(0, new_head)
        
        # 吃到食物逻辑
        if new_head == self.food:
            self.score += 1
            self.eat_count_for_obs += 1
            if eat_sound:
                eat_sound.play()
            # 每吃2个食物，生成一组3连块障碍物
            if self.eat_count_for_obs >= 2:
                self.spawn_triple_obstacle()
                self.eat_count_for_obs = 0
            self.spawn_food()
        # 没吃到食物：删除尾部，保持长度
        else:
            self.snake.pop()

    def draw_snake_head(self, pos):
        """根据当前方向绘制对应蛇头"""
        x, y = pos
        # 向右
        if self.dx == BLOCK_SIZE:
            if snake_head_right:
                self.screen.blit(snake_head_right, (x, y))
            else:
                pygame.draw.rect(self.screen, GREEN, (x, y, BLOCK_SIZE, BLOCK_SIZE))
        # 向左
        elif self.dx == -BLOCK_SIZE:
            if snake_head_left:
                self.screen.blit(snake_head_left, (x, y))
            else:
                pygame.draw.rect(self.screen, GREEN, (x, y, BLOCK_SIZE, BLOCK_SIZE))
        # 向上
        elif self.dy == -BLOCK_SIZE:
            if snake_head_up:
                self.screen.blit(snake_head_up, (x, y))
            else:
                pygame.draw.rect(self.screen, GREEN, (x, y, BLOCK_SIZE, BLOCK_SIZE))
        # 向下
        elif self.dy == BLOCK_SIZE:
            if snake_head_down:
                self.screen.blit(snake_head_down, (x, y))
            else:
                pygame.draw.rect(self.screen, GREEN, (x, y, BLOCK_SIZE, BLOCK_SIZE))

    def draw_menu(self):
        """绘制难度选择菜单"""
        # 标题
        title_text = self.title_font.render("SNAKE GAME", True, GREEN)
        self.screen.blit(title_text, (WIDTH//2 - title_text.get_width()//2, 120))
        
        # 副标题
        sub_text = self.menu_font.render("Select Difficulty", True, WHITE)
        self.screen.blit(sub_text, (WIDTH//2 - sub_text.get_width()//2, 200))

        # 三个难度选项
        level1_text = self.menu_font.render("Press 1 : Level 1 (Slow)", True, GRAY)
        self.screen.blit(level1_text, (WIDTH//2 - level1_text.get_width()//2, 280))
        
        level2_text = self.menu_font.render("Press 2 : Level 2 (Normal)", True, WHITE)
        self.screen.blit(level2_text, (WIDTH//2 - level2_text.get_width()//2, 320))
        
        level3_text = self.menu_font.render("Press 3 : Level 3 (Fast)", True, GRAY)
        self.screen.blit(level3_text, (WIDTH//2 - level3_text.get_width()//2, 360))

        # 底部规则提示
        tip_text = self.font.render("WASD move | Every 2 food spawn 3 connected obstacles | ESC exit", True, GRAY)
        self.screen.blit(tip_text, (WIDTH//2 - tip_text.get_width()//2, 480))

    def draw(self):
        """绘制全部画面，分状态渲染"""
        self.screen.fill(BLACK)

        # 菜单界面
        if self.state == 'menu':
            self.draw_menu()
        
        # 游戏界面（游戏中+结束+胜利都基于游戏画面）
        else:
            # 绘制障碍物
            for obs_pos in self.obstacles:
                if obstacle_img:
                    self.screen.blit(obstacle_img, obs_pos)
                else:
                    pygame.draw.rect(self.screen, OBSTACLE_COLOR, (obs_pos[0], obs_pos[1], BLOCK_SIZE, BLOCK_SIZE))

            # 绘制食物
            if food_img:
                self.screen.blit(food_img, self.food)
            else:
                pygame.draw.rect(self.screen, RED, (self.food[0], self.food[1], BLOCK_SIZE, BLOCK_SIZE))

            # 绘制蛇身和蛇头
            for i, block in enumerate(self.snake):
                if i == 0:
                    self.draw_snake_head(block)
                else:
                    if snake_body_img:
                        self.screen.blit(snake_body_img, block)
                    else:
                        pygame.draw.rect(self.screen, BLUE, (block[0], block[1], BLOCK_SIZE, BLOCK_SIZE))

            # 左上角信息
            score_text = self.font.render(f"Score: {self.score}", True, WHITE)
            self.screen.blit(score_text, (10, 10))
            
            level_text = self.font.render(f"Level: {self.current_level}", True, GRAY)
            self.screen.blit(level_text, (10, 35))
            
            obs_count_text = self.font.render(f"Obstacle Blocks: {len(self.obstacles)}", True, GRAY)
            self.screen.blit(obs_count_text, (10, 60))

            # 游戏结束遮罩界面
            if self.state == 'game_over':
                overlay = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
                overlay.fill((0, 0, 0, 180))
                self.screen.blit(overlay, (0, 0))

                over_title = self.big_font.render("GAME OVER", True, RED)
                self.screen.blit(over_title, (WIDTH//2 - over_title.get_width()//2, HEIGHT//2 - 70))
                
                retry_text = self.menu_font.render("R - Retry", True, WHITE)
                self.screen.blit(retry_text, (WIDTH//2 - retry_text.get_width()//2, HEIGHT//2 + 10))
                
                close_text = self.menu_font.render("ESC - Close", True, WHITE)
                self.screen.blit(close_text, (WIDTH//2 - close_text.get_width()//2, HEIGHT//2 + 50))
            
            # 游戏胜利遮罩界面
            elif self.state == 'win':
                overlay = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
                overlay.fill((0, 0, 0, 180))
                self.screen.blit(overlay, (0, 0))

                win_title = self.big_font.render("YOU WIN!", True, GREEN)
                self.screen.blit(win_title, (WIDTH//2 - win_title.get_width()//2, HEIGHT//2 - 70))
                
                win_tip = self.menu_font.render("All space filled completely!", True, WHITE)
                self.screen.blit(win_tip, (WIDTH//2 - win_tip.get_width()//2, HEIGHT//2 - 20))
                
                retry_text = self.menu_font.render("R - Retry", True, WHITE)
                self.screen.blit(retry_text, (WIDTH//2 - retry_text.get_width()//2, HEIGHT//2 + 20))
                
                close_text = self.menu_font.render("ESC - Close", True, WHITE)
                self.screen.blit(close_text, (WIDTH//2 - close_text.get_width()//2, HEIGHT//2 + 60))

        pygame.display.flip()

    def run(self):
        """游戏主循环"""
        while True:
            self.handle_input()
            self.update()
            self.draw()
            self.clock.tick(self.speed)

# -------------------------- 启动游戏 --------------------------
if __name__ == "__main__":
    game = SnakeGame()
    game.run()