import pyperclip
import time
import string
from pynput.keyboard import Key, Controller, Listener
from PyQt5 import QtWidgets, QtGui, QtCore

delay = 100
first_char_delay = 2000
shift_delay = 100
SETTINGS_FILE = 'settings.txt'


class SystemTrayIcon(QtWidgets.QSystemTrayIcon):
    def init(self, icon, parent=None):
        QtWidgets.QSystemTrayIcon.init(self, icon, parent)
        self.menu = QtWidgets.QMenu()
        self.settings_action = self.menu.addAction('Settings')
        self.exit_action = self.menu.addAction('Exit')
        self.setContextMenu(self.menu)
        self.settings_action.triggered.connect(self.open_settings)
        self.exit_action.triggered.connect(self.close_settings)
        self.settings_dialog = None
        self.worker = Worker()
        self.worker.start()
        self.worker.clipboard_changed.connect(self.handle_clipboard_change)
        self.keyboard_listener = Listener(on_press=self.on_press)
        self.keyboard_listener.start()
        self.is_typing = False

    def on_press(self, key):
        ctrl_alt_shift_pressed = (
            Listener.Control
            and Listener.alt_gr
            and any(k in Listener.pressed for k in (Key.shift, Key.shift_r))
        )

        if not self.is_typing and ctrl_alt_shift_pressed:
            self.is_typing = True
            self.type_copied_text()
            self.is_typing = False

    def type_copied_text(self):
        new_clipboard_content = pyperclip.paste().strip()
        if new_clipboard_content:
            # add a delay before typing the first character
            time.sleep(first_char_delay / 1000)
            keyboard = Controller()
            shift_chars = string.ascii_uppercase + ''.join(
                c for c in string.printable if not c.isalnum()
            )
            for char in new_clipboard_content:
                if char in string.printable:
                    if char in shift_chars:
                        keyboard.press(Key.shift)
                        time.sleep(shift_delay / 1000)
                    keyboard.press(char)
                    keyboard.release(char)
                    if char in shift_chars:
                        time.sleep(shift_delay / 1000)
                        keyboard.release(Key.shift)
                    time.sleep(delay / 1000)

    def handle_clipboard_change(self, text):
        self.worker.clipboard_content = text

    def open_settings(self):
        if not self.settings_dialog:
            self.settings_dialog = SettingsDialog()
        self.settings_dialog.show()

    def close_settings(self):
        QtWidgets.QApplication.quit()


class Worker(QtCore.QThread):
    clipboard_changed = QtCore.pyqtSignal(str)

    def init(self, parent=None):
        super().init(parent)
        self.clipboard_content = pyperclip.paste()

    def run(self):
        while True:
            new_clipboard_content = pyperclip.paste()
            if new_clipboard_content != self.clipboard_content:
                self.clipboard_content = new_clipboard_content
                self.clipboard_changed.emit(new_clipboard_content)
            time.sleep(0.1)


class SettingsDialog(QtWidgets.QDialog):
    def init(self, parent=None):
        super().init(parent)
        self.setWindowTitle('Settings')
        self.setFixedSize(400, 300)
        self.delay_label = QtWidgets.QLabel('Delay between characters (ms):')
        self.delay_spinbox = QtWidgets.QSpinBox()
        self.delay_spinbox.setMinimum(0)
        self.delay_spinbox.setMaximum(10000)
        self.delay_spinbox.setValue(delay)
        self.first_char_delay_label = QtWidgets.QLabel('Delay before first character (ms):')
        self.first_char_delay_spinbox = QtWidgets.QSpinBox()
        self.first_char_delay_spinbox.setMinimum(0)
        self.first_char_delay_spinbox.setMaximum(10000)
        self.first_char_delay_spinbox.setValue(first_char_delay)
        self.shift_delay_label = QtWidgets.QLabel('Shift key delay (ms):')
        self.shift_delay_spinbox = QtWidgets.QSpinBox()
        self.shift_delay_spinbox.setMinimum(0)
        self.shift_delay_spinbox.setMaximum(1000)
        self.shift_delay_spinbox.setValue(shift_delay)
        self.save_button = QtWidgets.QPushButton('Save')
        self.cancel_button = QtWidgets.QPushButton('Cancel')
        self.save_button.clicked.connect(self.save_settings)
        self.cancel_button.clicked.connect(self.reject)
        layout = QtWidgets.QVBoxLayout()
        layout.addWidget(self.delay_label)
        layout.addWidget(self.delay_spinbox)
        layout.addWidget(self.first_char_delay_label)
        layout.addWidget(self.first_char_delay_spinbox)
        layout.addWidget(self.shift_delay_label)
        layout.addWidget(self.shift_delay_spinbox)
        buttons_layout = QtWidgets.QHBoxLayout()
        buttons_layout.addWidget(self.save_button)
        buttons_layout.addWidget(self.cancel_button)
        layout.addLayout(buttons_layout)
        self.setLayout(layout)

    def save_settings(self):
        global delay, first_char_delay, shift_delay
        delay = self.delay_spinbox.value()
        first_char_delay = self.first_char_delay_spinbox.value()
        shift_delay = self.shift_delay_spinbox.value()
        with open(SETTINGS_FILE, 'w') as f:
            f.write(f"{delay}\n{first_char_delay}\n{shift_delay}")
        self.accept()


if __name__ == '__main__':
    app = QtWidgets.QApplication([])
    app.setQuitOnLastWindowClosed(False)
    tray_icon = SystemTrayIcon(QtGui.QIcon('icon.png'))
    tray_icon.show()
    try:
        with open(SETTINGS_FILE, 'r') as f:
            lines = f.read().splitlines()
            delay = int(lines[0])
            first_char_delay = int(lines[1])
            shift_delay = int(lines[2])
    except (FileNotFoundError, ValueError):
        pass
    app.exec_()