import pygame
import random
import json
import os
from enum import Enum

pygame.init()

# 颜色
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 210, 80)
RED = (230, 40, 40)
BLUE = (40, 140, 255)
PURPLE = (190, 60, 220)
ORANGE = (255, 170, 30)
DARK = (22, 26, 34)
GRAY = (185, 188, 192)
BG1 = (38, 43, 54)
BG2 = (42, 49, 59)

# 设置 - 大地图
CELL = 23
COLS = 52
ROWS = 36
PANEL_W = 280
WIDTH = CELL * COLS + PANEL_W
HEIGHT = CELL * ROWS + 16
FPS = 7

class Dir(Enum):
    UP = (0, -1)
    DOWN = (0, 1)
    LEFT = (-1, 0)
    RIGHT = (1, 0)

class Snake:
    def __init__(self, start, color, name, is_ai=False):
        self.body = [start]
        self.dir = Dir.RIGHT
        self.color = color
        self.name = name
        self.score = 0
        self.is_ai = is_ai
        self.alive = True
        self.reason = ""
        
    def move(self):
        if not self.alive:
            return
        head = self.body[0]
        dx, dy = self.dir.value
        new = (head[0] + dx, head[1] + dy)
        
        if new[0] < 0 or new[0] >= COLS or new[1] < 0 or new[1] >= ROWS:
            self.alive = False
            self.reason = "撞墙"
            return
            
        self.body.insert(0, new)
        
    def check_collision(self, others):
        if not self.alive:
            return
        head = self.body[0]
        if head in self.body[1:]:
            self.alive = False
            self.reason = "咬到自己"
        for s in others:
            if s != self and head in s.body:
                self.alive = False
                self.reason = f"撞到{s.name}"

def make_textures():
    tex = {}
    s = CELL
    
    # 蛇头 - 各颜色
    for name, clr in [("green", (0, 210, 80)), ("red", (230, 40, 40)),
                       ("blue", (40, 140, 255)), ("purple", (190, 60, 220)),
                       ("orange", (255, 170, 30))]:
        surf = pygame.Surface((s, s), pygame.SRCALPHA)
        pygame.draw.ellipse(surf, clr, (2, 2, s-4, s-4))
        h = s//2
        pygame.draw.circle(surf, WHITE, (h-5, h-5), 4)
        pygame.draw.circle(surf, WHITE, (h+5, h-5), 4)
        pygame.draw.circle(surf, BLACK, (h-5, h-5), 2)
        pygame.draw.circle(surf, BLACK, (h+5, h-5), 2)
        tex[f"head_{name}"] = surf
    
    # 蛇身
    for name, clr in [("green", (0, 165, 60)), ("red", (180, 30, 30)),
                       ("blue", (30, 110, 205)), ("purple", (150, 45, 175)),
                       ("orange", (200, 135, 20))]:
        surf = pygame.Surface((s, s), pygame.SRCALPHA)
        pygame.draw.rect(surf, clr, (2, 2, s-4, s-4), border_radius=5)
        tex[f"body_{name}"] = surf
    
    # 蛇尾
    for name, clr in [("green", (0, 125, 45)), ("red", (140, 20, 20)),
                       ("blue", (20, 85, 160)), ("purple", (115, 30, 135)),
                       ("orange", (155, 105, 15))]:
        surf = pygame.Surface((s, s), pygame.SRCALPHA)
        pts = [(s-2, s//2), (5, 3), (5, s-3)]
        pygame.draw.polygon(surf, clr, pts)
        tex[f"tail_{name}"] = surf
    
    # 食物 - 苹果
    apple = pygame.Surface((s, s), pygame.SRCALPHA)
    pygame.draw.circle(apple, (235, 30, 35), (s//2, s//2+2), s//2-3)
    pygame.draw.line(apple, (95, 68, 22), (s//2, 3), (s//2, s//2-5), 2)
    leaf = [(s//2, s//2-5), (s//2-5, 3), (s//2+5, 3)]
    pygame.draw.polygon(apple, (30, 168, 78), leaf)
    tex["apple"] = apple
    
    # 星星道具
    star = pygame.Surface((s, s), pygame.SRCALPHA)
    c = s//2
    pts = []
    for i in range(10):
        angle = -90 + i*36
        r = s//2-3 if i%2==0 else s//4
        x = c + r * pygame.math.Vector2(1,0).rotate(angle).x
        y = c + r * pygame.math.Vector2(1,0).rotate(angle).y
        pts.append((int(x), int(y)))
    pygame.draw.polygon(star, (252, 212, 72), pts)
    tex["star"] = star
    
    # 盾牌道具
    shield = pygame.Surface((s, s), pygame.SRCALPHA)
    h = s//2
    pts = [(h, 3), (s-4, h-3), (s-4, h+5), (h, s-3), (4, h+5), (4, h-3)]
    pygame.draw.polygon(shield, (92, 152, 204), pts)
    pygame.draw.polygon(shield, (182, 214, 242), [(h, 6), (s-7, h-1), (s-7, h+4), (h, s-6), (7, h+4), (7, h-1)], 2)
    tex["shield"] = shield
    
    # 乌龟道具
    turtle = pygame.Surface((s, s), pygame.SRCALPHA)
    pygame.draw.ellipse(turtle, (88, 158, 146), (4, h-3, s-8, s-5))
    pygame.draw.circle(turtle, (128, 198, 176), (h, h+2), h-6)
    pygame.draw.circle(turtle, BLACK, (h-5, h-4), 2)
    pygame.draw.circle(turtle, BLACK, (h+5, h-4), 2)
    tex["turtle"] = turtle
    
    # 石头
    stone = pygame.Surface((s, s), pygame.SRCALPHA)
    pygame.draw.ellipse(stone, (98, 106, 116), (3, 4, s-6, s-7))
    pygame.draw.ellipse(stone, (134, 140, 148), (5, 6, s-11, s-12))
    tex["stone"] = stone
    
    return tex

class Game:
    def __init__(self):
        self.screen = pygame.display.set_mode((WIDTH, HEIGHT))
        pygame.display.set_caption("🐍 贪吃蛇大乱斗")
        self.clock = pygame.time.Clock()
        self.font = pygame.font.Font(None, 26)
        self.big_font = pygame.font.Font(None, 48)
        self.running = True
        self.state = "menu"
        self.player = None
        self.ais = []
        self.food = None
        self.walls = []
        self.items = []
        self.tex = make_textures()
        self.save_dir = "saves"
        self.msg = ""
        self.msg_timer = 0
        
        if not os.path.exists(self.save_dir):
            os.makedirs(self.save_dir)
    
    def spawn_food(self):
        while True:
            pos = (random.randint(0, COLS-1), random.randint(0, ROWS-1))
            ok = True
            if pos in self.walls:
                ok = False
            all_snakes = [self.player] + self.ais if self.player else []
            for s in all_snakes:
                if s and pos in s.body:
                    ok = False
            if ok:
                return pos
    
    def spawn_item(self):
        if len(self.items) >= 5:
            return
        while True:
            pos = (random.randint(0, COLS-1), random.randint(0, ROWS-1))
            ok = True
            if pos in self.walls:
                ok = False
            all_snakes = [self.player] + self.ais if self.player else []
            for s in all_snakes:
                if s and pos in s.body:
                    ok = False
            if ok and pos != self.food:
                types = ["speed", "slow", "score", "invincible"]
                self.items.append({"pos": pos, "type": random.choice(types), "timer": 600})
                return
    
    def new_game(self):
        self.walls = []
        self.items = []
        
        # 随机生成一些墙壁
        for _ in range(30):
            x = random.randint(2, COLS-3)
            y = random.randint(2, ROWS-3)
            self.walls.append((x, y))
        
        self.player = Snake((3, ROWS//2), GREEN, "玩家")
        
        # 4个AI对手
        ai_configs = [
            ((COLS-4, 3), RED, "AI-红"),
            ((COLS-4, ROWS-4), BLUE, "AI-蓝"),
            ((3, ROWS-4), PURPLE, "AI-紫"),
            ((COLS//2, ROWS//2), ORANGE, "AI-橙"),
        ]
        self.ais = []
        for pos, color, name in ai_configs:
            ai = Snake(pos, color, name, True)
            ai.dir = random.choice(list(Dir))
            self.ais.append(ai)
        
        self.food = self.spawn_food()
        self.state = "play"
        self.msg = ""
    
    def handle(self, event):
        if event.type == pygame.KEYDOWN:
            if self.state == "menu":
                if event.key == pygame.K_1:
                    self.new_game()
                elif event.key == pygame.K_l:
                    saves = sorted([f for f in os.listdir(self.save_dir) if f.endswith('.json')])
                    if saves:
                        self.load(saves[-1])
            elif self.state == "play":
                p = self.player
                if event.key == pygame.K_UP and p.dir != Dir.DOWN:
                    p.dir = Dir.UP
                elif event.key == pygame.K_DOWN and p.dir != Dir.UP:
                    p.dir = Dir.DOWN
                elif event.key == pygame.K_LEFT and p.dir != Dir.RIGHT:
                    p.dir = Dir.LEFT
                elif event.key == pygame.K_RIGHT and p.dir != Dir.LEFT:
                    p.dir = Dir.RIGHT
                elif event.key == pygame.K_p:
                    self.state = "pause"
                elif event.key == pygame.K_s:
                    self.save()
            elif self.state == "pause":
                if event.key == pygame.K_p:
                    self.state = "play"
                elif event.key == pygame.K_q:
                    self.state = "menu"
            elif self.state == "over":
                if event.key == pygame.K_SPACE:
                    self.state = "menu"
    
    def ai_move(self, ai):
        if not ai.alive:
            return
        head = ai.body[0]
        tx, ty = self.food
        
        # 找最近的物品
        best_target = self.food
        best_dist = abs(tx-head[0]) + abs(ty-head[1])
        for item in self.items:
            d = abs(item['pos'][0]-head[0]) + abs(item['pos'][1]-head[1])
            if d < best_dist:
                best_target = item['pos']
                best_dist = d
        
        dirs = list(Dir)
        random.shuffle(dirs)
        best = ai.dir
        best_dist = 9999
        
        for d in dirs:
            dx, dy = d.value
            nx, ny = head[0]+dx, head[1]+dy
            if 0 <= nx < COLS and 0 <= ny < ROWS:
                blocked = False
                if (nx, ny) in ai.body:
                    blocked = True
                if (nx, ny) in self.walls:
                    blocked = True
                if not blocked:
                    dist = abs(nx-best_target[0]) + abs(ny-best_target[1])
                    if dist < best_dist:
                        best_dist = dist
                        best = d
        ai.dir = best
    
    def update(self):
        if self.state != "play":
            return
        
        if self.msg_timer > 0:
            self.msg_timer -= 1
        else:
            self.msg = ""
        
        # AI移动
        for ai in self.ais:
            self.ai_move(ai)
        
        # 所有蛇移动
        self.player.move()
        for ai in self.ais:
            ai.move()
        
        # 碰撞检测
        all_snakes = [self.player] + self.ais
        for s in all_snakes:
            s.check_collision(all_snakes)
        
        # 吃食物
        for s in all_snakes:
            if s.alive and s.body[0] == self.food:
                s.score += 10
                self.food = self.spawn_food()
        
        # 吃道具
        for item in self.items[:]:
            for s in all_snakes:
                if s.alive and s.body[0] == item['pos']:
                    if item['type'] == "speed":
                        s.score += 5
                    elif item['type'] == "slow":
                        pass
                    elif item['type'] == "score":
                        s.score += 20
                    elif item['type'] == "invincible":
                        s.score += 3
                    self.items.remove(item)
                    break
        
        # 道具计时
        for item in self.items[:]:
            item['timer'] -= 1
            if item['timer'] <= 0:
                self.items.remove(item)
        
        # 生成道具
        if random.random() < 0.008:
            self.spawn_item()
        
        # 检查游戏结束
        alive = [s for s in all_snakes if s.alive]
        if len(alive) <= 1:
            self.state = "over"
    
    def draw_grid(self):
        for x in range(COLS):
            for y in range(ROWS):
                rect = (x*CELL, y*CELL+8, CELL, CELL)
                color = BG1 if (x+y)%2==0 else BG2
                pygame.draw.rect(self.screen, color, rect)
    
    def draw_snake(self, snake):
        if not snake.body:
            return
        
        color_key = ""
        if snake.color == GREEN: color_key = "green"
        elif snake.color == RED: color_key = "red"
        elif snake.color == BLUE: color_key = "blue"
        elif snake.color == PURPLE: color_key = "purple"
        elif snake.color == ORANGE: color_key = "orange"
        
        clr = snake.color if snake.alive else (100, 102, 104)
        
        for i, seg in enumerate(snake.body):
            x, y = seg
            sx, sy = x*CELL, y*CELL+8
            
            if not snake.alive:
                pygame.draw.rect(self.screen, (100, 102, 104), (sx+1, sy+1, CELL-2, CELL-2), border_radius=4)
                continue
            
            if i == 0:
                tex = self.tex.get(f"head_{color_key}")
                if tex:
                    angle = 0
                    if snake.dir == Dir.UP: angle = 270
                    elif snake.dir == Dir.DOWN: angle = 90
                    elif snake.dir == Dir.LEFT: angle = 180
                    rot = pygame.transform.rotate(tex, angle)
                    self.screen.blit(rot, (sx, sy))
                else:
                    pygame.draw.ellipse(self.screen, clr, (sx+1, sy+1, CELL-2, CELL-2))
                    hx, hy = sx+CELL//2, sy+CELL//2
                    pygame.draw.circle(self.screen, WHITE, (hx-4, hy-4), 3)
                    pygame.draw.circle(self.screen, WHITE, (hx+4, hy-4), 3)
                    pygame.draw.circle(self.screen, BLACK, (hx-4, hy-4), 2)
                    pygame.draw.circle(self.screen, BLACK, (hx+4, hy-4), 2)
            elif i == len(snake.body)-1:
                tex = self.tex.get(f"tail_{color_key}")
                if tex:
                    prev = snake.body[i-1]
                    dx = prev[0] - seg[0]
                    dy = prev[1] - seg[1]
                    angle = 0
                    if dx == 1: angle = 180
                    elif dx == -1: angle = 0
                    elif dy == 1: angle = 90
                    elif dy == -1: angle = 270
                    rot = pygame.transform.rotate(tex, angle)
                    self.screen.blit(rot, (sx, sy))
                else:
                    pygame.draw.rect(self.screen, clr, (sx+1, sy+1, CELL-2, CELL-2), border_radius=4)
            else:
                tex = self.tex.get(f"body_{color_key}")
                if tex:
                    self.screen.blit(tex, (sx, sy))
                else:
                    pygame.draw.rect(self.screen, clr, (sx+1, sy+1, CELL-2, CELL-2), border_radius=4)
    
    def draw(self):
        self.screen.fill(DARK)
        
        if self.state == "menu":
            title = self.big_font.render("🐍 贪吃蛇大乱斗", True, (0, 248, 118))
            self.screen.blit(title, (WIDTH//2-145, 70))
            
            sub = self.font.render(f"🌍 {COLS}x{ROWS} 大地图 + 4个AI", True, GRAY)
            self.screen.blit(sub, (WIDTH//2-120, 120))
            
            opts = [
                "1️⃣  开始游戏",
                "📂  读取存档 (L)",
            ]
            y = 190
            for t in opts:
                label = self.font.render(t, True, GRAY)
                self.screen.blit(label, (WIDTH//2-75, y))
                y += 45
            
            # 底部展示贴图
            demo = [("head_green", "玩家"), ("head_red", "AI-红"), ("head_blue", "AI-蓝"),
                    ("head_purple", "AI-紫"), ("head_orange", "AI-橙")]
            bx = 80
            for tex_name, label in demo:
                tex = self.tex.get(tex_name)
                if tex:
                    self.screen.blit(tex, (bx, HEIGHT-70))
                    lbl = self.font.render(label, True, GRAY)
                    self.screen.blit(lbl, (bx-5, HEIGHT-45))
                bx += 110
                
        elif self.state in ["play", "pause", "over"]:
            self.draw_grid()
            
            # 墙壁
            for wx, wy in self.walls:
                tex = self.tex.get("stone")
                if tex:
                    self.screen.blit(tex, (wx*CELL, wy*CELL+8))
            
            # 食物
            fx, fy = self.food
            tex = self.tex.get("apple")
            if tex:
                self.screen.blit(tex, (fx*CELL, fy*CELL+8))
            else:
                pygame.draw.circle(self.screen, (235, 30, 35),
                                 (fx*CELL+CELL//2, fy*CELL+8+CELL//2), CELL//2-2)
            
            # 道具
            for item in self.items:
                ix, iy = item['pos']
                tex_name = {"speed":"star", "slow":"turtle", "score":"apple", "invincible":"shield"}
                tex = self.tex.get(tex_name.get(item['type'], "star"))
                if tex:
                    self.screen.blit(tex, (ix*CELL, iy*CELL+8))
            
            # 画所有蛇 - AI先画，玩家最后画
            for ai in self.ais:
                self.draw_snake(ai)
            self.draw_snake(self.player)
            
            # 面板
            px = COLS*CELL + 10
            py = 15
            
            all_snakes = [self.player] + self.ais
            alive_count = sum(1 for s in all_snakes if s.alive)
            
            header = self.font.render(f"🎮 存活: {alive_count}/{len(all_snakes)}", True, (0, 208, 100))
            self.screen.blit(header, (px, py))
            py += 35
            
            # 排行榜
            ranked = sorted(all_snakes, key=lambda s: -s.score)
            for s in ranked:
                icon = "🟢" if s.alive else "💀"
                clr_name = ""
                if s.color == GREEN: clr_name = "玩家"
                elif s.color == RED: clr_name = "红"
                elif s.color == BLUE: clr_name = "蓝"
                elif s.color == PURPLE: clr_name = "紫"
                elif s.color == ORANGE: clr_name = "橙"
                
                txt = f"{icon} {clr_name}: {s.score}分 ({len(s.body)}节)"
                if not s.alive:
                    txt += f" {s.reason}"
                
                color = s.color if s.alive else (120, 122, 124)
                label = self.font.render(txt, True, color)
                self.screen.blit(label, (px, py))
                py += 29
            
            if self.msg:
                msg_label = self.font.render(self.msg, True, (255, 200, 50))
                self.screen.blit(msg_label, (px, py+15))
            
            # 操作提示
            tips = self.font.render("↑↓←→移动 P暂停 S保存", True, (100, 112, 126))
            self.screen.blit(tips, (px, HEIGHT-35))
            
            if self.state == "pause":
                overlay = pygame.Surface((WIDTH, HEIGHT))
                overlay.set_alpha(140)
                overlay.fill(BLACK)
                self.screen.blit(overlay, (0, 0))
                
                txt = self.big_font.render("⏸️ 暂停", True, WHITE)
                self.screen.blit(txt, (WIDTH//2-70, HEIGHT//2-100))
                hint = self.font.render("P继续  Q返回菜单", True, GRAY)
                self.screen.blit(hint, (WIDTH//2-85, HEIGHT//2-45))
            
            if self.state == "over":
                overlay = pygame.Surface((WIDTH, HEIGHT))
                overlay.set_alpha(170)
                overlay.fill(BLACK)
                self.screen.blit(overlay, (0, 0))
                
                alive = [s for s in all_snakes if s.alive]
                if alive:
                    winner = alive[0]
                    w_name = "玩家" if winner == self.player else winner.name
                    w_color = winner.color
                else:
                    w_name = "无人"
                    w_color = WHITE
                
                txt = self.big_font.render(f"🏆 {w_name} 获胜!", True, w_color)
                self.screen.blit(txt, (WIDTH//2-130, HEIGHT//2-130))
                
                scores = "  ".join([f"{'玩家' if s==self.player else s.name}:{s.score}" for s in ranked[:3]])
                info = self.font.render(scores, True, WHITE)
                self.screen.blit(info, (WIDTH//2-180, HEIGHT//2-70))
                
                hint = self.font.render("按空格返回菜单", True, GRAY)
                self.screen.blit(hint, (WIDTH//2-90, HEIGHT//2-20))
        
        pygame.display.flip()
    
    def save(self):
        data = {
            'player_body': self.player.body,
            'player_score': self.player.score,
            'player_dir': self.player.dir.name,
            'ais': [{
                'body': ai.body,
                'score': ai.score,
                'dir': ai.dir.name,
                'color_idx': i
            } for i, ai in enumerate(self.ais)],
            'food': self.food,
            'walls': self.walls,
            'items': [{'pos': it['pos'], 'type': it['type'], 'timer': it['timer']} for it in self.items],
        }
        n = len([f for f in os.listdir(self.save_dir) if f.endswith('.json')])
        path = f"{self.save_dir}/save_{n+1}.json"
        with open(path, 'w') as f:
            json.dump(data, f)
        self.msg = f"💾 已保存 #{n+1}"
        self.msg_timer = 120
    
    def load(self, path):
        try:
            with open(path) as f:
                data = json.load(f)
            
            colors = [GREEN, RED, BLUE, PURPLE, ORANGE]
            names = ["玩家", "AI-红", "AI-蓝", "AI-紫", "AI-橙"]
            
            self.player = Snake((0,0), GREEN, "玩家")
            self.player.body = [tuple(p) for p in data['player_body']]
            self.player.score = data['player_score']
            self.player.dir = Dir[data['player_dir']]
            
            self.ais = []
            for adata in data['ais']:
                ci = adata.get('color_idx', 0)
                ai = Snake((0,0), colors[ci], names[ci], True)
                ai.body = [tuple(p) for p in adata['body']]
                ai.score = adata['score']
                ai.dir = Dir[adata['dir']]
                self.ais.append(ai)
            
            self.food = tuple(data['food'])
            self.walls = data['walls']
            self.items = [{'pos': tuple(it['pos']), 'type': it['type'], 'timer': it['timer']} for it in data['items']]
            
            self.state = "play"
            self.msg = "📂 加载成功"
            self.msg_timer = 120
        except Exception as e:
            print(f"加载失败: {e}")
    
    def run(self):
        while self.running:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    self.running = False
                self.handle(event)
            self.update()
            self.draw()
            self.clock.tick(FPS)
        pygame.quit()

if __name__ == "__main__":
    Game().run()