import pygame
import sys

pygame.init()
WIDTH, HEIGHT = 360, 520
SCREEN = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Pygame 计算器")

FONT_SMALL = pygame.font.SysFont(None, 30)
FONT_LARGE = pygame.font.SysFont(None, 48)

BG_COLOR = (30, 30, 30)
BTN_COLOR = (50, 50, 50)
BTN_HOVER = (70, 70, 70)
TXT_COLOR = (240, 240, 240)
ACCENT = (0, 150, 136)

# 按钮布局（文本, 行, 列, 跨列）
buttons = [
    [('C', 0, 0, 1), ('', 0, 1, 1), ('', 0, 2, 1), ('/', 0, 3, 1)],
    [('7', 1, 0, 1), ('8', 1, 1, 1), ('9', 1, 2, 1), ('*', 1, 3, 1)],
    [('4', 2, 0, 1), ('5', 2, 1, 1), ('6', 2, 2, 1), ('-', 2, 3, 1)],
    [('1', 3, 0, 1), ('2', 3, 1, 1), ('3', 3, 2, 1), ('+', 3, 3, 1)],
    [('0', 4, 0, 2), ('.', 4, 2, 1), ('=', 4, 3, 1)],
]

TOP_PADDING = 20
DISPLAY_H = 120
GRID_TOP = TOP_PADDING + DISPLAY_H + 10
ROWS = 5
COLS = 4
GAP = 10
btn_width = (WIDTH - (COLS + 1) * GAP) // COLS
btn_height = (HEIGHT - GRID_TOP - (ROWS + 1) * GAP) // ROWS

# 创建按钮矩形和文本
btns = []
for row in buttons:
    for text, r, c, span in row:
        w = btn_width * span + GAP * (span - 1)
        x = GAP + c * (btn_width + GAP)
        y = GRID_TOP + r * (btn_height + GAP)
        rect = pygame.Rect(x, y, w, btn_height)
        btns.append((text, rect))


def draw_display(expr, result=None):
    # 背景
    pygame.draw.rect(SCREEN, BG_COLOR, (0, 0, WIDTH, DISPLAY_H + TOP_PADDING))
    # 显示表达式
    expr_surf = FONT_SMALL.render(expr, True, TXT_COLOR)
    expr_rect = expr_surf.get_rect(right=WIDTH - 10, top=TOP_PADDING + 10)
    SCREEN.blit(expr_surf, expr_rect)
    # 显示结果（较大）
    res_text = result if result is not None else ''
    res_surf = FONT_LARGE.render(res_text, True, ACCENT)
    res_rect = res_surf.get_rect(
        right=WIDTH - 10, bottom=DISPLAY_H + TOP_PADDING - 10)
    SCREEN.blit(res_surf, res_rect)


def safe_eval(expr):
    # 仅允许数字、运算符和小数点
    allowed = set('0123456789.+-*/() ')
    if any(ch not in allowed for ch in expr):
        raise ValueError("非法字符")
    # 防止以运算符开头或连续两个运算符（简单检查）
    # 这里使用 Python eval 来计算结果
    return eval(expr)


def main():
    clock = pygame.time.Clock()
    expr = ''
    result = ''
    running = True

    while running:
        mouse_pos = pygame.mouse.get_pos()
        mouse_pressed = pygame.mouse.get_pressed()[0]

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
            elif event.type == pygame.KEYDOWN:
                # 允许键盘输入（数字、运算符、回车、退格、c）
                if event.key == pygame.K_RETURN or event.key == pygame.K_EQUALS:
                    try:
                        val = safe_eval(expr)
                        result = str(val)
                    except Exception as e:
                        result = "Error"
                elif event.key == pygame.K_BACKSPACE:
                    expr = expr[:-1]
                elif event.key == pygame.K_c:
                    expr = ''
                    result = ''
                else:
                    key = event.unicode
                    if key in '0123456789.+-*/()':
                        expr += key

        SCREEN.fill(BG_COLOR)
        draw_display(expr, result)

        for text, rect in btns:
            # 忽略空按钮（布局占位）
            if not text:
                continue
            # 鼠标悬停颜色
            color = BTN_COLOR
            if rect.collidepoint(mouse_pos):
                color = BTN_HOVER
                if mouse_pressed:
                    color = ACCENT
            pygame.draw.rect(SCREEN, color, rect, border_radius=8)
            # 边框
            pygame.draw.rect(SCREEN, (20, 20, 20), rect, 2, border_radius=8)
            # 按钮文字
            label = FONT_LARGE.render(text, True, TXT_COLOR)
            label_rect = label.get_rect(center=rect.center)
            SCREEN.blit(label, label_rect)

            if rect.collidepoint(mouse_pos) and mouse_pressed:
                # 处理按钮点击（简单去抖：等下一帧）
                if text == 'C':
                    expr = ''
                    result = ''
                elif text == '=':
                    try:
                        val = safe_eval(expr)
                        result = str(val)
                    except Exception:
                        result = "Error"
                else:
                    # 如果是数字或运算符或点
                    if text in '0123456789.+-*/()':
                        # 如果前面有结果且用户开始输入数字/点，则清空表达式以开始新运算
                        if result and expr == str(result):
                            expr = ''
                            result = ''
                        expr += text

        pygame.display.flip()
        clock.tick(30)

    pygame.quit()
    sys.exit()


if __name__ == '__main__':
    main()
