import sys
import ctypes
from PyQt6.QtCore import Qt, pyqtSignal, QObject, QThread
from PyQt6.QtGui import QPixmap
from PyQt6.QtWidgets import (
    QApplication, QWidget, QVBoxLayout, QHBoxLayout,
    QLineEdit, QPushButton, QLabel, QTextEdit
)

from predict import solve


def shot(target, width, height):
    lib = ctypes.CDLL('./shotrdp.dll')

    lib.GetScreen.argtypes = [
        ctypes.c_char_p,
        ctypes.POINTER(ctypes.POINTER(ctypes.c_char)),
        ctypes.POINTER(ctypes.c_int),
        ctypes.c_int,
        ctypes.c_int,
    ]
    lib.GetScreen.restype = ctypes.c_char_p

    data = ctypes.POINTER(ctypes.c_char)()
    length = ctypes.c_int()

    error_ptr = lib.GetScreen(target, ctypes.byref(data), ctypes.byref(length), width, height)
    if error_ptr:
        lib.Free(data)
        return None, ctypes.string_at(error_ptr).decode()
    else:
        image_bytes = ctypes.string_at(data, length.value)
        lib.Free(data)
        return image_bytes, ''


class Worker(QObject):
    error_occurred = pyqtSignal(str)
    image_signal = pyqtSignal(bytes, str)
    finished = pyqtSignal(str)

    def __init__(self, target):
        super().__init__()
        self.target = target

    def run(self):
        try:
            image_bytes, error_msg = shot(self.target, 1024, 800)
            if error_msg:
                self.error_occurred.emit(error_msg)
            else:
                self.image_signal.emit(image_bytes, error_msg)

                result = solve(image_bytes)
                self.finished.emit(result)
        except Exception as e:
            self.error_occurred.emit(str(e))


class MainWindow(QWidget):
    def __init__(self):
        super().__init__()
        self.init_ui()

    def init_ui(self):
        # 输入区域
        input_layout = QHBoxLayout()

        # 地址部分
        address_group = QHBoxLayout()
        self.address_label = QLabel("Host: ")
        self.address_input = QLineEdit()
        self.address_input.setText("127.0.0.1")
        address_group.addWidget(self.address_label)
        address_group.addWidget(self.address_input)

        # 端口部分
        port_group = QHBoxLayout()
        self.port_label = QLabel("Port: ")
        self.port_input = QLineEdit()
        self.port_input.setText("3389")
        port_group.addWidget(self.port_label)
        port_group.addWidget(self.port_input)

        # 开始按钮
        self.start_btn = QPushButton("Start")

        # 组合布局
        input_layout.addLayout(address_group)
        input_layout.addLayout(port_group)
        input_layout.addWidget(self.start_btn)
        input_layout.setSpacing(20)

        address_group.setSpacing(5)
        port_group.setSpacing(5)

        # 显示区域
        self.image_label = QLabel()
        self.image_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
        self.image_label.setMinimumSize(512, 400)

        self.log_output = QTextEdit()
        self.log_output.setReadOnly(True)
        self.log_output.setFixedHeight(400)

        display_layout = QHBoxLayout()
        display_layout.addWidget(self.image_label)
        display_layout.addWidget(self.log_output)

        # 主布局
        main_layout = QVBoxLayout()
        main_layout.addLayout(input_layout)
        main_layout.addLayout(display_layout)
        self.setLayout(main_layout)

        # 事件绑定
        self.start_btn.clicked.connect(self.start_scan)

        # 窗口设置
        self.setWindowTitle("Shot RDP - By yv1ing")
        self.setGeometry(100, 100, 900, 450)

    def start_scan(self):
        address = self.address_input.text().strip()
        port = self.port_input.text().strip()

        if not address or not port:
            self.log_output.append("[WARN] The address and port cannot be empty")
            return

        try:
            port = int(port)
            if not (0 < port <= 65535):
                raise ValueError
        except ValueError:
            self.log_output.append("[WARN] The port number is invalid.")
            return

        target = f"{address}:{port}".encode()
        self.log_output.append(f"[INFO] Start identifying: {target.decode()}")

        # 创建线程
        self.worker = Worker(target)
        self.thread = QThread()
        self.worker.moveToThread(self.thread)

        # 信号连接
        self.thread.started.connect(self.worker.run)
        self.worker.finished.connect(self.solve_result)
        self.worker.image_signal.connect(self.update_image)
        self.worker.error_occurred.connect(self.show_error)
        self.worker.finished.connect(self.thread.quit)
        self.worker.error_occurred.connect(self.thread.quit)
        self.thread.finished.connect(self.thread.deleteLater)

        # 禁用按钮防止重复点击
        self.start_btn.setEnabled(False)
        self.thread.finished.connect(lambda: self.start_btn.setEnabled(True))

        self.thread.start()

    def update_image(self, image_bytes, _):
        pixmap = QPixmap()
        pixmap.loadFromData(image_bytes)
        if not pixmap.isNull():
            scaled_pixmap = pixmap.scaled(512, 400, Qt.AspectRatioMode.KeepAspectRatio)
            self.image_label.setPixmap(scaled_pixmap)
            self.log_output.append("[INFO] Image acquisition successful")
        else:
            self.log_output.append("[WARN] Image data is invalid")

    def solve_result(self, result):
        self.log_output.append(f"[INFO] Operating system version: {result}")
        self.log_output.append("-" * 70)

    def show_error(self, error_msg):
        self.log_output.append(f"[WARN] {error_msg}")


if __name__ == "__main__":
    app = QApplication(sys.argv)

    window = MainWindow()
    window.show()
    sys.exit(app.exec())
