import sys
import time
import pickle
from threading import Thread, Lock
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt, QSize, QTimer, pyqtSignal, QObject
from PyQt5.QtGui import QIcon, QFont, QColor, QKeySequence
from PyQt5.QtWidgets import (
    QApplication, QMainWindow, QPushButton, QListWidget, QVBoxLayout,
    QWidget, QFileDialog, QMessageBox, QHBoxLayout, QLabel, QTabWidget,
    QFrame, QSplitter, QSizePolicy, QLineEdit, QAbstractItemView, QAction,
    QToolButton, QProgressBar, QCheckBox, QDialog, QDialogButtonBox,
    QFormLayout, QComboBox, QTextEdit
)
from pynput import mouse, keyboard
from pynput.keyboard import Key, Listener as KeyboardListener, KeyCode, GlobalHotKeys

# Data structure to hold events
class MacroEvent:
    def __init__(self, event_type, details, timestamp):
        self.event_type = event_type  # 'mouse_move', 'mouse_click', 'key_press', etc.
        self.details = details        # Details like position, button, key
        self.timestamp = timestamp    # Time since the last event

# Signals class for inter-thread communication
class Signals(QObject):
    event_recorded = pyqtSignal(object)
    recording_finished = pyqtSignal()
    playback_finished = pyqtSignal()
    playback_error = pyqtSignal(str)
    toggle_record = pyqtSignal()
    toggle_play = pyqtSignal()

# Worker class for recording events
class RecorderWorker(QObject):
    def __init__(self, signals):
        super().__init__()
        self.signals = signals
        self.is_recording = False
        self.start_time = None
        self.last_time = None
        self.events = []
        self.lock = Lock()

    def start_recording(self):
        with self.lock:
            self.is_recording = True
            self.events = []
            self.start_time = time.time()
            self.last_time = self.start_time

        # Start listeners
        with mouse.Listener(
            on_move=self.on_move,
            on_click=self.on_click,
            on_scroll=self.on_scroll
        ) as self.mouse_listener, \
             keyboard.Listener(
            on_press=self.on_press,
            on_release=self.on_release
        ) as self.keyboard_listener:
            self.mouse_listener.join()
            self.keyboard_listener.join()

    def stop_recording(self):
        with self.lock:
            self.is_recording = False
        if self.mouse_listener and self.mouse_listener.running:
            self.mouse_listener.stop()
        if self.keyboard_listener and self.keyboard_listener.running:
            self.keyboard_listener.stop()
        self.signals.recording_finished.emit()

    def on_move(self, x, y):
        with self.lock:
            if not self.is_recording:
                return
            current_time = time.time()
            delta = current_time - self.last_time
            self.last_time = current_time
            event = MacroEvent('mouse_move', {'position': (x, y)}, delta)
            self.events.append(event)
            self.signals.event_recorded.emit(event)

    def on_click(self, x, y, button, pressed):
        with self.lock:
            if not self.is_recording:
                return
            current_time = time.time()
            delta = current_time - self.last_time
            self.last_time = current_time
            event_type = 'mouse_click_pressed' if pressed else 'mouse_click_released'
            event = MacroEvent(event_type, {'position': (x, y), 'button': button}, delta)
            self.events.append(event)
            self.signals.event_recorded.emit(event)

    def on_scroll(self, x, y, dx, dy):
        with self.lock:
            if not self.is_recording:
                return
            current_time = time.time()
            delta = current_time - self.last_time
            self.last_time = current_time
            event = MacroEvent('mouse_scroll', {'position': (x, y), 'dx': dx, 'dy': dy}, delta)
            self.events.append(event)
            self.signals.event_recorded.emit(event)

    def on_press(self, key):
        with self.lock:
            if not self.is_recording:
                return
            current_time = time.time()
            delta = current_time - self.last_time
            self.last_time = current_time
            event = MacroEvent('key_press', {'key': key}, delta)
            self.events.append(event)
            self.signals.event_recorded.emit(event)

    def on_release(self, key):
        with self.lock:
            if not self.is_recording:
                return
            current_time = time.time()
            delta = current_time - self.last_time
            self.last_time = current_time
            event = MacroEvent('key_release', {'key': key}, delta)
            self.events.append(event)
            self.signals.event_recorded.emit(event)

# Worker class for playing back events
class PlayerWorker(QObject):
    def __init__(self, events, signals, continuous=False):
        super().__init__()
        self.events = events
        self.signals = signals
        self.is_playing = False
        self.continuous = continuous
        self.lock = Lock()

    def play_events(self):
        try:
            self.is_playing = True
            controller_mouse = mouse.Controller()
            controller_keyboard = keyboard.Controller()

            while self.is_playing:
                for event in self.events:
                    with self.lock:
                        if not self.is_playing:
                            break
                    time.sleep(event.timestamp)

                    if event.event_type == 'mouse_move':
                        controller_mouse.position = event.details['position']
                    elif event.event_type == 'mouse_click_pressed':
                        controller_mouse.position = event.details['position']
                        controller_mouse.press(event.details['button'])
                    elif event.event_type == 'mouse_click_released':
                        controller_mouse.position = event.details['position']
                        controller_mouse.release(event.details['button'])
                    elif event.event_type == 'mouse_scroll':
                        controller_mouse.scroll(event.details['dx'], event.details['dy'])
                    elif event.event_type == 'key_press':
                        controller_keyboard.press(event.details['key'])
                    elif event.event_type == 'key_release':
                        controller_keyboard.release(event.details['key'])

                if not self.continuous:
                    break

            self.signals.playback_finished.emit()

        except Exception as e:
            self.signals.playback_error.emit(str(e))

    def stop_playback(self):
        with self.lock:
            self.is_playing = False

# Hotkey Listener Class using pynput's GlobalHotKeys
class KeyboardHotkeyListener:
    def __init__(self, hotkeys, signals):
        """
        hotkeys: Dictionary with keys 'record' and 'play' mapping to their hotkey strings
        signals: Signals object for inter-thread communication
        """
        self.hotkeys = hotkeys
        self.signals = signals
        self.keys = {}
        self.listener = None
        self.thread = None
        self.lock = Lock()

    def create_hotkeys(self):
        """
        Creates a dictionary suitable for pynput's GlobalHotKeys based on current hotkeys
        """
        hotkey_dict = {}
        for action, hotkey in self.hotkeys.items():
            if hotkey:
                formatted_hotkey = '<' + '><'.join(hotkey.split('+')) + '>'
                if action == 'record':
                    hotkey_dict[formatted_hotkey] = self.signals.toggle_record.emit
                elif action == 'play':
                    hotkey_dict[formatted_hotkey] = self.signals.toggle_play.emit
        return hotkey_dict

    def start_listening(self):
        with self.lock:
            if self.listener:
                return  # Already listening

            hotkey_dict = self.create_hotkeys()
            if not hotkey_dict:
                return  # No hotkeys defined

            self.listener = GlobalHotKeys(hotkey_dict)
            self.listener.start()

    def stop_listening(self):
        with self.lock:
            if self.listener:
                self.listener.stop()
                self.listener = None

    def update_hotkeys(self, new_hotkeys):
        with self.lock:
            self.hotkeys = new_hotkeys
            self.stop_listening()
            self.start_listening()

    def enable_hotkeys(self):
        self.start_listening()

    def disable_hotkeys(self):
        self.stop_listening()

# Dialog for setting hotkeys
class HotkeyDialog(QDialog):
    def __init__(self, parent=None, current_hotkeys=None):
        super().__init__(parent)
        self.setWindowTitle("Set Hotkeys")
        self.setFixedSize(400, 250)
        self.current_hotkeys = current_hotkeys or {}
        self.new_hotkeys = {}

        layout = QVBoxLayout()

        form_layout = QFormLayout()

        self.record_hotkey_input = QLineEdit()
        self.record_hotkey_input.setPlaceholderText("Press new hotkey")
        self.record_hotkey_input.setText(self.current_hotkeys.get("record", "Ctrl+Alt+R"))
        self.record_hotkey_input.installEventFilter(self)
        form_layout.addRow("Record Hotkey:", self.record_hotkey_input)

        self.play_hotkey_input = QLineEdit()
        self.play_hotkey_input.setPlaceholderText("Press new hotkey")
        self.play_hotkey_input.setText(self.current_hotkeys.get("play", "Ctrl+Alt+P"))
        self.play_hotkey_input.installEventFilter(self)
        form_layout.addRow("Play Hotkey:", self.play_hotkey_input)

        layout.addLayout(form_layout)

        # Instructions
        instruction_label = QLabel("Press the desired key combination for each action.")
        instruction_label.setStyleSheet("font-size: 12px; color: gray;")
        layout.addWidget(instruction_label)

        # Buttons
        self.buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        self.buttons.accepted.connect(self.accept)
        self.buttons.rejected.connect(self.reject)
        layout.addWidget(self.buttons)

        self.setLayout(layout)

        self.current_input = None
        self.hotkey = ""
        self.keys = []

    def eventFilter(self, source, event):
        if event.type() == QtCore.QEvent.KeyPress and source in [self.record_hotkey_input, self.play_hotkey_input]:
            modifiers = []
            if event.modifiers() & Qt.ControlModifier:
                modifiers.append("Ctrl")
            if event.modifiers() & Qt.AltModifier:
                modifiers.append("Alt")
            if event.modifiers() & Qt.ShiftModifier:
                modifiers.append("Shift")
            if event.modifiers() & Qt.MetaModifier:
                modifiers.append("Meta")

            key = event.key()
            key_name = QKeySequence(key).toString()
            if key_name:
                # Prevent capturing modifier keys alone
                if key in [Qt.Key_Control, Qt.Key_Shift, Qt.Key_Alt, Qt.Key_Meta]:
                    return True
                self.hotkey = "+".join(modifiers + [key_name])
                source.setText(self.hotkey)
                return True
        return super().eventFilter(source, event)

    def get_hotkeys(self):
        return {
            "record": self.record_hotkey_input.text(),
            "play": self.play_hotkey_input.text()
        }

# Main Application Window
class MacroRecorderPlayer(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Advanced Macro Recorder")
        self.setGeometry(100, 100, 1300, 900)
        self.setWindowIcon(QIcon.fromTheme("media-record"))  # Use appropriate icon

        self.events = []
        self.is_recording = False
        self.is_playing = False
        self.continuous_playback = False
        self.hotkeys = {"record": "Ctrl+Alt+R", "play": "Ctrl+Alt+P"}  # Default hotkeys

        # Signals for inter-thread communication
        self.signals = Signals()
        self.signals.event_recorded.connect(self.on_event_recorded)
        self.signals.recording_finished.connect(self.on_recording_finished)
        self.signals.playback_finished.connect(self.on_playback_finished)
        self.signals.playback_error.connect(self.on_playback_error)
        self.signals.toggle_record.connect(self.toggle_recording_triggered)
        self.signals.toggle_play.connect(self.toggle_playback_triggered)

        # Initialize UI Components
        self.init_ui()

        # Initialize Threads
        self.init_threads()

        # Initialize Hotkey Listener
        self.init_hotkey_listener()

    def init_ui(self):
        # Central Widget
        central_widget = QWidget()
        self.setCentralWidget(central_widget)

        # Main Layout
        main_layout = QHBoxLayout()
        central_widget.setLayout(main_layout)

        # Sidebar
        self.sidebar = QFrame()
        self.sidebar.setFrameShape(QFrame.StyledPanel)
        self.sidebar.setFixedWidth(220)
        sidebar_layout = QVBoxLayout()
        self.sidebar.setLayout(sidebar_layout)
        sidebar_layout.setAlignment(Qt.AlignTop)

        # Toggle Sidebar Button
        self.toggle_sidebar_btn = QToolButton()
        self.toggle_sidebar_btn.setIcon(QIcon.fromTheme("go-next"))
        self.toggle_sidebar_btn.setToolTip("Toggle Sidebar")
        self.toggle_sidebar_btn.setCheckable(True)
        self.toggle_sidebar_btn.setChecked(True)
        self.toggle_sidebar_btn.clicked.connect(self.toggle_sidebar)
        sidebar_layout.addWidget(self.toggle_sidebar_btn)

        # Spacer
        sidebar_layout.addSpacing(10)

        # Control Buttons with Icons and Animations
        self.record_button = QPushButton(" Record")
        self.record_button.setIcon(QIcon.fromTheme("media-record"))
        self.record_button.setStyleSheet("""
            QPushButton {
                background-color: #e32636;
                color: white;
                font-size: 14px;
                border: none;
                padding: 12px;
                text-align: left;
                border-radius: 6px;
            }
            QPushButton:hover {
                background-color: #ff3545;
            }
        """)
        self.record_button.setIconSize(QSize(24,24))
        self.record_button.clicked.connect(self.toggle_recording)
        sidebar_layout.addWidget(self.record_button)

        self.play_button = QPushButton(" Play")
        self.play_button.setIcon(QIcon.fromTheme("media-playback-start"))
        self.play_button.setStyleSheet("""
            QPushButton {
                background-color: #27ae60;
                color: white;
                font-size: 14px;
                border: none;
                padding: 10px;
                text-align: left;
                border-radius: 5px;
            }
            QPushButton:hover {
                background-color: #1e8449;
            }
        """)
        self.play_button.setIconSize(QSize(24,24))
        self.play_button.setEnabled(False)
        self.play_button.clicked.connect(self.play_macro)
        sidebar_layout.addWidget(self.play_button)

        self.save_button = QPushButton(" Save")
        self.save_button.setIcon(QIcon.fromTheme("document-save"))
        self.save_button.setStyleSheet("""
            QPushButton {
                background-color: #2980b9;
                color: white;
                font-size: 14px;
                border: none;
                padding: 10px;
                text-align: left;
                border-radius: 5px;
            }
            QPushButton:hover {
                background-color: #1f618d;
            }
        """)
        self.save_button.setIconSize(QSize(24,24))
        self.save_button.setEnabled(False)
        self.save_button.clicked.connect(self.save_macro)
        sidebar_layout.addWidget(self.save_button)

        self.load_button = QPushButton(" Load")
        self.load_button.setIcon(QIcon.fromTheme("document-open"))
        self.load_button.setStyleSheet("""
            QPushButton {
                background-color: #8e44ad;
                color: white;
                font-size: 14px;
                border: none;
                padding: 10px;
                text-align: left;
                border-radius: 5px;
            }
            QPushButton:hover {
                background-color: #732d91;
            }
        """)
        self.load_button.setIconSize(QSize(24,24))
        self.load_button.setEnabled(True)
        self.load_button.clicked.connect(self.load_macro)
        sidebar_layout.addWidget(self.load_button)

        self.clear_button = QPushButton(" Clear")
        self.clear_button.setIcon(QIcon.fromTheme("edit-clear"))
        self.clear_button.setStyleSheet("""
            QPushButton {
                background-color: #f39c12;
                color: white;
                font-size: 14px;
                border: none;
                padding: 10px;
                text-align: left;
                border-radius: 5px;
            }
            QPushButton:hover {
                background-color: #d68910;
            }
        """)
        self.clear_button.setIconSize(QSize(24,24))
        self.clear_button.setEnabled(False)
        self.clear_button.clicked.connect(self.clear_events)
        sidebar_layout.addWidget(self.clear_button)

        # Spacer
        sidebar_layout.addStretch()

        # Settings Button
        self.settings_button = QPushButton(" Settings")
        self.settings_button.setIcon(QIcon.fromTheme("preferences-system"))
        self.settings_button.setStyleSheet("""
            QPushButton {
                background-color: #7f8c8d;
                color: white;
                font-size: 14px;
                border: none;
                padding: 10px;
                text-align: left;
                border-radius: 5px;
            }
            QPushButton:hover {
                background-color: #707b7c;
            }
        """)
        self.settings_button.setIconSize(QSize(24,24))
        self.settings_button.clicked.connect(self.open_settings)
        sidebar_layout.addWidget(self.settings_button)

        # Main Content Area
        self.content = QFrame()
        self.content.setFrameShape(QFrame.StyledPanel)
        content_layout = QVBoxLayout()
        self.content.setLayout(content_layout)

        # Top Status Bar
        self.status_bar_label = QLabel("Status: Idle")
        self.status_bar_label.setStyleSheet("font-size: 16px; padding: 5px;")
        content_layout.addWidget(self.status_bar_label)

        # Real-Time Mouse Position
        self.mouse_position_label = QLabel("Mouse Position: (0, 0)")
        self.mouse_position_label.setStyleSheet("font-size: 14px;")
        content_layout.addWidget(self.mouse_position_label)

        # Search Bar
        search_layout = QHBoxLayout()
        self.search_input = QLineEdit()
        self.search_input.setPlaceholderText("Search Events...")
        self.search_input.textChanged.connect(self.search_events)
        search_layout.addWidget(QLabel("Search:"))
        search_layout.addWidget(self.search_input)
        content_layout.addLayout(search_layout)

        # Splitter for Event Lists and Details
        splitter = QSplitter(Qt.Vertical)

        # Tabbed Interface for Events
        self.tabs = QTabWidget()
        self.tabs.setTabPosition(QTabWidget.North)
        self.tabs.setTabsClosable(False)
        self.tabs.setMovable(False)

        # Mouse Movements Tab
        self.mouse_move_tab = QListWidget()
        self.mouse_move_tab.setSelectionMode(QAbstractItemView.SingleSelection)
        self.mouse_move_tab.itemClicked.connect(self.display_event_details)
        self.tabs.addTab(self.mouse_move_tab, QIcon.fromTheme("system-run"), "Mouse Movements")

        # Mouse Clicks Tab
        self.mouse_click_tab = QListWidget()
        self.mouse_click_tab.setSelectionMode(QAbstractItemView.SingleSelection)
        self.mouse_click_tab.itemClicked.connect(self.display_event_details)
        self.tabs.addTab(self.mouse_click_tab, QIcon.fromTheme("go-jump"), "Mouse Clicks")

        # Mouse Scrolls Tab
        self.mouse_scroll_tab = QListWidget()
        self.mouse_scroll_tab.setSelectionMode(QAbstractItemView.SingleSelection)
        self.mouse_scroll_tab.itemClicked.connect(self.display_event_details)
        self.tabs.addTab(self.mouse_scroll_tab, QIcon.fromTheme("text-x-generic"), "Mouse Scrolls")

        # Keyboard Events Tab
        self.keyboard_tab = QListWidget()
        self.keyboard_tab.setSelectionMode(QAbstractItemView.SingleSelection)
        self.keyboard_tab.itemClicked.connect(self.display_event_details)
        self.tabs.addTab(self.keyboard_tab, QIcon.fromTheme("input-keyboard"), "Keyboard Events")

        splitter.addWidget(self.tabs)

        # Event Details Panel
        self.details_panel = QTextEdit()
        self.details_panel.setReadOnly(True)
        self.details_panel.setFixedHeight(150)
        splitter.addWidget(self.details_panel)

        splitter.setStretchFactor(0, 3)
        splitter.setStretchFactor(1, 1)

        content_layout.addWidget(splitter)

        # Progress Bar
        self.progress_bar = QProgressBar()
        self.progress_bar.setValue(0)
        self.progress_bar.setVisible(False)
        content_layout.addWidget(self.progress_bar)

        # Continuous Playback Checkbox
        self.continuous_playback_checkbox = QCheckBox("Enable Continuous Playback")
        content_layout.addWidget(self.continuous_playback_checkbox)

        # Add Widgets to Main Layout
        main_layout.addWidget(self.sidebar)
        main_layout.addWidget(self.content)

        # Timer for updating mouse position
        self.timer = QTimer()
        self.timer.timeout.connect(self.update_mouse_position)
        self.timer.start(100)  # Update every 100 milliseconds

        # Connect list widgets to display event details
        self.mouse_move_tab.itemClicked.connect(self.display_event_details)
        self.mouse_click_tab.itemClicked.connect(self.display_event_details)
        self.mouse_scroll_tab.itemClicked.connect(self.display_event_details)
        self.keyboard_tab.itemClicked.connect(self.display_event_details)

        # Apply initial light theme
        self.dark_mode = False
        self.apply_light_theme()

    def init_threads(self):
        # Recorder Thread
        self.recorder_worker = RecorderWorker(self.signals)
        self.record_thread = Thread(target=self.recorder_worker.start_recording, daemon=True)

        # Player Thread
        self.player_worker = None
        self.player_thread = None

    def init_hotkey_listener(self):
        self.hotkey_listener = KeyboardHotkeyListener(self.hotkeys, self.signals)
        self.hotkey_listener_thread = Thread(target=self.hotkey_listener.start_listening, daemon=True)
        self.hotkey_listener_thread.start()

    def toggle_sidebar(self):
        if self.toggle_sidebar_btn.isChecked():
            self.sidebar.setFixedWidth(220)
            self.toggle_sidebar_btn.setIcon(QIcon.fromTheme("go-next"))
            # Restore button texts
            for button in [self.record_button, self.play_button, self.save_button, self.load_button, self.clear_button, self.settings_button]:
                button.setText(button.toolTip())
        else:
            self.sidebar.setFixedWidth(60)
            self.toggle_sidebar_btn.setIcon(QIcon.fromTheme("go-previous"))
            # Hide button texts
            for button in [self.record_button, self.play_button, self.save_button, self.load_button, self.clear_button, self.settings_button]:
                button.setToolTip(button.text())
                button.setText("")

    def toggle_recording_triggered(self):
        self.toggle_recording()

    def toggle_playback_triggered(self):
        self.play_macro()

    def toggle_recording(self):
        if not self.is_recording:
            self.start_recording()
        else:
            self.stop_recording()

    def start_recording(self):
        self.is_recording = True
        self.events = []
        self.clear_event_lists()
        self.record_button.setText(" Stop")
        self.record_button.setStyleSheet("""
            QPushButton {
                background-color: #95a5a6;
                color: #2c3e50;
                font-size: 14px;
                border: none;
                padding: 10px;
                text-align: left;
                border-radius: 5px;
            }
            QPushButton:hover {
                background-color: #7f8c8d;
            }
        """)
        self.play_button.setEnabled(False)
        self.save_button.setEnabled(False)
        self.clear_button.setEnabled(False)
        self.status_bar_label.setText("Status: Recording...")
        self.hotkey_listener.disable_hotkeys()
        self.record_thread = Thread(target=self.recorder_worker.start_recording, daemon=True)
        self.record_thread.start()

    def stop_recording(self):
        self.recorder_worker.stop_recording()
        self.is_recording = False
        self.record_button.setText("Record")
        self.record_button.setStyleSheet("""
            QPushButton {
                background-color: #e74c3c;
                color: white;
                font-size: 14px;
                border: none;
                padding: 10px;
                text-align: left;
                border-radius: 5px;
            }
            QPushButton:hover {
                background-color: #c0392b;
            }
        """)
        self.play_button.setEnabled(True)
        self.save_button.setEnabled(True)
        self.clear_button.setEnabled(True)
        self.status_bar_label.setText("Status: Idle")
        self.hotkey_listener.enable_hotkeys()

    def on_recording_finished(self):
        self.is_recording = False
        self.record_button.setText("Record")
        self.record_button.setStyleSheet("""
            QPushButton {
                background-color: #e74c3c;
                color: white;
                font-size: 14px;
                border: none;
                padding: 10px;
                text-align: left;
                border-radius: 5px;
            }
            QPushButton:hover {
                background-color: #c0392b;
            }
        """)
        self.play_button.setEnabled(True)
        self.save_button.setEnabled(True)
        self.clear_button.setEnabled(True)
        self.status_bar_label.setText("Status: Idle")
        self.hotkey_listener.enable_hotkeys()

    def on_event_recorded(self, event):
        self.events.append(event)
        self.add_event_to_list(event)

    def add_event_to_list(self, event):
        if event.event_type == 'mouse_move':
            text = f"Pos: {event.details['position']} | Delay: {event.timestamp:.2f}s"
            self.mouse_move_tab.addItem(text)
        elif event.event_type in ['mouse_click_pressed', 'mouse_click_released']:
            action = "Pressed" if 'pressed' in event.event_type else "Released"
            text = f"{action} {event.details['button']} at {event.details['position']} | Delay: {event.timestamp:.2f}s"
            self.mouse_click_tab.addItem(text)
        elif event.event_type == 'mouse_scroll':
            text = f"Scroll dx: {event.details['dx']} dy: {event.details['dy']} at {event.details['position']} | Delay: {event.timestamp:.2f}s"
            self.mouse_scroll_tab.addItem(text)
        elif event.event_type in ['key_press', 'key_release']:
            action = "Pressed" if 'press' in event.event_type else "Released"
            key = event.details['key']
            key_str = self.format_key(key)
            text = f"{action} {key_str} | Delay: {event.timestamp:.2f}s"
            self.keyboard_tab.addItem(text)

    def format_key(self, key):
        if isinstance(key, KeyCode):
            return key.char if key.char else str(key)
        else:
            return str(key)

    def clear_event_lists(self):
        self.mouse_move_tab.clear()
        self.mouse_click_tab.clear()
        self.mouse_scroll_tab.clear()
        self.keyboard_tab.clear()
        self.details_panel.clear()

    def play_macro(self):
        if not self.events:
            QMessageBox.warning(self, "Play Macro", "No events to play.")
            return

        self.is_playing = True
        self.continuous_playback = self.continuous_playback_checkbox.isChecked()
        self.play_button.setEnabled(False)
        self.record_button.setEnabled(False)
        self.save_button.setEnabled(False)
        self.load_button.setEnabled(False)
        self.clear_button.setEnabled(False)
        self.status_bar_label.setText("Status: Playing...")
        self.progress_bar.setVisible(True)
        self.progress_bar.setMaximum(len(self.events))
        self.progress_bar.setValue(0)
        self.hotkey_listener.disable_hotkeys()

        # Initialize Player Worker
        self.player_worker = PlayerWorker(self.events, self.signals, continuous=self.continuous_playback)
        self.play_thread = Thread(target=self.player_worker.play_events, daemon=True)
        self.player_worker.playback_finished.connect(self.on_playback_finished)
        self.player_worker.playback_error.connect(self.on_playback_error)
        self.play_thread.start()

    def on_playback_finished(self):
        self.is_playing = False
        self.play_button.setEnabled(True)
        self.record_button.setEnabled(True)
        self.save_button.setEnabled(True)
        self.load_button.setEnabled(True)
        self.clear_button.setEnabled(True)
        self.progress_bar.setVisible(False)
        self.status_bar_label.setText("Status: Idle")
        self.hotkey_listener.enable_hotkeys()
        QMessageBox.information(self, "Playback", "Macro playback completed.")
        self.progress_bar.setValue(0)

    def on_playback_error(self, error):
        QMessageBox.critical(self, "Playback Error", f"An error occurred during playback:\n{error}")
        self.is_playing = False
        self.play_button.setEnabled(True)
        self.record_button.setEnabled(True)
        self.save_button.setEnabled(True)
        self.load_button.setEnabled(True)
        self.clear_button.setEnabled(True)
        self.progress_bar.setVisible(False)
        self.status_bar_label.setText("Status: Idle")
        self.hotkey_listener.enable_hotkeys()

    def save_macro(self):
        if not self.events:
            QMessageBox.warning(self, "Save Macro", "No events to save.")
            return

        options = QFileDialog.Options()
        file_path, _ = QFileDialog.getSaveFileName(
            self, "Save Macro", "", "Macro Files (*.macro);;All Files (*)", options=options
        )
        if file_path:
            try:
                with open(file_path, 'wb') as f:
                    pickle.dump(self.events, f)
                QMessageBox.information(self, "Save Macro", f"Macro saved successfully to {file_path}.")
            except Exception as e:
                QMessageBox.critical(self, "Save Macro", f"Failed to save macro:\n{e}")

    def load_macro(self):
        options = QFileDialog.Options()
        file_path, _ = QFileDialog.getOpenFileName(
            self, "Load Macro", "", "Macro Files (*.macro);;All Files (*)", options=options
        )
        if file_path:
            try:
                with open(file_path, 'rb') as f:
                    self.events = pickle.load(f)
                self.populate_event_lists()
                self.play_button.setEnabled(True)
                self.save_button.setEnabled(True)
                self.clear_button.setEnabled(True)
                QMessageBox.information(self, "Load Macro", f"Macro loaded successfully from {file_path}.")
            except Exception as e:
                QMessageBox.critical(self, "Load Macro", f"Failed to load macro:\n{e}")

    def populate_event_lists(self):
        self.clear_event_lists()
        for event in self.events:
            self.add_event_to_list(event)

    def clear_events(self):
        self.events = []
        self.clear_event_lists()
        self.play_button.setEnabled(False)
        self.save_button.setEnabled(False)
        self.clear_button.setEnabled(False)

    def update_mouse_position(self):
        try:
            controller = mouse.Controller()
            position = controller.position
            self.mouse_position_label.setText(f"Mouse Position: {position}")
        except Exception as e:
            self.mouse_position_label.setText("Mouse Position: Error")

    def display_event_details(self, item):
        event_text = item.text()
        self.details_panel.setText(event_text)

    def search_events(self, text):
        current_tab = self.tabs.currentWidget()
        for index in range(current_tab.count()):
            item = current_tab.item(index)
            if text.lower() in item.text().lower():
                current_tab.setCurrentRow(index)
                self.display_event_details(item)
                break

    def open_settings(self):
        dialog = HotkeyDialog(self, current_hotkeys=self.hotkeys)
        if dialog.exec_():
            new_hotkeys = dialog.get_hotkeys()
            self.hotkeys = new_hotkeys
            self.hotkey_listener.update_hotkeys(self.hotkeys)
            QMessageBox.information(self, "Settings", "Hotkeys updated successfully.")

    def toggle_theme(self):
        if self.dark_mode:
            self.apply_light_theme()
            self.dark_mode = False
        else:
            self.apply_dark_theme()
            self.dark_mode = True

    def apply_dark_theme(self):
        dark_stylesheet = """
            /* Modern Dark Theme */
            QWidget {
                background-color: #1a1a1a;
                color: #ffffff;
                font-size: 14px;
            }
            
            QPushButton {
                background-color: #2d2d2d;
                color: #ffffff;
                border: none;
                padding: 12px;
                border-radius: 6px;
            }
            QPushButton:hover {
                background-color: #e32636;
            }
            
            QFrame {
                background-color: #2d2d2d;
                border-radius: 8px;
            }
            
            QLineEdit, QTextEdit {
                background-color: #2d2d2d;
                border: 1px solid #404040;
                border-radius: 6px;
                padding: 8px;
                color: #ffffff;
            }
            
            QListWidget {
                background-color: #2d2d2d;
                border: 1px solid #404040;
                border-radius: 6px;
            }
            
            QTabWidget::pane {
                border: 1px solid #404040;
                border-radius: 6px;
            }
            
            QTabBar::tab {
                background: #2d2d2d;
                border: none;
                padding: 8px 16px;
                margin: 2px;
                border-radius: 4px;
            }
            
            QTabBar::tab:selected, QTabBar::tab:hover {
                background: #e32636;
            }
            
            QProgressBar {
                border: none;
                border-radius: 4px;
                background-color: #2d2d2d;
                text-align: center;
            }
            
            QProgressBar::chunk {
                background-color: #e32636;
                border-radius: 4px;
            }
            
            QCheckBox {
                color: #ffffff;
            }
            
            QScrollBar:vertical {
                border: none;
                background: #2d2d2d;
                width: 10px;
                border-radius: 5px;
            }
            
            QScrollBar::handle:vertical {
                background: #404040;
                border-radius: 5px;
            }
            
            QScrollBar::handle:vertical:hover {
                background: #e32636;
            }
        """
        self.setStyleSheet(dark_stylesheet)

    def apply_light_theme(self):
        light_stylesheet = """
            /* Light Theme Stylesheet */
            QWidget {
                background-color: #ecf0f1;
                color: #2c3e50;
                font-size: 14px;
            }
            QPushButton {
                background-color: #bdc3c7;
                border: none;
                padding: 10px;
                border-radius: 5px;
            }
            QPushButton:hover {
                background-color: #95a5a6;
            }
            QFrame {
                background-color: #bdc3c7;
            }
            QLineEdit, QTextEdit {
                background-color: #ffffff;
                border: 1px solid #bdc3c7;
                border-radius: 5px;
                padding: 5px;
                color: #2c3e50;
            }
            QListWidget {
                background-color: #ffffff;
                border: 1px solid #bdc3c7;
                border-radius: 5px;
            }
            QTabWidget::pane { /* The tab widget frame */
                border-top: 2px solid #C2C7CB;
            }
            QTabBar::tab {
                background: #bdc3c7;
                border: 2px solid #2c3e50;
                border-bottom-color: #C2C7CB; /* same as the pane color */
                border-top-left-radius: 4px;
                border-top-right-radius: 4px;
                padding: 5px;
            }
            QTabBar::tab:selected, QTabBar::tab:hover {
                background: #95a5a6;
            }
            QProgressBar {
                border: 1px solid #2c3e50;
                border-radius: 5px;
                text-align: center;
            }
            QProgressBar::chunk {
                background-color: #1abc9c;
                width: 20px;
            }
            QCheckBox {
                color: #2c3e50;
            }
        """
        self.setStyleSheet(light_stylesheet)

    def closeEvent(self, event):
        # Clean up before exiting
        if self.is_recording:
            self.recorder_worker.stop_recording()
        if self.is_playing and self.player_worker:
            self.player_worker.stop_playback()
        self.hotkey_listener.stop_listening()
        event.accept()

# Hotkey Listener Class using pynput's GlobalHotKeys
class KeyboardHotkeyListener:
    def __init__(self, hotkeys, signals):
        """
        hotkeys: Dictionary with keys 'record' and 'play' mapping to their hotkey strings
        signals: Signals object for inter-thread communication
        """
        self.hotkeys = hotkeys
        self.signals = signals
        self.listener = None
        self.thread = None
        self.lock = Lock()

    def create_hotkeys(self):
        """
        Creates a dictionary suitable for pynput's GlobalHotKeys based on current hotkeys
        """
        hotkey_dict = {}
        for action, hotkey in self.hotkeys.items():
            if hotkey:
                # Convert hotkey string to pynput format
                # e.g., "Ctrl+Alt+R" -> "<ctrl>+<alt>+r"
                parts = hotkey.split('+')
                formatted_hotkey = '+'.join(['<' + part.lower() + '>' if part.lower() in ['ctrl', 'alt', 'shift', 'meta'] else part.lower() for part in parts])
                if action == 'record':
                    hotkey_dict[formatted_hotkey] = self.signals.toggle_record.emit
                elif action == 'play':
                    hotkey_dict[formatted_hotkey] = self.signals.toggle_play.emit
        return hotkey_dict

    def start_listening(self):
        with self.lock:
            if self.listener:
                return  # Already listening

            hotkey_dict = self.create_hotkeys()
            if not hotkey_dict:
                return  # No hotkeys defined

            self.listener = GlobalHotKeys(hotkey_dict)
            self.listener.start()

    def stop_listening(self):
        with self.lock:
            if self.listener:
                self.listener.stop()
                self.listener = None

    def update_hotkeys(self, new_hotkeys):
        with self.lock:
            self.hotkeys = new_hotkeys
            self.stop_listening()
            self.start_listening()

    def enable_hotkeys(self):
        self.start_listening()

    def disable_hotkeys(self):
        self.stop_listening()

# Dialog for setting hotkeys
class HotkeyDialog(QDialog):
    def __init__(self, parent=None, current_hotkeys=None):
        super().__init__(parent)
        self.setWindowTitle("Set Hotkeys")
        self.setFixedSize(400, 250)
        self.current_hotkeys = current_hotkeys or {}
        self.new_hotkeys = {}

        layout = QVBoxLayout()

        form_layout = QFormLayout()

        self.record_hotkey_input = QLineEdit()
        self.record_hotkey_input.setPlaceholderText("Press new hotkey")
        self.record_hotkey_input.setText(self.current_hotkeys.get("record", "Ctrl+Alt+R"))
        self.record_hotkey_input.installEventFilter(self)
        form_layout.addRow("Record Hotkey:", self.record_hotkey_input)

        self.play_hotkey_input = QLineEdit()
        self.play_hotkey_input.setPlaceholderText("Press new hotkey")
        self.play_hotkey_input.setText(self.current_hotkeys.get("play", "Ctrl+Alt+P"))
        self.play_hotkey_input.installEventFilter(self)
        form_layout.addRow("Play Hotkey:", self.play_hotkey_input)

        layout.addLayout(form_layout)

        # Instructions
        instruction_label = QLabel("Press the desired key combination for each action.\nUse modifiers like Ctrl, Alt, Shift, Meta.")
        instruction_label.setStyleSheet("font-size: 12px; color: gray;")
        instruction_label.setAlignment(Qt.AlignCenter)
        layout.addWidget(instruction_label)

        # Buttons
        self.buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        self.buttons.accepted.connect(self.accept)
        self.buttons.rejected.connect(self.reject)
        layout.addWidget(self.buttons)

        self.setLayout(layout)

        self.current_input = None
        self.hotkey = ""
        self.keys = []

    def eventFilter(self, source, event):
        if event.type() == QtCore.QEvent.KeyPress and source in [self.record_hotkey_input, self.play_hotkey_input]:
            modifiers = []
            if event.modifiers() & Qt.ControlModifier:
                modifiers.append("Ctrl")
            if event.modifiers() & Qt.AltModifier:
                modifiers.append("Alt")
            if event.modifiers() & Qt.ShiftModifier:
                modifiers.append("Shift")
            if event.modifiers() & Qt.MetaModifier:
                modifiers.append("Meta")

            key = event.key()
            key_name = QKeySequence(key).toString()
            if key_name:
                # Prevent capturing modifier keys alone
                if key in [Qt.Key_Control, Qt.Key_Shift, Qt.Key_Alt, Qt.Key_Meta]:
                    return True
                self.hotkey = "+".join(modifiers + [key_name])
                source.setText(self.hotkey)
                return True
        return super().eventFilter(source, event)

    def get_hotkeys(self):
        return {
            "record": self.record_hotkey_input.text(),
            "play": self.play_hotkey_input.text()
        }

def check_exit():
    QApplication.quit()

def main():
    app = QApplication(sys.argv)
    app.setStyle("Fusion")  # Change style for better aesthetics

    # Initialize and display the main window
    window = MacroRecorderPlayer()
    window.show()

    sys.exit(app.exec_())

if __name__ == "__main__":
    main() do it completly red black designed please more modern design this one is still simple i want all elements to be modern