import tkinter as tk
from tkinter import ttk, messagebox
import json
import os
from datetime import datetime, date, timedelta
import threading
import time
from tkinter.font import Font

class CalendarReminderApp:
    def __init__(self, root):
        self.root = root
        self.root.title("日历提醒与计划表")
        self.root.geometry("1000x700")
        
        # 设置样式
        self.setup_styles()
        
        # 数据文件路径
        self.data_file = "calendar_events.json"
        self.events = self.load_events()
        
        # 当前显示的日期
        self.current_date = date.today()
        
        # 创建界面
        self.setup_ui()
        
        # 启动提醒检查线程
        self.reminder_active = True
        self.reminder_thread = threading.Thread(target=self.check_reminders, daemon=True)
        self.reminder_thread.start()
        
        # 绑定窗口关闭事件
        self.root.protocol("WM_DELETE_WINDOW", self.on_closing)
        
        # 刷新日历显示
        self.update_calendar()
    
    def setup_styles(self):
        """设置界面样式"""
        self.style = ttk.Style()
        self.style.theme_use('clam')
        
        # 自定义颜色
        self.bg_color = "#f0f8ff"
        self.header_bg = "#4a90e2"
        self.header_fg = "white"
        self.today_bg = "#ffeb3b"
        self.event_day_bg = "#ffcccb"
        self.weekend_bg = "#f5f5f5"
        
        self.root.configure(bg=self.bg_color)
    
    def setup_ui(self):
        """设置用户界面"""
        # 创建主框架
        main_frame = tk.Frame(self.root, bg=self.bg_color)
        main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        # 顶部控制栏
        control_frame = tk.Frame(main_frame, bg=self.bg_color)
        control_frame.pack(fill=tk.X, pady=(0, 10))
        
        # 月份导航按钮
        nav_frame = tk.Frame(control_frame, bg=self.bg_color)
        nav_frame.pack(side=tk.LEFT)
        
        tk.Button(nav_frame, text="◀ 上月", command=self.prev_month, 
                 bg=self.header_bg, fg=self.header_fg, font=("Arial", 10)).pack(side=tk.LEFT, padx=2)
        tk.Button(nav_frame, text="今天", command=self.go_today,
                 bg="#4CAF50", fg="white", font=("Arial", 10)).pack(side=tk.LEFT, padx=2)
        tk.Button(nav_frame, text="下月 ▶", command=self.next_month,
                 bg=self.header_bg, fg=self.header_fg, font=("Arial", 10)).pack(side=tk.LEFT, padx=2)
        
        # 当前月份显示
        self.month_label = tk.Label(control_frame, font=("Arial", 16, "bold"), 
                                   bg=self.bg_color, fg="#333")
        self.month_label.pack(side=tk.LEFT, padx=20)
        
        # 分隔线
        ttk.Separator(main_frame, orient=tk.HORIZONTAL).pack(fill=tk.X, pady=5)
        
        # 创建左右两个主要区域
        content_frame = tk.Frame(main_frame, bg=self.bg_color)
        content_frame.pack(fill=tk.BOTH, expand=True)
        
        # 左侧日历区域 (2/3宽度)
        left_frame = tk.Frame(content_frame, bg=self.bg_color)
        left_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(0, 10))
        
        # 右侧事件管理区域 (1/3宽度)
        right_frame = tk.Frame(content_frame, bg=self.bg_color, width=300)
        right_frame.pack(side=tk.RIGHT, fill=tk.BOTH)
        right_frame.pack_propagate(False)
        
        # 创建日历
        self.create_calendar(left_frame)
        
        # 创建事件管理区域
        self.create_event_manager(right_frame)
    
    def create_calendar(self, parent):
        """创建日历显示区域"""
        # 星期标题
        weekdays_frame = tk.Frame(parent, bg=self.bg_color)
        weekdays_frame.pack(fill=tk.X)
        
        weekdays = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]
        for i, day in enumerate(weekdays):
            bg_color = self.header_bg
            fg_color = self.header_fg
            if i >= 5:  # 周末
                bg_color = "#e64a19"
            
            label = tk.Label(weekdays_frame, text=day, font=("Arial", 10, "bold"),
                            bg=bg_color, fg=fg_color, width=10, height=2)
            label.grid(row=0, column=i, padx=1, pady=1, sticky="nsew")
            weekdays_frame.grid_columnconfigure(i, weight=1)
        
        # 日历网格框架
        self.calendar_frame = tk.Frame(parent, bg=self.bg_color)
        self.calendar_frame.pack(fill=tk.BOTH, expand=True)
        
        # 初始化日历网格
        self.day_labels = []
        for i in range(6):  # 最多6行
            row_labels = []
            for j in range(7):  # 7列
                label = tk.Label(self.calendar_frame, font=("Arial", 10),
                               width=10, height=5, relief=tk.RAISED, borderwidth=1)
                label.grid(row=i, column=j, padx=1, pady=1, sticky="nsew")
                label.bind("<Button-1>", lambda e, row=i, col=j: self.on_day_click(row, col))
                row_labels.append(label)
            self.day_labels.append(row_labels)
            self.calendar_frame.grid_rowconfigure(i, weight=1)
        
        for j in range(7):
            self.calendar_frame.grid_columnconfigure(j, weight=1)
    
    def create_event_manager(self, parent):
        """创建事件管理区域"""
        # 标题
        tk.Label(parent, text="事件管理", font=("Arial", 14, "bold"),
                bg=self.bg_color).pack(pady=(0, 10))
        
        # 选中的日期显示
        self.selected_date_label = tk.Label(parent, text="未选择日期", 
                                          font=("Arial", 12), bg=self.bg_color)
        self.selected_date_label.pack(pady=(0, 10))
        
        # 事件列表框架
        list_frame = tk.Frame(parent, bg=self.bg_color)
        list_frame.pack(fill=tk.BOTH, expand=True, pady=(0, 10))
        
        # 事件列表标题
        tk.Label(list_frame, text="当天事件:", font=("Arial", 11, "bold"),
                bg=self.bg_color).pack(anchor=tk.W)
        
        # 事件列表框
        event_list_frame = tk.Frame(list_frame, bg="white", relief=tk.SUNKEN, borderwidth=1)
        event_list_frame.pack(fill=tk.BOTH, expand=True, pady=(5, 0))
        
        # 创建滚动条
        scrollbar = tk.Scrollbar(event_list_frame)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        
        # 创建事件列表
        self.event_listbox = tk.Listbox(event_list_frame, yscrollcommand=scrollbar.set,
                                       font=("Arial", 10), selectmode=tk.SINGLE)
        self.event_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        scrollbar.config(command=self.event_listbox.yview)
        
        # 按钮框架
        button_frame = tk.Frame(parent, bg=self.bg_color)
        button_frame.pack(fill=tk.X, pady=(0, 10))
        
        tk.Button(button_frame, text="添加事件", command=self.add_event_dialog,
                 bg="#4CAF50", fg="white", font=("Arial", 10)).pack(side=tk.LEFT, fill=tk.X, expand=True, padx=2)
        tk.Button(button_frame, text="编辑事件", command=self.edit_event,
                 bg="#2196F3", fg="white", font=("Arial", 10)).pack(side=tk.LEFT, fill=tk.X, expand=True, padx=2)
        tk.Button(button_frame, text="删除事件", command=self.delete_event,
                 bg="#f44336", fg="white", font=("Arial", 10)).pack(side=tk.LEFT, fill=tk.X, expand=True, padx=2)
        
        # 事件详情框架
        detail_frame = tk.Frame(parent, bg=self.bg_color, relief=tk.GROOVE, borderwidth=2)
        detail_frame.pack(fill=tk.BOTH, expand=True)
        
        tk.Label(detail_frame, text="事件详情:", font=("Arial", 11, "bold"),
                bg=self.bg_color).pack(anchor=tk.W, pady=(5, 0))
        
        self.event_detail_text = tk.Text(detail_frame, font=("Arial", 10), 
                                       height=8, wrap=tk.WORD, state=tk.DISABLED)
        self.event_detail_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
    
    def update_calendar(self):
        """更新日历显示"""
        # 更新月份标签
        year = self.current_date.year
        month = self.current_date.month
        self.month_label.config(text=f"{year}年{month}月")
        
        # 获取月份的第一天和最后一天
        first_day = date(year, month, 1)
        last_day = date(year, month + 1, 1) - timedelta(days=1) if month < 12 else date(year + 1, 1, 1) - timedelta(days=1)
        
        # 获取第一天是星期几 (0=周一, ..., 6=周日)
        first_weekday = first_day.weekday()  # 周一为0
        
        # 清空所有日期标签
        for i in range(6):
            for j in range(7):
                label = self.day_labels[i][j]
                label.config(text="", bg="white", fg="black")
        
        # 填充日期
        current_day = 1
        today = date.today()
        
        for i in range(6):
            for j in range(7):
                if i == 0 and j < first_weekday:
                    continue
                
                if current_day > last_day.day:
                    break
                
                label = self.day_labels[i][j]
                label.config(text=str(current_day))
                
                # 检查是否为今天
                if (year == today.year and month == today.month and current_day == today.day):
                    label.config(bg=self.today_bg, font=("Arial", 10, "bold"))
                # 检查是否为周末
                elif j >= 5:  # 周六或周日
                    label.config(bg=self.weekend_bg, fg="red")
                else:
                    label.config(bg="white")
                
                # 检查该日期是否有事件
                event_date = date(year, month, current_day)
                date_str = event_date.isoformat()
                if date_str in self.events and self.events[date_str]:
                    # 有事件的日期用不同背景色标记
                    if not (year == today.year and month == today.month and current_day == today.day):
                        label.config(bg=self.event_day_bg)
                
                current_day += 1
        
        # 更新事件列表
        self.update_event_list()
    
    def update_event_list(self):
        """更新事件列表"""
        # 清空当前列表
        self.event_listbox.delete(0, tk.END)
        self.event_detail_text.config(state=tk.NORMAL)
        self.event_detail_text.delete(1.0, tk.END)
        self.event_detail_text.config(state=tk.DISABLED)
        
        # 如果没有选中日期，使用当前日期
        if not hasattr(self, 'selected_calendar_date') or self.selected_calendar_date is None:
            self.selected_calendar_date = date.today()
        
        date_str = self.selected_calendar_date.isoformat()
        
        # 更新选中日期标签
        self.selected_date_label.config(text=f"选中日期: {self.selected_calendar_date}")
        
        # 显示该日期的事件
        if date_str in self.events:
            for i, event in enumerate(self.events[date_str]):
                time_str = event.get('time', '全天')
                title = event.get('title', '无标题')
                self.event_listbox.insert(tk.END, f"{time_str} - {title}")
        
        # 绑定列表选择事件
        self.event_listbox.bind('<<ListboxSelect>>', self.on_event_select)
    
    def on_day_click(self, row, col):
        """处理日期点击事件"""
        # 获取点击的日期
        year = self.current_date.year
        month = self.current_date.month
        
        # 获取月份的第一天
        first_day = date(year, month, 1)
        first_weekday = first_day.weekday()  # 周一为0
        
        # 计算点击的日期
        day_offset = row * 7 + col - first_weekday
        if day_offset < 0 or day_offset >= 31:  # 简单检查
            return
        
        try:
            clicked_date = date(year, month, day_offset + 1)
            self.selected_calendar_date = clicked_date
            
            # 更新事件列表
            self.update_event_list()
            
            # 可选：高亮选中的日期
            self.highlight_selected_day(row, col)
        except ValueError:
            pass  # 无效日期
    
    def highlight_selected_day(self, row, col):
        """高亮选中的日期"""
        # 重置所有日期的边框
        for i in range(6):
            for j in range(7):
                label = self.day_labels[i][j]
                label.config(relief=tk.RAISED)
        
        # 高亮选中的日期
        if 0 <= row < 6 and 0 <= col < 7:
            self.day_labels[row][col].config(relief=tk.SUNKEN)
    
    def on_event_select(self, event):
        """处理事件选择"""
        selection = self.event_listbox.curselection()
        if not selection:
            return
        
        index = selection[0]
        date_str = self.selected_calendar_date.isoformat()
        
        if date_str in self.events and index < len(self.events[date_str]):
            event_data = self.events[date_str][index]
            
            # 在详情区域显示事件信息
            self.event_detail_text.config(state=tk.NORMAL)
            self.event_detail_text.delete(1.0, tk.END)
            
            details = f"标题: {event_data.get('title', '无标题')}\n"
            details += f"时间: {event_data.get('time', '全天')}\n"
            details += f"日期: {self.selected_calendar_date}\n"
            details += f"提醒: {'是' if event_data.get('reminder', False) else '否'}\n"
            details += f"提醒时间: {event_data.get('reminder_time', '不提醒')}\n"
            details += "\n描述:\n"
            details += event_data.get('description', '无描述')
            
            self.event_detail_text.insert(1.0, details)
            self.event_detail_text.config(state=tk.DISABLED)
    
    def add_event_dialog(self):
        """打开添加事件对话框"""
        if not hasattr(self, 'selected_calendar_date') or self.selected_calendar_date is None:
            messagebox.showwarning("警告", "请先在日历中选择一个日期")
            return
        
        # 创建对话框
        dialog = tk.Toplevel(self.root)
        dialog.title("添加新事件")
        dialog.geometry("400x500")
        dialog.transient(self.root)
        dialog.grab_set()
        
        # 对话框内容
        tk.Label(dialog, text="添加新事件", font=("Arial", 14, "bold")).pack(pady=10)
        
        # 事件标题
        tk.Label(dialog, text="事件标题:", anchor=tk.W).pack(fill=tk.X, padx=20, pady=(10, 0))
        title_entry = tk.Entry(dialog, font=("Arial", 10))
        title_entry.pack(fill=tk.X, padx=20, pady=(0, 10))
        title_entry.insert(0, "新事件")
        
        # 事件时间
        tk.Label(dialog, text="时间 (HH:MM 或 '全天'):", anchor=tk.W).pack(fill=tk.X, padx=20, pady=(0, 0))
        time_entry = tk.Entry(dialog, font=("Arial", 10))
        time_entry.pack(fill=tk.X, padx=20, pady=(0, 10))
        time_entry.insert(0, "09:00")
        
        # 日期显示
        tk.Label(dialog, text=f"日期: {self.selected_calendar_date}", anchor=tk.W).pack(fill=tk.X, padx=20, pady=(0, 10))
        
        # 提醒设置
        reminder_var = tk.BooleanVar(value=True)
        tk.Checkbutton(dialog, text="设置提醒", variable=reminder_var).pack(anchor=tk.W, padx=20)
        
        tk.Label(dialog, text="提前提醒时间 (分钟):", anchor=tk.W).pack(fill=tk.X, padx=20, pady=(0, 0))
        reminder_time_entry = tk.Entry(dialog, font=("Arial", 10))
        reminder_time_entry.pack(fill=tk.X, padx=20, pady=(0, 10))
        reminder_time_entry.insert(0, "15")
        
        # 事件描述
        tk.Label(dialog, text="事件描述:", anchor=tk.W).pack(fill=tk.X, padx=20, pady=(0, 0))
        desc_text = tk.Text(dialog, font=("Arial", 10), height=8)
        desc_text.pack(fill=tk.BOTH, padx=20, pady=(0, 10), expand=True)
        desc_text.insert(1.0, "请输入事件描述...")
        
        def save_event():
            """保存事件"""
            title = title_entry.get().strip()
            time_str = time_entry.get().strip()
            description = desc_text.get(1.0, tk.END).strip()
            reminder = reminder_var.get()
            reminder_time = reminder_time_entry.get().strip()
            
            if not title:
                messagebox.showwarning("警告", "事件标题不能为空")
                return
            
            # 验证时间格式
            if time_str != "全天":
                try:
                    datetime.strptime(time_str, "%H:%M")
                except ValueError:
                    messagebox.showwarning("警告", "时间格式应为 HH:MM 或 '全天'")
                    return
            
            # 验证提醒时间
            if reminder:
                try:
                    int(reminder_time)
                except ValueError:
                    messagebox.showwarning("警告", "提醒时间应为数字")
                    return
            
            # 创建事件对象
            event = {
                'title': title,
                'time': time_str,
                'description': description,
                'reminder': reminder,
                'reminder_time': int(reminder_time) if reminder else 0,
                'created': datetime.now().isoformat()
            }
            
            # 保存到数据
            date_str = self.selected_calendar_date.isoformat()
            if date_str not in self.events:
                self.events[date_str] = []
            
            self.events[date_str].append(event)
            self.save_events()
            
            # 更新界面
            self.update_calendar()
            dialog.destroy()
            
            messagebox.showinfo("成功", "事件已添加")
        
        def cancel():
            dialog.destroy()
        
        # 按钮框架
        button_frame = tk.Frame(dialog)
        button_frame.pack(fill=tk.X, padx=20, pady=10)
        
        tk.Button(button_frame, text="保存", command=save_event, 
                 bg="#4CAF50", fg="white").pack(side=tk.RIGHT, padx=5)
        tk.Button(button_frame, text="取消", command=cancel).pack(side=tk.RIGHT, padx=5)
    
    def edit_event(self):
        """编辑选中的事件"""
        selection = self.event_listbox.curselection()
        if not selection:
            messagebox.showwarning("警告", "请先选择一个事件")
            return
        
        # 这里实现编辑功能
        messagebox.showinfo("提示", "编辑功能与添加类似，限于篇幅这里不展开实现")
    
    def delete_event(self):
        """删除选中的事件"""
        selection = self.event_listbox.curselection()
        if not selection:
            messagebox.showwarning("警告", "请先选择一个事件")
            return
        
        index = selection[0]
        date_str = self.selected_calendar_date.isoformat()
        
        if date_str in self.events and index < len(self.events[date_str]):
            if messagebox.askyesno("确认", "确定要删除这个事件吗？"):
                del self.events[date_str][index]
                
                # 如果该日期没有事件了，删除日期键
                if not self.events[date_str]:
                    del self.events[date_str]
                
                self.save_events()
                self.update_calendar()
    
    def prev_month(self):
        """切换到上个月"""
        year = self.current_date.year
        month = self.current_date.month
        
        if month == 1:
            year -= 1
            month = 12
        else:
            month -= 1
        
        self.current_date = date(year, month, 1)
        self.update_calendar()
    
    def next_month(self):
        """切换到下个月"""
        year = self.current_date.year
        month = self.current_date.month
        
        if month == 12:
            year += 1
            month = 1
        else:
            month += 1
        
        self.current_date = date(year, month, 1)
        self.update_calendar()
    
    def go_today(self):
        """切换到今天"""
        self.current_date = date.today()
        self.selected_calendar_date = date.today()
        self.update_calendar()
    
    def check_reminders(self):
        """检查提醒的后台线程"""
        while self.reminder_active:
            now = datetime.now()
            current_date_str = now.date().isoformat()
            current_time_str = now.strftime("%H:%M")
            
            # 检查所有事件
            for date_str, events in self.events.items():
                for event in events:
                    if event.get('reminder', False):
                        event_time_str = event.get('time', '')
                        reminder_minutes = event.get('reminder_time', 0)
                        
                        if event_time_str and event_time_str != "全天":
                            try:
                                event_time = datetime.strptime(event_time_str, "%H:%M").time()
                                event_datetime = datetime.combine(datetime.strptime(date_str, "%Y-%m-%d").date(), event_time)
                                
                                # 计算提醒时间
                                reminder_datetime = event_datetime - timedelta(minutes=reminder_minutes)
                                
                                # 如果当前时间接近提醒时间 (在1分钟误差内)
                                if abs((now - reminder_datetime).total_seconds()) < 60:
                                    # 在主线程中显示提醒
                                    self.root.after(0, lambda e=event, d=date_str: self.show_reminder(e, d))
                                    
                                    # 标记为已提醒 (避免重复提醒)
                                    event['reminded'] = True
                            except:
                                pass
            
            # 每分钟检查一次
            time.sleep(60)
    
    def show_reminder(self, event, date_str):
        """显示提醒"""
        title = event.get('title', '无标题')
        time_str = event.get('time', '全天')
        description = event.get('description', '无描述')
        
        message = f"提醒: {title}\n"
        message += f"时间: {date_str} {time_str}\n"
        message += f"描述: {description[:100]}..." if len(description) > 100 else f"描述: {description}"
        
        messagebox.showwarning("事件提醒", message)
    
    def load_events(self):
        """从文件加载事件"""
        if os.path.exists(self.data_file):
            try:
                with open(self.data_file, 'r', encoding='utf-8') as f:
                    return json.load(f)
            except:
                return {}
        
        # 如果没有找到文件，返回示例数据
        return self.create_sample_data()
    
    def create_sample_data(self):
        """创建示例数据"""
        today = date.today()
        sample_data = {
            today.isoformat(): [
                {
                    'title': '团队会议',
                    'time': '10:00',
                    'description': '每周团队例会，讨论项目进展',
                    'reminder': True,
                    'reminder_time': 10,
                    'created': datetime.now().isoformat()
                },
                {
                    'title': '医生预约',
                    'time': '14:30',
                    'description': '年度体检',
                    'reminder': True,
                    'reminder_time': 30,
                    'created': datetime.now().isoformat()
                }
            ],
            (today + timedelta(days=1)).isoformat(): [
                {
                    'title': '项目截止',
                    'time': '全天',
                    'description': '提交项目最终报告',
                    'reminder': True,
                    'reminder_time': 60,
                    'created': datetime.now().isoformat()
                }
            ],
            (today + timedelta(days=3)).isoformat(): [
                {
                    'title': '朋友生日',
                    'time': '19:00',
                    'description': '生日聚会，记得带礼物',
                    'reminder': True,
                    'reminder_time': 120,
                    'created': datetime.now().isoformat()
                }
            ]
        }
        
        # 保存示例数据
        self.save_events_to_file(sample_data)
        return sample_data
    
    def save_events_to_file(self, events):
        """保存事件到文件"""
        try:
            with open(self.data_file, 'w', encoding='utf-8') as f:
                json.dump(events, f, ensure_ascii=False, indent=2)
        except Exception as e:
            print(f"保存事件时出错: {e}")
    
    def save_events(self):
        """保存当前事件"""
        self.save_events_to_file(self.events)
    
    def on_closing(self):
        """处理窗口关闭"""
        self.reminder_active = False
        self.save_events()
        self.root.destroy()

def main():
    """主函数"""
    root = tk.Tk()
    app = CalendarReminderApp(root)
    root.mainloop()

if __name__ == "__main__":
    main()
