import sys
import random
import socket
import threading
import time
from PyQt5.QtWidgets import (
    QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
    QPushButton, QLabel, QFrame, QSizePolicy, QStyle
)
from PyQt5.QtCore import Qt, QTimer, QSize, QPointF, pyqtSignal, QThread
from PyQt5.QtGui import QPainter, QColor, QPen, QLinearGradient, QBrush, QIcon, QFont

# --- Phone Client Thread ---
class PhoneClient(QThread):
    data_received = pyqtSignal(bytes, int)  # Include client_id
    state_changed = pyqtSignal(str, str, int)  # Include client_id

    def __init__(self, host, port, client_id):
        super().__init__()
        self.host = host
        self.port = port
        self.client_id = client_id
        self.sock = None
        self.running = True

    def run(self):
        try:
            self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
            self.sock.settimeout(15)
            self.sock.connect((self.host, self.port))
            print(f"Client {self.client_id} connected to {self.host}:{self.port}")
            while self.running:
                try:
                    data = self.sock.recv(1024)
                    if not data:
                        print(f"Client {self.client_id} disconnected")
                        self.state_changed.emit("CALL_END", "", self.client_id)
                        break
                    decoded_data = data.decode('utf-8', errors='ignore').strip()
                    print(f"Client {self.client_id} received raw: {decoded_data}")
                    if decoded_data in ["RINGING", "CALL_END", "CALL_DROPPED", "IN_CALL"]:
                        self.state_changed.emit(decoded_data, "", self.client_id)
                    else:
                        self.data_received.emit(data, self.client_id)
                        print(f"Client {self.client_id} received audio: {decoded_data}")
                except socket.timeout:
                    print(f"Client {self.client_id} timed out waiting for data")
                    continue
                except Exception as e:
                    print(f"Client {self.client_id} error: {e}")
                    self.state_changed.emit("CALL_END", "", self.client_id)
                    break
        except Exception as e:
            print(f"Client {self.client_id} connection failed: {e}")
        finally:
            if self.sock:
                self.sock.close()

    def send(self, message):
        if self.sock and self.running:
            try:
                self.sock.send(message.encode())
                print(f"Client {self.client_id} sent: {message}")
            except Exception as e:
                print(f"Client {self.client_id} send error: {e}")

    def stop(self):
        self.running = False
        if self.sock:
            self.sock.close()

# --- Custom Waveform Widget ---
class WaveformWidget(QWidget):
    def __init__(self, parent=None, dynamic=False):
        super().__init__(parent)
        self.dynamic = dynamic
        self.setMinimumSize(200, 80)
        self.setMaximumHeight(100)
        self.waveform_data = [random.randint(10, 90) for _ in range(50)]
        if self.dynamic:
            self.timer = QTimer(self)
            self.timer.timeout.connect(self.update_waveform)
            self.timer.start(100)

    def update_waveform(self):
        self.waveform_data = self.waveform_data[1:] + [random.randint(10, 90)]
        self.update()

    def set_data(self, data):
        amplitude = sum(byte for byte in data) % 90 + 10
        self.waveform_data = self.waveform_data[1:] + [amplitude]
        self.update()

    def paintEvent(self, event):
        painter = QPainter(self)
        painter.setRenderHint(QPainter.Antialiasing)
        painter.fillRect(self.rect(), QColor("#2D2D2D"))
        gradient = QLinearGradient(0, 0, 0, self.height())
        gradient.setColorAt(0.0, QColor("#0078D4"))
        gradient.setColorAt(1.0, QColor("#50E6A4"))
        pen = QPen(QBrush(gradient), 2)
        painter.setPen(pen)
        bar_width = self.width() / len(self.waveform_data)
        max_h = self.height() - 10
        for i, val in enumerate(self.waveform_data):
            bar_height = (val / 100.0) * max_h
            x = i * bar_width
            y = (self.height() - bar_height) / 2
            painter.drawLine(QPointF(x + bar_width / 2, y), QPointF(x + bar_width / 2, y + bar_height))

    def resizeEvent(self, event):
        super().resizeEvent(event)
        self.update()

# --- Phone State ---
class PhoneState:
    IDLE = 0
    CALLING = 1
    IN_CALL = 2
    RINGING = 3

class PhoneUI(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Enhanced Dual Phone Interface")
        self.setGeometry(100, 100, 900, 750)
        self.setStyleSheet("""
            QMainWindow { background-color: #333333; }
            QLabel { color: #E0E0E0; font-size: 14px; }
            QPushButton {
                background-color: #0078D4; color: white; border: none;
                padding: 10px 15px; border-radius: 5px; font-size: 14px;
                min-height: 30px;
            }
            QPushButton:hover { background-color: #005A9E; }
            QPushButton:pressed { background-color: #003C6B; }
            QPushButton#settingsButton { background-color: #555555; }
            QPushButton#settingsButton:hover { background-color: #777777; }
            QFrame#phoneDisplay {
                background-color: #1E1E1E; border: 2px solid #0078D4;
                border-radius: 10px;
            }
            QLabel#phoneTitleLabel {
                font-size: 18px; font-weight: bold; padding-bottom: 5px;
                color: #FFFFFF;
            }
            QLabel#mainTitleLabel {
                font-size: 24px; font-weight: bold; color: #00A2E8;
                padding: 15px;
            }
            QWidget#phoneWidget {
                border: 1px solid #4A4A4A; border-radius: 8px;
                padding: 10px; background-color: #3A3A3A;
            }
        """)

        # Phone states
        self.phone1_state = PhoneState.IDLE
        self.phone2_state = PhoneState.IDLE

        # Phone clients
        self.phone1_client = PhoneClient("localhost", 12345, 0)
        self.phone2_client = PhoneClient("localhost", 12345, 1)
        self.phone1_client.data_received.connect(lambda data, cid: self.update_waveform(cid, data))
        self.phone2_client.data_received.connect(lambda data, cid: self.update_waveform(cid, data))
        self.phone1_client.state_changed.connect(lambda state, num, cid: self.set_phone_state(cid, self.map_state(state), num))
        self.phone2_client.state_changed.connect(lambda state, num, cid: self.set_phone_state(cid, self.map_state(state), num))
        self.phone1_client.start()
        self.phone2_client.start()

        # Main widget and layout
        main_widget = QWidget()
        self.setCentralWidget(main_widget)
        main_layout = QVBoxLayout()
        main_layout.setSpacing(20)
        main_layout.setContentsMargins(20, 20, 20, 20)
        main_layout.setAlignment(Qt.AlignCenter)
        main_widget.setLayout(main_layout)

        # App Title
        app_title_label = QLabel("Dual Phone Control Panel")
        app_title_label.setObjectName("mainTitleLabel")
        app_title_label.setAlignment(Qt.AlignCenter)
        main_layout.addWidget(app_title_label)

        # Phone displays layout
        phone_controls_layout = QHBoxLayout()
        phone_controls_layout.setSpacing(50)
        phone_controls_layout.setAlignment(Qt.AlignCenter)
        main_layout.addLayout(phone_controls_layout)

        # Phone 1
        phone1_widget_container, self.phone1_display, self.phone1_button, self.phone1_waveform = self._create_phone_ui("Phone 1", self.phone1_action)
        phone_controls_layout.addWidget(phone1_widget_container)

        # Phone 2
        phone2_widget_container, self.phone2_display, self.phone2_button, self.phone2_waveform = self._create_phone_ui("Phone 2", self.phone2_action)
        phone_controls_layout.addWidget(phone2_widget_container)

        # Spacer
        main_layout.addStretch(1)

        # Settings Button
        self.settings_button = QPushButton("Settings")
        self.settings_button.setObjectName("settingsButton")
        self.settings_button.setFixedWidth(180)
        self.settings_button.setIcon(self.style().standardIcon(QStyle.SP_FileDialogDetailedView))
        self.settings_button.setIconSize(QSize(20, 20))
        self.settings_button.clicked.connect(self.settings_action)
        settings_layout = QHBoxLayout()
        settings_layout.addStretch()
        settings_layout.addWidget(self.settings_button)
        settings_layout.addStretch()
        main_layout.addLayout(settings_layout)

        # Initialize button states
        self._update_phone_button_ui(self.phone1_button, self.phone1_state)
        self._update_phone_button_ui(self.phone2_button, self.phone2_state)

    def _create_phone_ui(self, title, action_slot):
        phone_container_widget = QWidget()
        phone_container_widget.setObjectName("phoneWidget")
        phone_layout = QVBoxLayout()
        phone_layout.setAlignment(Qt.AlignCenter)
        phone_layout.setSpacing(15)
        phone_container_widget.setLayout(phone_layout)

        phone_title_label = QLabel(title)
        phone_title_label.setObjectName("phoneTitleLabel")
        phone_title_label.setAlignment(Qt.AlignCenter)
        phone_layout.addWidget(phone_title_label)

        phone_display_frame = QFrame()
        phone_display_frame.setObjectName("phoneDisplay")
        phone_display_frame.setFixedSize(250, 350)
        phone_display_frame.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)

        display_content_layout = QVBoxLayout(phone_display_frame)
        display_content_layout.setAlignment(Qt.AlignCenter)
        phone_status_label = QLabel("Idle")
        phone_status_label.setAlignment(Qt.AlignCenter)
        phone_status_label.setFont(QFont("Arial", 16))
        display_content_layout.addWidget(phone_status_label)
        phone_layout.addWidget(phone_display_frame, alignment=Qt.AlignCenter)

        phone_button = QPushButton()
        phone_button.setFixedWidth(120)
        phone_button.setIconSize(QSize(20, 20))
        phone_button.clicked.connect(action_slot)
        phone_layout.addWidget(phone_button, alignment=Qt.AlignCenter)

        waveform_label = QLabel(f"{title} Audio")
        waveform_label.setAlignment(Qt.AlignCenter)
        waveform_label.setStyleSheet("font-size: 14px; color: #E0E0E0;")
        phone_layout.addWidget(waveform_label)
        waveform_widget = WaveformWidget(dynamic=False)
        phone_layout.addWidget(waveform_widget, alignment=Qt.AlignCenter)

        phone_display_frame.setProperty("statusLabel", phone_status_label)
        return phone_container_widget, phone_display_frame, phone_button, waveform_widget

    def _update_phone_button_ui(self, button, state, phone_number=""):
        parent_widget = button.parentWidget()
        if parent_widget:
            frame = parent_widget.findChild(QFrame, "phoneDisplay")
            if frame:
                status_label = frame.property("statusLabel")
                if status_label:
                    if state == PhoneState.IDLE:
                        button.setText("Call")
                        button.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))
                        status_label.setText("Idle")
                        button.setStyleSheet("background-color: #0078D4;")
                    elif state == PhoneState.CALLING:
                        button.setText("Cancel")
                        button.setIcon(self.style().standardIcon(QStyle.SP_MediaStop))
                        status_label.setText(f"Calling {phone_number}...")
                        button.setStyleSheet("background-color: #E81123;")
                    elif state == PhoneState.IN_CALL:
                        button.setText("Hang Up")
                        button.setIcon(self.style().standardIcon(QStyle.SP_DialogCancelButton))
                        status_label.setText(f"In Call with {phone_number}")
                        button.setStyleSheet("background-color: #E81123;")
                    elif state == PhoneState.RINGING:
                        button.setText("Answer")
                        button.setIcon(self.style().standardIcon(QStyle.SP_DialogApplyButton))
                        status_label.setText(f"Incoming Call from {phone_number}")
                        button.setStyleSheet("background-color: #107C10;")
                else:
                    print("Warning: statusLabel property not found")
            else:
                print("Warning: QFrame not found")
        else:
            print("Warning: Parent widget not found")

    def update_waveform(self, client_id, data):
        print(f"Updating waveform for client_id {client_id}")
        waveform = self.phone1_waveform if client_id == 0 else self.phone2_waveform
        waveform.set_data(data)

    def map_state(self, state_str):
        if state_str == "RINGING":
            return PhoneState.RINGING
        elif state_str in ["CALL_END", "CALL_DROPPED"]:
            return PhoneState.IDLE
        elif state_str == "IN_CALL":
            return PhoneState.IN_CALL
        return PhoneState.IDLE  # Default to IDLE

    def set_phone_state(self, client_id, state, number=""):
        if client_id == 0:
            self.phone1_state = state
            self._update_phone_button_ui(self.phone1_button, self.phone1_state, number if number else "123-4567")
            if state == PhoneState.IDLE and hasattr(self, 'phone1_audio_timer'):
                self.phone1_audio_timer.stop()
            elif state == PhoneState.IN_CALL and (not hasattr(self, 'phone1_audio_timer') or not self.phone1_audio_timer.isActive()):
                self.phone1_audio_timer = QTimer(self)
                self.phone1_audio_timer.timeout.connect(self.send_phone1_audio)
                self.phone1_audio_timer.start(1000)
        else:
            self.phone2_state = state
            self._update_phone_button_ui(self.phone2_button, self.phone2_state, number if number else "987-6543")
            if state == PhoneState.IDLE and hasattr(self, 'phone2_audio_timer'):
                self.phone2_audio_timer.stop()
            elif state == PhoneState.IN_CALL and (not hasattr(self, 'phone2_audio_timer') or not self.phone2_audio_timer.isActive()):
                self.phone2_audio_timer = QTimer(self)
                self.phone2_audio_timer.timeout.connect(self.send_phone2_audio)
                self.phone2_audio_timer.start(1000)

    def phone1_action(self):
        print("Phone 1 Action")
        if self.phone1_state == PhoneState.IDLE:
            self.phone1_state = PhoneState.CALLING
            self.phone1_client.send("RINGING")
            self._update_phone_button_ui(self.phone1_button, self.phone1_state, "123-4567")
        elif self.phone1_state == PhoneState.CALLING:
            self.phone1_state = PhoneState.IDLE
            self.phone1_client.send("CALL_END")
            self._update_phone_button_ui(self.phone1_button, self.phone1_state)
            if hasattr(self, 'phone1_audio_timer'):
                self.phone1_audio_timer.stop()
        elif self.phone1_state == PhoneState.RINGING:
            self.phone1_state = PhoneState.IN_CALL
            self.phone2_state = PhoneState.IN_CALL  # Sync both phones
            self.phone1_client.send("IN_CALL")
            self._update_phone_button_ui(self.phone1_button, self.phone1_state, "123-4567")
            self._update_phone_button_ui(self.phone2_button, self.phone2_state, "987-6543")
            # Start audio timer
            self.phone1_audio_timer = QTimer(self)
            self.phone1_audio_timer.timeout.connect(self.send_phone1_audio)
            self.phone1_audio_timer.start(1000)
        elif self.phone1_state == PhoneState.IN_CALL:
            self.phone1_state = PhoneState.IDLE
            self.phone2_state = PhoneState.IDLE  # Sync both phones
            self.phone1_client.send("CALL_END")
            self._update_phone_button_ui(self.phone1_button, self.phone1_state)
            self._update_phone_button_ui(self.phone2_button, self.phone2_state)
            if hasattr(self, 'phone1_audio_timer'):
                self.phone1_audio_timer.stop()

    def send_phone1_audio(self):
        if self.phone1_state == PhoneState.IN_CALL:
            message = f"Audio packet {random.randint(1, 1000)}"
            self.phone1_client.send(message)

    def phone2_action(self):
        print("Phone 2 Action")
        if self.phone2_state == PhoneState.IDLE:
            self.phone2_state = PhoneState.CALLING
            self.phone2_client.send("RINGING")
            self._update_phone_button_ui(self.phone2_button, self.phone2_state, "987-6543")
        elif self.phone2_state == PhoneState.CALLING:
            self.phone2_state = PhoneState.IDLE
            self.phone2_client.send("CALL_END")
            self._update_phone_button_ui(self.phone2_button, self.phone2_state)
            if hasattr(self, 'phone2_audio_timer'):
                self.phone2_audio_timer.stop()
        elif self.phone2_state == PhoneState.RINGING:
            self.phone2_state = PhoneState.IN_CALL
            self.phone1_state = PhoneState.IN_CALL  # Sync both phones
            self.phone2_client.send("IN_CALL")
            self._update_phone_button_ui(self.phone2_button, self.phone2_state, "987-6543")
            self._update_phone_button_ui(self.phone1_button, self.phone1_state, "123-4567")
            # Start audio timer
            self.phone2_audio_timer = QTimer(self)
            self.phone2_audio_timer.timeout.connect(self.send_phone2_audio)
            self.phone2_audio_timer.start(1000)
        elif self.phone2_state == PhoneState.IN_CALL:
            self.phone2_state = PhoneState.IDLE
            self.phone1_state = PhoneState.IDLE  # Sync both phones
            self.phone2_client.send("CALL_END")
            self._update_phone_button_ui(self.phone2_button, self.phone2_state)
            self._update_phone_button_ui(self.phone1_button, self.phone1_state)
            if hasattr(self, 'phone2_audio_timer'):
                self.phone2_audio_timer.stop()

    def send_phone2_audio(self):
        if self.phone2_state == PhoneState.IN_CALL:
            message = f"Audio packet {random.randint(1, 1000)}"
            self.phone2_client.send(message)

    def settings_action(self):
        print("Settings clicked")

    def closeEvent(self, event):
        self.phone1_client.stop()
        self.phone2_client.stop()
        event.accept()

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = PhoneUI()
    window.show()
    sys.exit(app.exec_())