import pygame
import random
import json
import os
import sys

# --- 配置与常量 ---

# 颜色定义 (R, G, B)
COLORS = {
    'bg': (20, 20, 30),
    'grid': (30, 30, 45),
    'snake_head': (0, 255, 128),
    'snake_body': (0, 200, 100),
    'food': (255, 80, 80),
    'text': (255, 255, 255),
    'text_dim': (150, 150, 150),
    'button': (70, 130, 180),
    'button_hover': (100, 160, 210),
    'danger': (255, 50, 50)
}

# 屏幕与网格设置
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
GRID_SIZE = 20
GRID_WIDTH = SCREEN_WIDTH // GRID_SIZE
GRID_HEIGHT = SCREEN_HEIGHT // GRID_SIZE

# 配置文件路径
CONFIG_FILE = 'snake_config.json'

# 默认键位配置
DEFAULT_KEYS = {
    'up': pygame.K_UP,
    'down': pygame.K_DOWN,
    'left': pygame.K_LEFT,
    'right': pygame.K_RIGHT
}

# 速度与分数系数
SPEEDS = {
    '慢速': 10,
    '正常': 15,
    '快速': 20,
    '极速': 30
}
SCORE_MULTIPLIERS = {
    '慢速': 1.0,
    '正常': 1.5,
    '快速': 2.0,
    '极速': 3.0
}

# --- 辅助类 ---

class Button:
    """精美的按钮类"""
    def __init__(self, x, y, width, height, text, font):
        self.rect = pygame.Rect(x, y, width, height)
        self.text = text
        self.font = font
        self.is_hovered = False

    def draw(self, screen):
        color = COLORS['button_hover'] if self.is_hovered else COLORS['button']
        # 绘制圆角矩形
        pygame.draw.rect(screen, color, self.rect, border_radius=10)
        # 绘制边框
        pygame.draw.rect(screen, COLORS['text'], self.rect, 2, border_radius=10)
        
        # 绘制文字
        text_surf = self.font.render(self.text, True, COLORS['text'])
        text_rect = text_surf.get_rect(center=self.rect.center)
        screen.blit(text_surf, text_rect)

    def check_hover(self, pos):
        self.is_hovered = self.rect.collidepoint(pos)
        return self.is_hovered

    def is_clicked(self, pos, event):
        if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
            if self.rect.collidepoint(pos):
                return True
        return False

# --- 核心游戏逻辑 ---

class SnakeGame:
    def __init__(self):
        pygame.init()
        pygame.mixer.init()
        
        self.screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
        pygame.display.set_caption("Python 贪吃蛇 - my_pkg.games100")
        self.clock = pygame.time.Clock()
        
        # 字体加载
        self.font_large = pygame.font.SysFont('simhei', 60)
        self.font_medium = pygame.font.SysFont('simhei', 36)
        self.font_small = pygame.font.SysFont('simhei', 24)
        
        # 游戏状态
        self.state = 'MENU' # MENU, SETTINGS, PLAYING, GAMEOVER
        self.score = 0
        self.high_score = 0
        
        # 设置加载
        self.keys = self.load_config()
        self.current_speed_name = '正常'
        
        # 键位设置状态
        self.rebinding_key = None 

    def load_config(self):
        if os.path.exists(CONFIG_FILE):
            try:
                with open(CONFIG_FILE, 'r') as f:
                    return json.load(f)
            except:
                return DEFAULT_KEYS.copy()
        return DEFAULT_KEYS.copy()

    def save_config(self):
        with open(CONFIG_FILE, 'w') as f:
            json.dump(self.keys, f)

    def reset_game(self):
        self.snake = [(GRID_WIDTH // 2, GRID_HEIGHT // 2)]
        self.direction = (1, 0)
        self.next_direction = (1, 0)
        self.spawn_food()
        self.score = 0
        self.speed = SPEEDS[self.current_speed_name]
        self.multiplier = SCORE_MULTIPLIERS[self.current_speed_name]

    def spawn_food(self):
        while True:
            self.food = (random.randint(0, GRID_WIDTH - 1), random.randint(0, GRID_HEIGHT - 1))
            if self.food not in self.snake:
                break

    # --- 绘制方法 ---
    
    def draw_grid(self):
        for x in range(0, SCREEN_WIDTH, GRID_SIZE):
            pygame.draw.line(self.screen, COLORS['grid'], (x, 0), (x, SCREEN_HEIGHT))
        for y in range(0, SCREEN_HEIGHT, GRID_SIZE):
            pygame.draw.line(self.screen, COLORS['grid'], (0, y), (SCREEN_WIDTH, y))

    def draw_snake(self):
        for i, (x, y) in enumerate(self.snake):
            rect = pygame.Rect(x * GRID_SIZE, y * GRID_SIZE, GRID_SIZE, GRID_SIZE)
            color = COLORS['snake_head'] if i == 0 else COLORS['snake_body']
            pygame.draw.rect(self.screen, color, rect, border_radius=5)
            # 画个眼睛
            if i == 0:
                eye_size = 4
                eye_offset = 6
                if self.direction == (1, 0): # 右
                    pygame.draw.circle(self.screen, (0,0,0), (rect.right - eye_offset, rect.centery), eye_size)
                elif self.direction == (-1, 0): # 左
                    pygame.draw.circle(self.screen, (0,0,0), (rect.left + eye_offset, rect.centery), eye_size)
                elif self.direction == (0, 1): # 下
                    pygame.draw.circle(self.screen, (0,0,0), (rect.centerx, rect.bottom - eye_offset), eye_size)
                elif self.direction == (0, -1): # 上
                    pygame.draw.circle(self.screen, (0,0,0), (rect.centerx, rect.top + eye_offset), eye_size)

    def draw_food(self):
        x, y = self.food
        rect = pygame.Rect(x * GRID_SIZE, y * GRID_SIZE, GRID_SIZE, GRID_SIZE)
        pygame.draw.rect(self.screen, COLORS['food'], rect, border_radius=5)
        # 画个高光
        pygame.draw.circle(self.screen, (255, 200, 200), (rect.centerx - 3, rect.centery - 3), 3)

    def draw_text_center(self, text, font, y_offset, color=COLORS['text']):
        surf = font.render(text, True, color)
        rect = surf.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2 + y_offset))
        self.screen.blit(surf, rect)

    # --- 界面逻辑 ---

    def show_menu(self):
        self.screen.fill(COLORS['bg'])
        
        # 标题动画（简单的呼吸效果）
        scale = 1.0 + 0.05 * abs((pygame.time.get_ticks() % 2000) - 1000) / 1000
        title_font = pygame.font.SysFont('simhei', int(70 * scale))
        self.draw_text_center("贪 吃 蛇", title_font, -150, COLORS['snake_head'])
        
        self.draw_text_center(f"最高分: {self.high_score}", self.font_small, -80, COLORS['text_dim'])

        mouse_pos = pygame.mouse.get_pos()
        
        # 按钮
        btn_start = Button(SCREEN_WIDTH//2 - 100, 280, 200, 50, "开始游戏", self.font_medium)
        btn_settings = Button(SCREEN_WIDTH//2 - 100, 350, 200, 50, "键位设置", self.font_medium)
        btn_quit = Button(SCREEN_WIDTH//2 - 100, 420, 200, 50, "退出游戏", self.font_medium)

        for btn in [btn_start, btn_settings, btn_quit]:
            btn.check_hover(mouse_pos)
            btn.draw(self.screen)

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
            
            if btn_start.is_clicked(mouse_pos, event):
                self.reset_game()
                self.state = 'PLAYING'
            if btn_settings.is_clicked(mouse_pos, event):
                self.state = 'SETTINGS'
            if btn_quit.is_clicked(mouse_pos, event):
                pygame.quit()
                sys.exit()

    def show_settings(self):
        self.screen.fill(COLORS['bg'])
        self.draw_text_center("键位设置", self.font_large, -200)
        self.draw_text_center("点击下方按键框，然后按下新按键进行绑定", self.font_small, -140, COLORS['text_dim'])

        mouse_pos = pygame.mouse.get_pos()
        y_start = -50
        
        key_names = ['up', 'down', 'left', 'right']
        display_names = {'up': '上移', 'down': '下移', 'left': '左移', 'right': '右移'}
        
        buttons = []

        for i, key in enumerate(key_names):
            # 标签
            label = self.font_medium.render(f"{display_names[key]}:", True, COLORS['text'])
            label_rect = label.get_rect(center=(SCREEN_WIDTH//2 - 80, SCREEN_HEIGHT//2 + y_start + i*60))
            self.screen.blit(label, label_rect)
            
            # 按键按钮
            key_name = pygame.key.name(self.keys[key]).upper()
            btn = Button(SCREEN_WIDTH//2 + 20, SCREEN_HEIGHT//2 + y_start + i*60 - 20, 120, 40, key_name, self.font_small)
            
            # 如果正在等待该键位的输入，变色提示
            if self.rebinding_key == key:
                btn.color = COLORS['danger'] # 红色提示正在输入
            
            btn.check_hover(mouse_pos)
            btn.draw(self.screen)
            buttons.append((btn, key))

        # 速度选择
        self.draw_text_center(f"当前速度: {self.current_speed_name} (分数倍率: x{SCORE_MULTIPLIERS[self.current_speed_name]})", self.font_small, 180, COLORS['text_dim'])
        
        btn_speed = Button(SCREEN_WIDTH//2 - 100, 400, 200, 40, "切换速度", self.font_small)
        btn_back = Button(SCREEN_WIDTH//2 - 100, 480, 200, 40, "返回主菜单", self.font_medium)
        
        btn_speed.check_hover(mouse_pos)
        btn_back.check_hover(mouse_pos)
        btn_speed.draw(self.screen)
        btn_back.draw(self.screen)

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
            
            if self.rebinding_key:
                # 等待按键输入
                if event.type == pygame.KEYDOWN:
                    # 防止绑定冲突（简单检查）
                    self.keys[self.rebinding_key] = event.key
                    self.save_config()
                    self.rebinding_key = None
            else:
                # 处理按钮点击
                if btn_back.is_clicked(mouse_pos, event):
                    self.state = 'MENU'
                
                if btn_speed.is_clicked(mouse_pos, event):
                    # 循环切换速度
                    speed_list = list(SPEEDS.keys())
                    current_idx = speed_list.index(self.current_speed_name)
                    next_idx = (current_idx + 1) % len(speed_list)
                    self.current_speed_name = speed_list[next_idx]

                for btn, key in buttons:
                    if btn.is_clicked(mouse_pos, event):
                        self.rebinding_key = key

    def show_gameover(self):
        # 半透明遮罩
        overlay = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT), pygame.SRCALPHA)
        overlay.fill((0, 0, 0, 180))
        self.screen.blit(overlay, (0, 0))

        self.draw_text_center("游戏结束", self.font_large, -80, COLORS['danger'])
        self.draw_text_center(f"得分: {self.score} (x{self.multiplier})", self.font_medium, 0)
        if self.score > self.high_score:
            self.draw_text_center("新纪录!", self.font_medium, 50, COLORS['snake_head'])
            self.high_score = self.score

        mouse_pos = pygame.mouse.get_pos()
        btn_restart = Button(SCREEN_WIDTH//2 - 100, 350, 200, 50, "重新开始", self.font_medium)
        btn_menu = Button(SCREEN_WIDTH//2 - 100, 420, 200, 50, "返回主菜单", self.font_medium)

        for btn in [btn_restart, btn_menu]:
            btn.check_hover(mouse_pos)
            btn.draw(self.screen)

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
            
            if btn_restart.is_clicked(mouse_pos, event):
                self.reset_game()
                self.state = 'PLAYING'
            if btn_menu.is_clicked(mouse_pos, event):
                self.state = 'MENU'

    def update_game(self):
        # 事件处理
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
            if event.type == pygame.KEYDOWN:
                if event.key == self.keys['up'] and self.direction != (0, 1):
                    self.next_direction = (0, -1)
                elif event.key == self.keys['down'] and self.direction != (0, -1):
                    self.next_direction = (0, 1)
                elif event.key == self.keys['left'] and self.direction != (1, 0):
                    self.next_direction = (-1, 0)
                elif event.key == self.keys['right'] and self.direction != (-1, 0):
                    self.next_direction = (1, 0)
        
        # 移动逻辑（控制帧率以实现速度调整）
        current_speed_val = SPEEDS[self.current_speed_name]
        
        # pygame的时间控制
        self.clock.tick(current_speed_val)
        
        self.direction = self.next_direction
        head_x, head_y = self.snake[0]
        dx, dy = self.direction
        new_head = (head_x + dx, head_y + dy)

        # 边界检测（穿墙模式 or 撞墙死）
        # 这里采用撞墙死亡模式增加挑战性
        if not (0 <= new_head[0] < GRID_WIDTH and 0 <= new_head[1] < GRID_HEIGHT):
            self.state = 'GAMEOVER'
            return

        # 自身碰撞
        if new_head in self.snake:
            self.state = 'GAMEOVER'
            return

        self.snake.insert(0, new_head)

        # 吃食物
        if new_head == self.food:
            base_score = 10
            self.score += int(base_score * self.multiplier)
            self.spawn_food()
        else:
            self.snake.pop()

        # 绘制
        self.screen.fill(COLORS['bg'])
        self.draw_grid()
        self.draw_food()
        self.draw_snake()
        
        # 绘制分数
        score_surf = self.font_medium.render(f"分数: {self.score}", True, COLORS['text'])
        self.screen.blit(score_surf, (10, 10))
        
        speed_surf = self.font_small.render(f"速度: {self.current_speed_name}", True, COLORS['text_dim'])
        self.screen.blit(speed_surf, (10, 50))

    def run(self):
        while True:
            if self.state == 'MENU':
                self.show_menu()
            elif self.state == 'SETTINGS':
                self.show_settings()
            elif self.state == 'PLAYING':
                self.update_game()
            elif self.state == 'GAMEOVER':
                # 保持游戏画面在背景
                # 先画游戏画面，再画结束弹窗
                self.screen.fill(COLORS['bg'])
                self.draw_grid()
                self.draw_food()
                self.draw_snake()
                self.show_gameover()
            
            pygame.display.flip()

# --- 模块接口 ---

_game_instance = None

def run():
    """
    启动贪吃蛇游戏的入口函数。
    用法: from my_pkg.games100 import snake; snake.run()
    """
    global _game_instance
    if _game_instance is None:
        _game_instance = SnakeGame()
    _game_instance.run()


