import socket
import threading
import json
import tkinter as tk
from tkinter import messagebox, ttk
import sys

class PongClient:
    def __init__(self, host='127.0.0.1', port=12345):
        self.host = host
        self.port = port
        self.socket = None
        self.player_id = None
        self.username = None
        self.game_state = None
        self.running = False
        self.key_pressed = False
        self.move_direction = 0
        
        # GUI
        self.root = tk.Tk()
        self.root.title("🏓 乒乓球对战")
        self.root.geometry("900x650")
        self.root.protocol("WM_DELETE_WINDOW", self.on_closing)
        self.root.configure(bg='#2c3e50')
        
        self.setup_gui()
        self.show_connect_dialog()
    
    def setup_gui(self):
        """设置GUI"""
        # 标题
        title_frame = tk.Frame(self.root, bg='#2c3e50')
        title_frame.pack(fill=tk.X, pady=5)
        
        title_label = tk.Label(
            title_frame,
            text="🏓 乒乓球对战",
            font=('Arial', 20, 'bold'),
            bg='#2c3e50',
            fg='white'
        )
        title_label.pack()
        
        # 主框架
        self.main_frame = tk.Frame(self.root, bg='#2c3e50')
        self.main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=5)
        
        # 游戏画布
        self.canvas = tk.Canvas(
            self.main_frame,
            width=800,
            height=600,
            bg='black',
            highlightthickness=3,
            highlightbackground='#3498db'
        )
        self.canvas.pack(side=tk.LEFT, padx=(0, 10))
        
        # 右侧信息面板
        self.info_frame = tk.Frame(
            self.main_frame,
            width=200,
            bg='#34495e',
            relief=tk.RAISED,
            bd=2
        )
        self.info_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True)
        self.info_frame.pack_propagate(False)
        
        # 玩家信息
        tk.Label(
            self.info_frame,
            text="📊 游戏信息",
            font=('Arial', 14, 'bold'),
            bg='#34495e',
            fg='white'
        ).pack(pady=10)
        
        # 分数显示
        self.score_frame = tk.Frame(self.info_frame, bg='#34495e')
        self.score_frame.pack(fill=tk.X, pady=10)
        
        self.score_labels = {}
        
        # 状态信息
        self.status_label = tk.Label(
            self.info_frame,
            text="⏳ 等待对手...",
            font=('Arial', 12),
            bg='#34495e',
            fg='#f1c40f'
        )
        self.status_label.pack(pady=10)
        
        # 准备按钮
        self.ready_button = tk.Button(
            self.info_frame,
            text="✅ 准备开始",
            command=self.toggle_ready,
            bg='#27ae60',
            fg='white',
            font=('Arial', 12),
            height=2,
            state=tk.DISABLED
        )
        self.ready_button.pack(pady=10, padx=20, fill=tk.X)
        
        # 游戏说明
        help_text = """
        🎮 操作说明:
        
        玩家1: 使用 W/S 键
        玩家2: 使用 ↑/↓ 键
        
        📌 先按"准备开始"
        等待双方都准备
        """
        
        tk.Label(
            self.info_frame,
            text=help_text,
            font=('Arial', 10),
            bg='#34495e',
            fg='white',
            justify=tk.LEFT
        ).pack(pady=20, padx=10)
        
        # 绑定键盘事件
        self.root.bind('<KeyPress>', self.on_key_press)
        self.root.bind('<KeyRelease>', self.on_key_release)
        self.root.focus_set()
    
    def show_connect_dialog(self):
        """连接对话框"""
        dialog = tk.Toplevel(self.root)
        dialog.title("连接服务器")
        dialog.geometry("450x300")
        dialog.transient(self.root)
        dialog.grab_set()
        dialog.configure(bg='#2c3e50')
        
        tk.Label(
            dialog,
            text="🏓 乒乓球对战 - 连接",
            font=('Arial', 18, 'bold'),
            bg='#2c3e50',
            fg='white'
        ).pack(pady=15)
        
        form = tk.Frame(dialog, bg='#2c3e50')
        form.pack(pady=10)
        
        # 服务器地址
        tk.Label(
            form,
            text="服务器地址:",
            font=('Arial', 11),
            bg='#2c3e50',
            fg='white'
        ).grid(row=0, column=0, sticky=tk.W, pady=5, padx=5)
        
        host_entry = tk.Entry(form, width=25, font=('Arial', 11))
        host_entry.insert(0, '127.0.0.1')
        host_entry.grid(row=0, column=1, pady=5, padx=5)
        
        # 端口
        tk.Label(
            form,
            text="端口:",
            font=('Arial', 11),
            bg='#2c3e50',
            fg='white'
        ).grid(row=1, column=0, sticky=tk.W, pady=5, padx=5)
        
        port_entry = tk.Entry(form, width=25, font=('Arial', 11))
        port_entry.insert(0, '12345')
        port_entry.grid(row=1, column=1, pady=5, padx=5)
        
        # 用户名
        tk.Label(
            form,
            text="用户名:",
            font=('Arial', 11),
            bg='#2c3e50',
            fg='white'
        ).grid(row=2, column=0, sticky=tk.W, pady=5, padx=5)
        
        username_entry = tk.Entry(form, width=25, font=('Arial', 11))
        username_entry.grid(row=2, column=1, pady=5, padx=5)
        username_entry.focus()
        
        def connect():
            host = host_entry.get().strip()
            try:
                port = int(port_entry.get().strip())
            except:
                messagebox.showerror("错误", "端口必须是数字")
                return
            
            username = username_entry.get().strip()
            
            if not username:
                messagebox.showerror("错误", "请输入用户名")
                return
            
            self.host = host
            self.port = port
            self.username = username
            
            if self.connect_to_server():
                dialog.destroy()
                self.root.title(f"🏓 乒乓球对战 - {self.username}")
        
        connect_btn = tk.Button(
            dialog,
            text="🚀 连接",
            command=connect,
            bg='#3498db',
            fg='white',
            font=('Arial', 12),
            width=20,
            height=1
        )
        connect_btn.pack(pady=20)
        
        dialog.bind('<Return>', lambda e: connect())
    
    def connect_to_server(self):
        """连接服务器"""
        try:
            self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.socket.connect((self.host, self.port))
            
            # 发送用户名
            self.socket.send(json.dumps({
                'username': self.username
            }).encode('utf-8'))
            
            # 启动接收线程
            self.running = True
            receive_thread = threading.Thread(target=self.receive_messages, daemon=True)
            receive_thread.start()
            
            # 启动发送线程
            send_thread = threading.Thread(target=self.send_loop, daemon=True)
            send_thread.start()
            
            return True
            
        except Exception as e:
            messagebox.showerror("❌ 连接失败", f"{e}")
            return False
    
    def receive_messages(self):
        """接收消息"""
        while self.running:
            try:
                data = self.socket.recv(4096).decode('utf-8')
                if not data:
                    break
                
                message = json.loads(data)
                self.handle_message(message)
                
            except json.JSONDecodeError:
                continue
            except:
                break
        
        if self.running:
            self.root.after(0, self.handle_disconnect)
    
    def handle_message(self, message):
        """处理消息"""
        msg_type = message.get('type')
        
        if msg_type == 'init':
            # 初始化
            self.player_id = message['player_id']
            self.root.after(0, lambda: self.status_label.config(
                text=f"🎮 你是 {'左方' if self.player_id == 1 else '右方'} 玩家"
            ))
            self.root.after(0, lambda: self.ready_button.config(state=tk.NORMAL))
            
            # 更新标题
            self.root.title(f"🏓 乒乓球对战 - {self.username} (玩家{self.player_id})")
            
        elif msg_type == 'game_state':
            # 更新游戏状态
            self.game_state = message['state']
            self.root.after(0, self.update_display)
            
        elif msg_type == 'system':
            # 系统消息
            self.root.after(0, lambda: self.status_label.config(
                text=message['message']
            ))
            
        elif msg_type == 'score':
            scorer = message['scorer']
            self.root.after(0, lambda: self.status_label.config(
                text=f"🎯 玩家{scorer} 得分！"
            ))
            
        elif msg_type == 'error':
            messagebox.showerror("错误", message['message'])
    
    def send_loop(self):
        """发送循环"""
        while self.running:
            try:
                if self.player_id and self.game_state:
                    # 发送移动
                    if self.key_pressed:
                        self.send_json({
                            'type': 'move',
                            'direction': self.move_direction
                        })
                
                time.sleep(1/30)  # 30 FPS
                
            except:
                break
    
    def send_json(self, data):
        """发送JSON数据"""
        try:
            self.socket.send(json.dumps(data).encode('utf-8'))
        except:
            pass
    
    def toggle_ready(self):
        """切换准备状态"""
        self.send_json({
            'type': 'ready'
        })
        self.ready_button.config(text="⏳ 已准备，等待对手...", state=tk.DISABLED)
    
    def update_display(self):
        """更新游戏显示"""
        self.canvas.delete('all')
        
        if not self.game_state:
            return
        
        # 绘制中场线
        for i in range(0, 600, 30):
            self.canvas.create_line(400, i, 400, i+15, fill='white', width=2)
        
        # 绘制中心圆
        self.canvas.create_oval(375, 275, 425, 325, outline='white', width=2)
        
        # 绘制球
        ball = self.game_state['ball']
        self.canvas.create_oval(
            ball['x']-8, ball['y']-8,
            ball['x']+8, ball['y']+8,
            fill='white'
        )
        
        # 绘制球拍
        paddles = self.game_state['paddles']
        
        # 玩家1球拍（左侧）
        if 1 in paddles:
            y1 = paddles[1]['y']
            color = '#3498db' if self.player_id == 1 else '#e74c3c'
            self.canvas.create_rectangle(
                10, y1-40, 25, y1+40,
                fill=color,
                outline='white',
                width=2
            )
            # 标记玩家名
            self.canvas.create_text(
                17, y1+50,
                text="P1",
                fill='white',
                font=('Arial', 10, 'bold')
            )
        
        # 玩家2球拍（右侧）
        if 2 in paddles:
            y2 = paddles[2]['y']
            color = '#e74c3c' if self.player_id == 2 else '#3498db'
            self.canvas.create_rectangle(
                775, y2-40, 790, y2+40,
                fill=color,
                outline='white',
                width=2
            )
            self.canvas.create_text(
                783, y2+50,
                text="P2",
                fill='white',
                font=('Arial', 10, 'bold')
            )
        
        # 更新分数
        self.update_scores()
    
    def update_scores(self):
        """更新分数显示"""
        # 清除旧分数
        for widget in self.score_frame.winfo_children():
            widget.destroy()
        
        if self.game_state and 'scores' in self.game_state:
            for username, score in self.game_state['scores'].items():
                # 判断是哪个玩家
                player_num = "P1" if list(self.game_state['scores'].keys()).index(username) == 0 else "P2"
                color = '#3498db' if player_num == "P1" else '#e74c3c'
                
                label = tk.Label(
                    self.score_frame,
                    text=f"{player_num}: {username} - {score}分",
                    font=('Arial', 12, 'bold'),
                    bg='#34495e',
                    fg=color
                )
                label.pack(pady=2)
    
    def on_key_press(self, event):
        """键盘按下"""
        if not self.game_state:
            return
            
        key = event.keysym.lower()
        
        # 玩家1使用 W/S
        if self.player_id == 1:
            if key == 'w':
                self.key_pressed = True
                self.move_direction = -1
            elif key == 's':
                self.key_pressed = True
                self.move_direction = 1
        
        # 玩家2使用 ↑/↓
        elif self.player_id == 2:
            if key == 'up':
                self.key_pressed = True
                self.move_direction = -1
            elif key == 'down':
                self.key_pressed = True
                self.move_direction = 1
    
    def on_key_release(self, event):
        """键盘释放"""
        key = event.keysym.lower()
        
        if self.player_id == 1:
            if key in ['w', 's']:
                self.key_pressed = False
                self.move_direction = 0
        elif self.player_id == 2:
            if key in ['up', 'down']:
                self.key_pressed = False
                self.move_direction = 0
    
    def handle_disconnect(self):
        """处理断开连接"""
        self.running = False
        messagebox.showwarning("⚠️ 断开连接", "与服务器断开连接")
        self.root.destroy()
    
    def on_closing(self):
        """关闭窗口"""
        self.running = False
        if self.socket:
            try:
                self.socket.close()
            except:
                pass
        self.root.destroy()
        sys.exit(0)
    
    def run(self):
        """运行客户端"""
        self.root.mainloop()

if __name__ == "__main__":
    client = PongClient()
    client.run()