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

class ChatClient:
    def __init__(self, host='127.0.0.1', port=12345):
        self.host = host
        self.port = port
        self.socket = None
        self.username = None
        self.running = False
        self.current_room = 'general'
        self.typing = False
        
        # GUI组件
        self.root = tk.Tk()
        self.root.title("局域网聊天室")
        self.root.geometry("800x600")
        self.root.protocol("WM_DELETE_WINDOW", self.on_closing)
        
        self.setup_gui()
        
        # 连接对话框
        self.show_connect_dialog()
    
    def setup_gui(self):
        """设置GUI界面"""
        # 顶部框架 - 显示当前房间和在线用户
        self.top_frame = tk.Frame(self.root, bg='#2c3e50', height=40)
        self.top_frame.pack(fill=tk.X)
        
        self.room_label = tk.Label(
            self.top_frame,
            text="房间: general",
            bg='#2c3e50',
            fg='white',
            font=('Arial', 12)
        )
        self.room_label.pack(side=tk.LEFT, padx=10, pady=5)
        
        self.user_count_label = tk.Label(
            self.top_frame,
            text="在线: 0",
            bg='#2c3e50',
            fg='white',
            font=('Arial', 12)
        )
        self.user_count_label.pack(side=tk.RIGHT, padx=10, pady=5)
        
        # 主框架
        self.main_frame = tk.Frame(self.root)
        self.main_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        # 左侧 - 在线用户列表
        self.left_frame = tk.Frame(self.main_frame, width=150)
        self.left_frame.pack(side=tk.LEFT, fill=tk.Y, padx=(0, 5))
        
        self.user_label = tk.Label(self.left_frame, text="在线用户", font=('Arial', 10, 'bold'))
        self.user_label.pack(pady=5)
        
        self.user_listbox = tk.Listbox(
            self.left_frame,
            height=20,
            selectmode=tk.SINGLE,
            bg='#ecf0f1'
        )
        self.user_listbox.pack(fill=tk.BOTH, expand=True)
        self.user_listbox.bind('<Double-Button-1>', self.on_user_double_click)
        
        # 右侧 - 聊天区域
        self.right_frame = tk.Frame(self.main_frame)
        self.right_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True)
        
        # 聊天显示区域
        self.chat_display = scrolledtext.ScrolledText(
            self.right_frame,
            wrap=tk.WORD,
            font=('Arial', 10),
            height=20,
            bg='white'
        )
        self.chat_display.pack(fill=tk.BOTH, expand=True)
        self.chat_display.config(state=tk.DISABLED)
        
        # 输入区域
        self.input_frame = tk.Frame(self.right_frame)
        self.input_frame.pack(fill=tk.X, pady=(5, 0))
        
        self.input_entry = tk.Entry(self.input_frame, font=('Arial', 10))
        self.input_entry.pack(side=tk.LEFT, fill=tk.X, expand=True)
        self.input_entry.bind('<Return>', self.send_message)
        self.input_entry.bind('<KeyPress>', self.on_typing)
        
        self.send_button = tk.Button(
            self.input_frame,
            text="发送",
            command=self.send_message,
            bg='#3498db',
            fg='white'
        )
        self.send_button.pack(side=tk.RIGHT, padx=(5, 0))
        
        # 底部状态栏
        self.status_bar = tk.Label(
            self.root,
            text="就绪",
            bd=1,
            relief=tk.SUNKEN,
            anchor=tk.W
        )
        self.status_bar.pack(side=tk.BOTTOM, fill=tk.X)
    
    def show_connect_dialog(self):
        """显示连接对话框"""
        dialog = tk.Toplevel(self.root)
        dialog.title("连接到聊天室")
        dialog.geometry("400x250")
        dialog.transient(self.root)
        dialog.grab_set()
        
        # 标题
        tk.Label(dialog, text="局域网聊天室", font=('Arial', 16, 'bold')).pack(pady=10)
        
        # 表单框架
        form = tk.Frame(dialog)
        form.pack(pady=10)
        
        # 服务器地址
        tk.Label(form, text="服务器地址:", font=('Arial', 10)).grid(row=0, column=0, sticky=tk.W, pady=5)
        host_entry = tk.Entry(form, width=30)
        host_entry.insert(0, '127.0.0.1')
        host_entry.grid(row=0, column=1, pady=5)
        
        # 端口
        tk.Label(form, text="端口:", font=('Arial', 10)).grid(row=1, column=0, sticky=tk.W, pady=5)
        port_entry = tk.Entry(form, width=30)
        port_entry.insert(0, '12345')
        port_entry.grid(row=1, column=1, pady=5)
        
        # 用户名
        tk.Label(form, text="用户名:", font=('Arial', 10)).grid(row=2, column=0, sticky=tk.W, pady=5)
        username_entry = tk.Entry(form, width=30)
        username_entry.grid(row=2, column=1, pady=5)
        username_entry.focus()
        
        # 连接按钮
        def connect():
            host = host_entry.get().strip()
            port = int(port_entry.get().strip())
            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}")
                self.status_bar.config(text=f"已连接到 {self.host}:{self.port}")
            else:
                messagebox.showerror("错误", "无法连接到服务器")
        
        tk.Button(
            dialog,
            text="连接",
            command=connect,
            bg='#3498db',
            fg='white',
            font=('Arial', 10),
            width=20
        ).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(self.username.encode('utf-8'))
            
            # 启动接收线程
            self.running = True
            receive_thread = threading.Thread(target=self.receive_messages, daemon=True)
            receive_thread.start()
            
            return True
            
        except Exception as e:
            print(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_data = json.loads(data)
                self.handle_message(message_data)
                
            except json.JSONDecodeError:
                continue
            except ConnectionError:
                break
            except Exception as e:
                print(f"接收消息错误: {e}")
                break
        
        if self.running:
            self.root.after(0, self.handle_disconnect)
    
    def handle_message(self, message_data):
        """处理接收到的消息"""
        msg_type = message_data.get('type')
        
        if msg_type == 'message':
            # 普通消息
            username = message_data.get('username', '匿名')
            content = message_data.get('content', '')
            time = message_data.get('time', '')
            
            self.display_message(f'[{time}] {username}: {content}')
            
        elif msg_type == 'system':
            # 系统消息
            message = message_data.get('message', '')
            time = message_data.get('time', '')
            self.display_message(f'[{time}] [系统] {message}', 'system')
            
        elif msg_type == 'private':
            # 私聊消息
            from_user = message_data.get('from', '')
            content = message_data.get('content', '')
            time = message_data.get('time', '')
            self.display_message(f'[{time}] [私聊] {from_user} → 你: {content}', 'private')
            
        elif msg_type == 'user_list':
            # 用户列表更新
            users = message_data.get('users', [])
            self.update_user_list(users)
            
        elif msg_type == 'typing':
            # 正在输入状态
            username = message_data.get('username', '')
            is_typing = message_data.get('is_typing', False)
            if is_typing:
                self.status_bar.config(text=f'{username} 正在输入...')
            else:
                self.status_bar.config(text='就绪')
    
    def display_message(self, message, msg_type='normal'):
        """显示消息"""
        def _display():
            self.chat_display.config(state=tk.NORMAL)
            
            # 设置消息颜色
            if msg_type == 'system':
                self.chat_display.insert(tk.END, message + '\n', 'system')
                self.chat_display.tag_config('system', foreground='#e67e22')
            elif msg_type == 'private':
                self.chat_display.insert(tk.END, message + '\n', 'private')
                self.chat_display.tag_config('private', foreground='#8e44ad')
            else:
                self.chat_display.insert(tk.END, message + '\n')
            
            self.chat_display.see(tk.END)
            self.chat_display.config(state=tk.DISABLED)
        
        self.root.after(0, _display)
    
    def send_message(self, event=None):
        """发送消息"""
        content = self.input_entry.get().strip()
        if not content:
            return
        
        # 处理命令
        if content.startswith('/'):
            self.send_command(content)
        else:
            # 发送普通消息
            message = {
                'type': 'message',
                'content': content,
                'room': self.current_room
            }
            self.send_json(message)
            
            # 显示自己的消息
            self.display_message(f'[{datetime.datetime.now().strftime("%H:%M:%S")}] 你: {content}')
        
        self.input_entry.delete(0, tk.END)
        self.typing = False
    
    def send_command(self, command):
        """发送命令"""
        self.send_json({
            'type': 'command',
            'command': command
        })
    
    def send_json(self, data):
        """发送JSON数据"""
        try:
            self.socket.send(json.dumps(data).encode('utf-8'))
        except Exception as e:
            print(f"发送消息错误: {e}")
            self.handle_disconnect()
    
    def on_typing(self, event):
        """处理输入状态"""
        if not self.typing:
            self.typing = True
            self.send_json({
                'type': 'typing',
                'is_typing': True
            })
    
    def on_user_double_click(self, event):
        """双击用户列表触发私聊"""
        selection = self.user_listbox.curselection()
        if selection:
            username = self.user_listbox.get(selection[0])
            if username != self.username:
                self.input_entry.insert(0, f'/private {username} ')
                self.input_entry.focus()
    
    def update_user_list(self, users):
        """更新用户列表"""
        def _update():
            self.user_listbox.delete(0, tk.END)
            for user in users:
                if user != self.username:
                    self.user_listbox.insert(tk.END, user)
            self.user_count_label.config(text=f"在线: {len(users)}")
        
        self.root.after(0, _update)
    
    def handle_disconnect(self):
        """处理断开连接"""
        self.running = False
        messagebox.showwarning("断开连接", "与服务器断开连接")
        self.root.destroy()
    
    def on_closing(self):
        """关闭窗口"""
        if self.socket:
            try:
                self.send_json({
                    'type': 'system',
                    'message': '/quit'
                })
                self.socket.close()
            except:
                pass
        
        self.running = False
        self.root.destroy()
        sys.exit(0)
    
    def run(self):
        """运行客户端"""
        self.root.mainloop()

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