import pygame
import sys
import math
import random
from typing import List, Dict, Tuple

# 初始化pygame
pygame.init()

# 颜色定义
COLORS = {
    'background': (240, 248, 255),  # AliceBlue
    'text': (30, 30, 30),
    'grid': (200, 200, 200),
    'axis': (100, 100, 100),
    'bar_colors': [
        (255, 99, 132),   # 红色
        (54, 162, 235),   # 蓝色
        (255, 205, 86),   # 黄色
        (75, 192, 192),   # 青色
        (153, 102, 255),  # 紫色
        (255, 159, 64)    # 橙色
    ],
    'line_color': (255, 99, 132),
    'button_normal': (70, 130, 180),
    'button_hover': (100, 160, 210),
    'button_text': (255, 255, 255)
}


class GradeData:
    """成绩数据管理类"""

    def __init__(self):
        self.students = ["张三", "李四", "王五", "赵六", "钱七", "孙八"]
        self.subjects = ["数学", "语文", "英语", "物理", "化学", "生物"]
        self.grades = {}
        self.generate_random_data()

    def generate_random_data(self):
        """生成随机成绩数据"""
        for student in self.students:
            self.grades[student] = {}
            for subject in self.subjects:
                # 生成60-100之间的随机成绩
                self.grades[student][subject] = random.randint(60, 100)

    def get_student_grades(self, student: str) -> List[int]:
        """获取指定学生的各科成绩"""
        return [self.grades[student][subject] for subject in self.subjects]

    def get_subject_averages(self) -> List[float]:
        """获取各科平均分"""
        averages = []
        for subject in self.subjects:
            total = sum(self.grades[student][subject]
                        for student in self.students)
            avg = total / len(self.students)
            averages.append(avg)
        return averages

    def get_student_average(self, student: str) -> float:
        """获取指定学生的平均分"""
        grades = self.get_student_grades(student)
        return sum(grades) / len(grades)


class Button:
    """按钮类"""

    def __init__(self, x: int, y: int, width: int, height: int, text: str):
        self.rect = pygame.Rect(x, y, width, height)
        self.text = text
        self.hovered = False

    def draw(self, screen: pygame.Surface, font: pygame.font.Font):
        """绘制按钮"""
        color = COLORS['button_hover'] if self.hovered else COLORS['button_normal']
        pygame.draw.rect(screen, color, self.rect, border_radius=8)
        pygame.draw.rect(screen, COLORS['axis'], self.rect, 2, border_radius=8)

        text_surf = font.render(self.text, True, COLORS['button_text'])
        text_rect = text_surf.get_rect(center=self.rect.center)
        screen.blit(text_surf, text_rect)

    def check_hover(self, pos: Tuple[int, int]):
        """检查鼠标是否悬停在按钮上"""
        self.hovered = self.rect.collidepoint(pos)

    def is_clicked(self, pos: Tuple[int, int], event) -> bool:
        """检查按钮是否被点击"""
        if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
            return self.rect.collidepoint(pos)
        return False


class GradeChart:
    """成绩图表类"""

    def __init__(self, data: GradeData):
        self.data = data
        self.chart_type = "bar"  # bar, line, radar, pie
        self.selected_student = 0
        self.width = 800
        self.height = 600
        self.margin = 60
        self.chart_width = self.width - 2 * self.margin
        self.chart_height = self.height - 2 * self.margin

    def draw_bar_chart(self, screen: pygame.Surface, font: pygame.font.Font):
        """绘制柱状图 - 各科平均分"""
        averages = self.data.get_subject_averages()
        max_grade = 100
        bar_width = self.chart_width // len(self.data.subjects) - 10

        # 绘制坐标轴
        origin_x = self.margin
        origin_y = self.height - self.margin

        pygame.draw.line(screen, COLORS['axis'], (origin_x, origin_y),
                         (self.width - self.margin, origin_y), 2)
        pygame.draw.line(screen, COLORS['axis'], (origin_x, origin_y),
                         (origin_x, self.margin), 2)

        # 绘制网格线和刻度
        for i in range(0, 101, 20):
            y_pos = origin_y - (i / max_grade) * self.chart_height
            pygame.draw.line(screen, COLORS['grid'], (origin_x, y_pos),
                             (self.width - self.margin, y_pos), 1)

            # 绘制Y轴刻度标签
            label = font.render(str(i), True, COLORS['text'])
            screen.blit(label, (origin_x - 30, y_pos - 10))

        # 绘制柱状图
        for i, (subject, avg) in enumerate(zip(self.data.subjects, averages)):
            bar_height = (avg / max_grade) * self.chart_height
            x_pos = origin_x + i * (bar_width + 10) + 5

            color = COLORS['bar_colors'][i % len(COLORS['bar_colors'])]
            pygame.draw.rect(screen, color,
                             (x_pos, origin_y - bar_height, bar_width, bar_height))

            # 绘制科目标签
            subject_label = font.render(subject, True, COLORS['text'])
            screen.blit(subject_label, (x_pos, origin_y + 10))

            # 绘制数值标签
            value_label = font.render(f"{avg:.1f}", True, COLORS['text'])
            screen.blit(value_label, (x_pos, origin_y - bar_height - 20))

    def draw_line_chart(self, screen: pygame.Surface, font: pygame.font.Font):
        """绘制折线图 - 选中学生的各科成绩"""
        student_name = self.data.students[self.selected_student]
        grades = self.data.get_student_grades(student_name)
        max_grade = 100

        # 绘制标题
        title = font.render(f"{student_name}的成绩分布", True, COLORS['text'])
        screen.blit(title, (self.width // 2 - title.get_width() // 2, 20))

        # 绘制坐标轴
        origin_x = self.margin
        origin_y = self.height - self.margin

        pygame.draw.line(screen, COLORS['axis'], (origin_x, origin_y),
                         (self.width - self.margin, origin_y), 2)
        pygame.draw.line(screen, COLORS['axis'], (origin_x, origin_y),
                         (origin_x, self.margin), 2)

        # 绘制网格线和刻度
        for i in range(0, 101, 20):
            y_pos = origin_y - (i / max_grade) * self.chart_height
            pygame.draw.line(screen, COLORS['grid'], (origin_x, y_pos),
                             (self.width - self.margin, y_pos), 1)

            label = font.render(str(i), True, COLORS['text'])
            screen.blit(label, (origin_x - 30, y_pos - 10))

        # 绘制折线图
        points = []
        for i, (subject, grade) in enumerate(zip(self.data.subjects, grades)):
            x_pos = origin_x + (i / (len(grades) - 1)) * self.chart_width
            y_pos = origin_y - (grade / max_grade) * self.chart_height
            points.append((x_pos, y_pos))

            # 绘制数据点
            pygame.draw.circle(
                screen, COLORS['line_color'], (int(x_pos), int(y_pos)), 6)
            pygame.draw.circle(
                screen, COLORS['background'], (int(x_pos), int(y_pos)), 4)

            # 绘制科目标签
            subject_label = font.render(subject, True, COLORS['text'])
            screen.blit(
                subject_label, (x_pos - subject_label.get_width() // 2, origin_y + 10))

            # 绘制数值标签
            value_label = font.render(str(grade), True, COLORS['text'])
            screen.blit(
                value_label, (x_pos - value_label.get_width() // 2, y_pos - 25))

        # 连接数据点
        if len(points) > 1:
            pygame.draw.lines(screen, COLORS['line_color'], False, points, 3)

    def draw_radar_chart(self, screen: pygame.Surface, font: pygame.font.Font):
        """绘制雷达图 - 选中学生的各科成绩"""
        student_name = self.data.students[self.selected_student]
        grades = self.data.get_student_grades(student_name)
        max_grade = 100

        # 绘制标题
        title = font.render(f"{student_name}雷达图", True, COLORS['text'])
        screen.blit(title, (self.width // 2 - title.get_width() // 2, 20))

        # 雷达图参数
        center_x = self.width // 2
        center_y = self.height // 2
        radius = min(self.chart_width, self.chart_height) // 2 - 50

        # 绘制网格（同心圆）
        for r in range(20, 101, 20):
            circle_radius = (r / max_grade) * radius
            pygame.draw.circle(
                screen, COLORS['grid'], (center_x, center_y), int(circle_radius), 1)

            # 绘制刻度标签
            label = font.render(str(r), True, COLORS['text'])
            screen.blit(label, (center_x + circle_radius + 5, center_y - 10))

        # 计算角度
        angles = []
        points = []
        for i in range(len(grades)):
            angle = 2 * math.pi * i / len(grades) - math.pi / 2
            angles.append(angle)

            # 计算点的位置
            distance = (grades[i] / max_grade) * radius
            x = center_x + distance * math.cos(angle)
            y = center_y + distance * math.sin(angle)
            points.append((x, y))

            # 绘制科目标签
            subject_angle = angle
            label_x = center_x + (radius + 30) * math.cos(subject_angle)
            label_y = center_y + (radius + 30) * math.sin(subject_angle)
            subject_label = font.render(
                self.data.subjects[i], True, COLORS['text'])
            screen.blit(subject_label, (label_x - subject_label.get_width() // 2,
                                        label_y - subject_label.get_height() // 2))

        # 绘制雷达图区域
        if len(points) > 2:
            pygame.draw.polygon(
                screen, COLORS['line_color'][:3] + (100,), points)
            pygame.draw.polygon(screen, COLORS['line_color'], points, 2)

        # 绘制数据点
        for point in points:
            pygame.draw.circle(
                screen, COLORS['line_color'], (int(point[0]), int(point[1])), 6)
            pygame.draw.circle(
                screen, COLORS['background'], (int(point[0]), int(point[1])), 4)

    def draw_pie_chart(self, screen: pygame.font.Font, font: pygame.font.Font):
        """绘制饼图 - 选中学生的成绩分布"""
        student_name = self.data.students[self.selected_student]
        grades = self.data.get_student_grades(student_name)
        total = sum(grades)

        # 绘制标题
        title = font.render(f"{student_name}成绩占比", True, COLORS['text'])
        screen.blit(title, (self.width // 2 - title.get_width() // 2, 20))

        # 饼图参数
        center_x = self.width // 2
        center_y = self.height // 2
        radius = min(self.chart_width, self.chart_height) // 3

        # 绘制饼图
        start_angle = 0
        for i, (subject, grade) in enumerate(zip(self.data.subjects, grades)):
            percentage = grade / total
            end_angle = start_angle + 2 * math.pi * percentage

            color = COLORS['bar_colors'][i % len(COLORS['bar_colors'])]

            # 绘制扇形
            self.draw_pie_slice(screen, center_x, center_y, radius,
                                start_angle, end_angle, color)

            # 计算标签位置
            mid_angle = (start_angle + end_angle) / 2
            label_radius = radius + 40
            label_x = center_x + label_radius * math.cos(mid_angle)
            label_y = center_y + label_radius * math.sin(mid_angle)

            # 绘制标签
            percentage_text = f"{subject}: {percentage*100:.1f}%"
            label = font.render(percentage_text, True, COLORS['text'])
            screen.blit(label, (label_x - label.get_width() // 2,
                                label_y - label.get_height() // 2))

            start_angle = end_angle

    def draw_pie_slice(self, screen, center_x, center_y, radius, start_angle, end_angle, color):
        """绘制饼图的一个扇形"""
        points = [(center_x, center_y)]
        num_points = 20
        for i in range(num_points + 1):
            angle = start_angle + (end_angle - start_angle) * i / num_points
            x = center_x + radius * math.cos(angle)
            y = center_y + radius * math.sin(angle)
            points.append((x, y))

        pygame.draw.polygon(screen, color, points)
        pygame.draw.polygon(screen, COLORS['axis'], points, 2)

    def draw(self, screen: pygame.Surface, font: pygame.font.Font):
        """绘制当前图表"""
        if self.chart_type == "bar":
            self.draw_bar_chart(screen, font)
        elif self.chart_type == "line":
            self.draw_line_chart(screen, font)
        elif self.chart_type == "radar":
            self.draw_radar_chart(screen, font)
        elif self.chart_type == "pie":
            self.draw_pie_chart(screen, font)


class GradeVisualizer:
    """成绩可视化主程序"""

    def __init__(self):
        self.width = 1000
        self.height = 700
        self.screen = pygame.display.set_mode((self.width, self.height))
        pygame.display.set_caption("高级成绩统计图")

        self.clock = pygame.time.Clock()
        self.font = pygame.font.SysFont(None, 24)
        self.title_font = pygame.font.SysFont(None, 36)

        self.data = GradeData()
        self.chart = GradeChart(self.data)

        # 创建按钮
        button_width, button_height = 120, 40
        button_y = 650
        self.buttons = {
            "bar": Button(50, button_y, button_width, button_height, "各科平均分"),
            "line": Button(200, button_y, button_width, button_height, "学生成绩"),
            "radar": Button(350, button_y, button_width, button_height, "雷达图"),
            "pie": Button(500, button_y, button_width, button_height, "成绩占比"),
            "prev": Button(650, button_y, button_width, button_height, "上一个"),
            "next": Button(800, button_y, button_width, button_height, "下一个"),
            "refresh": Button(850, 600, 100, 30, "刷新数据")
        }

    def handle_events(self):
        """处理事件"""
        mouse_pos = pygame.mouse.get_pos()

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                return False

            # 检查按钮点击
            for chart_type, button in self.buttons.items():
                if chart_type in ["bar", "line", "radar", "pie"]:
                    if button.is_clicked(mouse_pos, event):
                        self.chart.chart_type = chart_type

                elif chart_type == "prev":
                    if button.is_clicked(mouse_pos, event):
                        self.chart.selected_student = (
                            self.chart.selected_student - 1) % len(self.data.students)

                elif chart_type == "next":
                    if button.is_clicked(mouse_pos, event):
                        self.chart.selected_student = (
                            self.chart.selected_student + 1) % len(self.data.students)

                elif chart_type == "refresh":
                    if button.is_clicked(mouse_pos, event):
                        self.data.generate_random_data()

            # 更新按钮悬停状态
            for button in self.buttons.values():
                button.check_hover(mouse_pos)

        return True

    def draw_interface(self):
        """绘制界面"""
        self.screen.fill(COLORS['background'])

        # 绘制标题
        title = self.title_font.render("高级成绩统计图", True, COLORS['text'])
        self.screen.blit(title, (self.width // 2 - title.get_width() // 2, 10))

        # 绘制当前学生信息（如果适用）
        if self.chart.chart_type in ["line", "radar", "pie"]:
            current_student = self.data.students[self.chart.selected_student]
            student_info = self.font.render(
                f"当前学生: {current_student}", True, COLORS['text'])
            self.screen.blit(student_info, (50, 50))

        # 绘制图表
        chart_surface = pygame.Surface((800, 600))
        chart_surface.fill(COLORS['background'])
        self.chart.draw(chart_surface, self.font)
        self.screen.blit(chart_surface, (100, 80))

        # 绘制按钮
        for button in self.buttons.values():
            button.draw(self.screen, self.font)

    def run(self):
        """运行主循环"""
        running = True
        while running:
            running = self.handle_events()
            self.draw_interface()
            pygame.display.flip()
            self.clock.tick(60)

        pygame.quit()
        sys.exit()


if __name__ == "__main__":
    app = GradeVisualizer()
    app.run()
