import sys
import socket
import threading
from PyQt6.QtWidgets import (
    QApplication, QWidget, QVBoxLayout, QTextEdit,
    QLineEdit, QPushButton, QLabel, QHBoxLayout
)
from PyQt6.QtCore import pyqtSignal, QObject


class ReceiverThread(threading.Thread):
    def __init__(self, sock, signal_handler):
        super().__init__(daemon=True)
        self.sock = sock
        self.signal_handler = signal_handler
        self.running = True

    def run(self):
        while self.running:
            try:
                data = self.sock.recv(1024)
                if not data:
                    self.signal_handler("Disconnected from server.")
                    break
                self.signal_handler(data.decode())
            except:
                self.signal_handler("Connection lost.")
                break

    def stop(self):
        self.running = False


class Communicator(QObject):
    data_received = pyqtSignal(str)


class SocketClientGUI(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Socket Client")
        self.sock = None
        self.receiver_thread = None
        self.comm = Communicator()
        self.comm.data_received.connect(self.display_message)

        self.init_ui()

    def init_ui(self):
        layout = QVBoxLayout()

        self.output = QTextEdit()
        self.output.setReadOnly(True)

        self.input_line = QLineEdit()
        self.input_line.returnPressed.connect(self.send_message)

        self.send_button = QPushButton("Send")
        self.send_button.clicked.connect(self.send_message)

        self.connect_button = QPushButton("Connect")
        self.connect_button.clicked.connect(self.connect_to_server)

        self.disconnect_button = QPushButton("Disconnect")
        self.disconnect_button.clicked.connect(self.disconnect_from_server)

        self.ip_input = QLineEdit("127.0.0.1")
        self.port_input = QLineEdit("12345")

        ip_layout = QHBoxLayout()
        ip_layout.addWidget(QLabel("IP:"))
        ip_layout.addWidget(self.ip_input)
        ip_layout.addWidget(QLabel("Port:"))
        ip_layout.addWidget(self.port_input)
        ip_layout.addWidget(self.connect_button)
        ip_layout.addWidget(self.disconnect_button)

        layout.addLayout(ip_layout)
        layout.addWidget(self.output)
        layout.addWidget(self.input_line)
        layout.addWidget(self.send_button)

        self.setLayout(layout)

    def connect_to_server(self):
        ip = self.ip_input.text()
        port = int(self.port_input.text())
        try:
            self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.sock.connect((ip, port))
            self.comm.data_received.emit(f"Connected to {ip}:{port}")
            self.receiver_thread = ReceiverThread(self.sock, self.comm.data_received.emit)
            self.receiver_thread.start()
        except Exception as e:
            self.comm.data_received.emit(f"Connection failed: {e}")

    def disconnect_from_server(self):
        if self.sock:
            if self.receiver_thread:
                self.receiver_thread.stop()
            self.sock.close()
            self.sock = None
            self.comm.data_received.emit("Disconnected from server.")

    def send_message(self):
        msg = self.input_line.text()
        if self.sock and msg:
            try:
                self.sock.sendall(msg.encode())
                self.input_line.clear()
            except Exception as e:
                self.comm.data_received.emit(f"Send failed: {e}")
        else:
            self.comm.data_received.emit("Not connected or empty input.")

    def display_message(self, msg):
        self.output.append(msg)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    client = SocketClientGUI()
    client.resize(600, 400)
    client.show()
    sys.exit(app.exec())
