import pygame
import datetime
import calendar
import json
import os
from pygame.locals import *

# 初始化 Pygame
pygame.init()

# 颜色定义
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
GRAY = (200, 200, 200)
LIGHT_GRAY = (240, 240, 240)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
GREEN = (0, 200, 0)
YELLOW = (255, 255, 0)
DARK_GRAY = (100, 100, 100)
LIGHT_BLUE = (173, 216, 230)

# 屏幕尺寸
WIDTH, HEIGHT = 800, 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Calendar Reminder & Planner")

# 字体
font_large = pygame.font.Font(None, 36)
font_medium = pygame.font.Font(None, 28)
font_small = pygame.font.Font(None, 24)

# 日历区域参数
CAL_TOP = 80          # 日历区域顶部 Y 坐标
CAL_LEFT = 50         # 日历区域左侧 X 坐标
CELL_WIDTH = (WIDTH - 2 * CAL_LEFT) // 7   # 每个单元格宽度
CELL_HEIGHT = (HEIGHT - CAL_TOP - 100) // 6  # 每个单元格高度（预留底部按钮空间）

# 数据文件路径
DATA_FILE = "plans.json"

def load_plans():
    """从文件加载计划数据"""
    if os.path.exists(DATA_FILE):
        with open(DATA_FILE, 'r', encoding='utf-8') as f:
            return json.load(f)
    return {}

def save_plans(plans):
    """保存计划数据到文件"""
    with open(DATA_FILE, 'w', encoding='utf-8') as f:
        json.dump(plans, f, ensure_ascii=False, indent=2)

def get_month_days(year, month):
    """返回该月的天数（1-31）"""
    return calendar.monthrange(year, month)[1]

def get_month_start_weekday(year, month):
    """返回该月第一天是星期几（0=周一，6=周日）"""
    return calendar.monthrange(year, month)[0]

def draw_text(text, font, color, x, y, center=False):
    """绘制文本"""
    img = font.render(text, True, color)
    if center:
        rect = img.get_rect(center=(x, y))
        screen.blit(img, rect)
    else:
        screen.blit(img, (x, y))

def draw_button(text, x, y, w, h, color, hover_color):
    """
    绘制按钮，不处理点击，仅返回按钮矩形区域。
    调用方需自行判断点击。
    """
    mouse = pygame.mouse.get_pos()
    rect = pygame.Rect(x, y, w, h)
    # 鼠标悬停效果
    if rect.collidepoint(mouse):
        pygame.draw.rect(screen, hover_color, rect)
    else:
        pygame.draw.rect(screen, color, rect)
    pygame.draw.rect(screen, BLACK, rect, 2)  # 边框
    draw_text(text, font_medium, BLACK, x + w//2, y + h//2, center=True)
    return rect

class CalendarApp:
    def __init__(self):
        self.current_date = datetime.date.today()
        self.year = self.current_date.year
        self.month = self.current_date.month
        self.plans = load_plans()           # 字典 {date_str: [plan1, plan2, ...]}
        self.selected_date = None            # 当前选中的日期 (year, month, day)
        self.show_plan_panel = False         # 是否显示计划面板
        self.new_plan_text = ""              # 正在输入的新计划文本
        self.input_active = False            # 输入框是否激活
        self.cursor_visible = True           # 光标闪烁
        self.last_cursor_time = pygame.time.get_ticks()
        self.del_rects = []                  # 存储删除按钮区域与计划索引的列表

        # 计划面板的尺寸和位置
        self.panel_rect = pygame.Rect(WIDTH//2 - 200, HEIGHT//2 - 200, 400, 400)

    def get_current_calendar(self):
        """获取当前月份的日历数据，返回二维列表，每个元素为 (year, month, day) 或 None"""
        first_weekday = get_month_start_weekday(self.year, self.month)
        days_in_month = get_month_days(self.year, self.month)
        cal = []
        day = 1
        for row in range(6):
            week = []
            for col in range(7):
                if row == 0 and col < first_weekday:
                    week.append(None)
                elif day <= days_in_month:
                    week.append((self.year, self.month, day))
                    day += 1
                else:
                    week.append(None)
            cal.append(week)
        return cal

    def draw_calendar(self):
        """绘制日历网格和日期数字"""
        # 月份标题（英文）
        month_names = ["January", "February", "March", "April", "May", "June",
                       "July", "August", "September", "October", "November", "December"]
        title = f"{month_names[self.month-1]} {self.year}"
        draw_text(title, font_large, BLACK, WIDTH//2, CAL_TOP - 40, center=True)

        # 星期标题（英文）
        weekdays = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
        for i, day in enumerate(weekdays):
            x = CAL_LEFT + i * CELL_WIDTH + CELL_WIDTH//2
            y = CAL_TOP + 5
            draw_text(day, font_medium, BLACK, x, y, center=True)

        # 日历数据
        cal = self.get_current_calendar()
        for row_idx, week in enumerate(cal):
            for col_idx, date_tuple in enumerate(week):
                x = CAL_LEFT + col_idx * CELL_WIDTH
                y = CAL_TOP + 30 + row_idx * CELL_HEIGHT
                rect = pygame.Rect(x, y, CELL_WIDTH, CELL_HEIGHT)

                if date_tuple:
                    # 判断是否是今天
                    today = datetime.date.today()
                    is_today = (date_tuple[0] == today.year and
                                date_tuple[1] == today.month and
                                date_tuple[2] == today.day)
                    # 判断是否被选中
                    is_selected = (self.selected_date and
                                   self.selected_date[0] == date_tuple[0] and
                                   self.selected_date[1] == date_tuple[1] and
                                   self.selected_date[2] == date_tuple[2])
                    bg_color = LIGHT_BLUE if is_selected else (LIGHT_GRAY if is_today else WHITE)
                    pygame.draw.rect(screen, bg_color, rect)
                    pygame.draw.rect(screen, GRAY, rect, 1)
                    # 日期数字
                    day_text = str(date_tuple[2])
                    draw_text(day_text, font_medium, BLACK, x + 5, y + 5)
                    # 有计划则显示红点
                    date_str = f"{date_tuple[0]}-{date_tuple[1]:02d}-{date_tuple[2]:02d}"
                    if date_str in self.plans and self.plans[date_str]:
                        pygame.draw.circle(screen, RED, (x + CELL_WIDTH - 10, y + 10), 5)
                else:
                    pygame.draw.rect(screen, LIGHT_GRAY, rect)
                    pygame.draw.rect(screen, GRAY, rect, 1)

    def draw_plan_panel(self):
        """绘制计划面板（模态），返回按钮矩形供事件循环使用"""
        # 半透明背景
        s = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
        s.fill((0, 0, 0, 128))
        screen.blit(s, (0, 0))

        # 面板背景
        pygame.draw.rect(screen, WHITE, self.panel_rect)
        pygame.draw.rect(screen, BLACK, self.panel_rect, 2)

        # 标题（英文）
        if self.selected_date:
            y, m, d = self.selected_date
            title = f"Plans for {y}-{m:02d}-{d:02d}"
            draw_text(title, font_large, BLACK, self.panel_rect.centerx, self.panel_rect.top + 20, center=True)

        # 计划列表区域
        list_rect = pygame.Rect(self.panel_rect.x + 20, self.panel_rect.y + 70,
                                self.panel_rect.width - 40, 200)
        pygame.draw.rect(screen, LIGHT_GRAY, list_rect)
        pygame.draw.rect(screen, BLACK, list_rect, 1)

        date_str = f"{self.selected_date[0]}-{self.selected_date[1]:02d}-{self.selected_date[2]:02d}"
        plans = self.plans.get(date_str, [])

        # 存储删除按钮区域和对应的计划索引
        self.del_rects = []
        y_offset = list_rect.y + 5
        for idx, plan in enumerate(plans):
            if idx >= 8:  # 限制显示数量，超出显示“...”
                draw_text("...", font_small, BLACK, list_rect.x + 5, y_offset)
                break
            draw_text(plan, font_small, BLACK, list_rect.x + 5, y_offset)
            # 删除按钮
            del_rect = pygame.Rect(list_rect.right - 25, y_offset, 20, 20)
            pygame.draw.rect(screen, RED, del_rect)
            draw_text("X", font_small, WHITE, del_rect.centerx, del_rect.centery, center=True)
            self.del_rects.append((del_rect, idx))
            y_offset += 25

        # 输入框
        input_rect = pygame.Rect(self.panel_rect.x + 20, self.panel_rect.bottom - 80,
                                 self.panel_rect.width - 40, 30)
        pygame.draw.rect(screen, WHITE, input_rect)
        pygame.draw.rect(screen, BLACK, input_rect, 1)
        display_text = self.new_plan_text
        if self.input_active and self.cursor_visible:
            display_text += "|"
        draw_text(display_text, font_small, BLACK, input_rect.x + 5, input_rect.y + 5)

        # 添加按钮（英文）
        add_rect = draw_button("Add", self.panel_rect.x + 20, self.panel_rect.bottom - 40,
                               80, 30, GREEN, (0, 255, 0))
        # 关闭按钮
        close_rect = draw_button("X", self.panel_rect.right - 40, self.panel_rect.top + 10,
                                 30, 30, RED, (255, 100, 100))

        return input_rect, add_rect, close_rect

    def handle_date_click(self, pos):
        """处理日历格子点击"""
        cal = self.get_current_calendar()
        for row_idx, week in enumerate(cal):
            for col_idx, date_tuple in enumerate(week):
                if date_tuple:
                    x = CAL_LEFT + col_idx * CELL_WIDTH
                    y = CAL_TOP + 30 + row_idx * CELL_HEIGHT
                    rect = pygame.Rect(x, y, CELL_WIDTH, CELL_HEIGHT)
                    if rect.collidepoint(pos):
                        self.selected_date = date_tuple
                        self.show_plan_panel = True
                        self.new_plan_text = ""
                        self.input_active = False
                        return True
        return False

    def run(self):
        clock = pygame.time.Clock()
        running = True

        # 定义按钮区域
        prev_btn = pygame.Rect(50, 20, 80, 40)
        next_btn = pygame.Rect(WIDTH - 130, 20, 80, 40)

        while running:
            screen.fill(WHITE)

            # 绘制按钮（英文）
            draw_button("< Prev", prev_btn.x, prev_btn.y, prev_btn.w, prev_btn.h, GRAY, DARK_GRAY)
            draw_button("Next >", next_btn.x, next_btn.y, next_btn.w, next_btn.h, GRAY, DARK_GRAY)
            self.draw_calendar()

            # 计划面板相关变量
            input_rect = None
            add_rect = None
            close_rect = None
            if self.show_plan_panel:
                input_rect, add_rect, close_rect = self.draw_plan_panel()

            # 事件处理
            for event in pygame.event.get():
                if event.type == QUIT:
                    running = False

                elif event.type == MOUSEBUTTONDOWN:
                    mouse_pos = event.pos

                    # 处理月份切换按钮
                    if prev_btn.collidepoint(mouse_pos):
                        if self.month == 1:
                            self.month = 12
                            self.year -= 1
                        else:
                            self.month -= 1
                        self.selected_date = None
                        self.show_plan_panel = False

                    elif next_btn.collidepoint(mouse_pos):
                        if self.month == 12:
                            self.month = 1
                            self.year += 1
                        else:
                            self.month += 1
                        self.selected_date = None
                        self.show_plan_panel = False

                    # 处理计划面板内的交互（如果面板打开）
                    elif self.show_plan_panel:
                        # 关闭按钮
                        if close_rect and close_rect.collidepoint(mouse_pos):
                            self.show_plan_panel = False
                            self.selected_date = None
                            self.input_active = False

                        # 添加按钮
                        elif add_rect and add_rect.collidepoint(mouse_pos):
                            if self.new_plan_text.strip():
                                date_str = f"{self.selected_date[0]}-{self.selected_date[1]:02d}-{self.selected_date[2]:02d}"
                                if date_str not in self.plans:
                                    self.plans[date_str] = []
                                self.plans[date_str].append(self.new_plan_text.strip())
                                save_plans(self.plans)
                                self.new_plan_text = ""
                                self.input_active = False

                        # 删除按钮
                        else:
                            for del_rect, idx in self.del_rects:
                                if del_rect.collidepoint(mouse_pos):
                                    date_str = f"{self.selected_date[0]}-{self.selected_date[1]:02d}-{self.selected_date[2]:02d}"
                                    if date_str in self.plans and idx < len(self.plans[date_str]):
                                        del self.plans[date_str][idx]
                                        if not self.plans[date_str]:
                                            del self.plans[date_str]
                                        save_plans(self.plans)
                                    break

                        # 输入框激活
                        if input_rect and input_rect.collidepoint(mouse_pos):
                            self.input_active = True
                        else:
                            self.input_active = False

                    else:
                        # 点击日历区域
                        self.handle_date_click(mouse_pos)

                elif event.type == KEYDOWN:
                    if self.show_plan_panel and self.input_active:
                        if event.key == K_RETURN:
                            # 回车添加计划
                            if self.new_plan_text.strip():
                                date_str = f"{self.selected_date[0]}-{self.selected_date[1]:02d}-{self.selected_date[2]:02d}"
                                if date_str not in self.plans:
                                    self.plans[date_str] = []
                                self.plans[date_str].append(self.new_plan_text.strip())
                                save_plans(self.plans)
                                self.new_plan_text = ""
                                self.input_active = False
                        elif event.key == K_BACKSPACE:
                            self.new_plan_text = self.new_plan_text[:-1]
                        else:
                            self.new_plan_text += event.unicode

            # 光标闪烁
            if self.show_plan_panel and self.input_active:
                now = pygame.time.get_ticks()
                if now - self.last_cursor_time > 500:
                    self.cursor_visible = not self.cursor_visible
                    self.last_cursor_time = now

            pygame.display.flip()
            clock.tick(30)

        pygame.quit()

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