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, QPropertyAnimation, QEasingCurve
)
from PyQt5.QtGui import QIcon, QFont, QColor, QKeySequence, QPixmap, QPainter, QBrush
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, QListWidgetItem, QMenu, QGroupBox,
    QGraphicsDropShadowEffect, QStackedLayout, QMenuBar, QStatusBar
)
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()
        self.mouse_listener = None
        self.keyboard_listener = None

    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 index, event in enumerate(self.events):
                    with self.lock:
                        if not self.is_playing:
                            break
                    time.sleep(event.timestamp)
                    self.signals.playback_finished.emit()

                    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.listener = 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()
        }


# Dialog for managing multiple macros
class MacroManagerDialog(QDialog):
    def __init__(self, parent=None, macros=None):
        super().__init__(parent)
        self.setWindowTitle("Manage Macros")
        self.setFixedSize(400, 300)
        self.macros = macros or {}

        layout = QVBoxLayout()

        # List Widget
        self.list_widget = QListWidget()
        self.list_widget.addItems(self.macros.keys())
        self.list_widget.setSelectionMode(QAbstractItemView.SingleSelection)
        self.list_widget.setStyleSheet("""
            QListWidget {
                background-color: #1e1e1e;
                border: none;
                padding: 10px;
                border-radius: 8px;
                font-size: 14px;
            }
            QListWidget::item:selected {
                background-color: #ff3b30;
                color: #ffffff;
            }
        """)
        layout.addWidget(self.list_widget)

        # Buttons
        button_layout = QHBoxLayout()
        self.rename_button = QPushButton("Rename")
        self.rename_button.setStyleSheet("""
            QPushButton {
                background-color: #2c2c2c;
                color: #ffffff;
                border: none;
                padding: 8px 16px;
                border-radius: 8px;
                transition: background-color 0.3s;
            }
            QPushButton:hover {
                background-color: #ff3b30;
            }
            QPushButton:disabled {
                background-color: #555555;
                color: #aaaaaa;
            }
        """)
        self.rename_button.setEnabled(False)
        self.delete_button = QPushButton("Delete")
        self.delete_button.setStyleSheet("""
            QPushButton {
                background-color: #2c2c2c;
                color: #ffffff;
                border: none;
                padding: 8px 16px;
                border-radius: 8px;
                transition: background-color 0.3s;
            }
            QPushButton:hover {
                background-color: #ff3b30;
            }
            QPushButton:disabled {
                background-color: #555555;
                color: #aaaaaa;
            }
        """)
        self.delete_button.setEnabled(False)
        button_layout.addWidget(self.rename_button)
        button_layout.addWidget(self.delete_button)
        layout.addLayout(button_layout)

        # Connect signals
        self.list_widget.itemSelectionChanged.connect(self.update_buttons)
        self.rename_button.clicked.connect(self.rename_macro)
        self.delete_button.clicked.connect(self.delete_macro)

        # Add Stretch
        layout.addStretch()

        self.setLayout(layout)

    def update_buttons(self):
        has_selection = bool(self.list_widget.selectedItems())
        self.rename_button.setEnabled(has_selection)
        self.delete_button.setEnabled(has_selection)

    def rename_macro(self):
        selected_item = self.list_widget.selectedItems()[0]
        current_name = selected_item.text()
        new_name, ok = QtWidgets.QInputDialog.getText(self, "Rename Macro", "New name:", text=current_name)
        if ok and new_name:
            if new_name in self.macros:
                QMessageBox.warning(self, "Rename Macro", "A macro with this name already exists.")
                return
            self.macros[new_name] = self.macros.pop(current_name)
            selected_item.setText(new_name)

    def delete_macro(self):
        selected_item = self.list_widget.selectedItems()[0]
        macro_name = selected_item.text()
        reply = QMessageBox.question(self, "Delete Macro",
                                     f"Are you sure you want to delete the macro '{macro_name}'?",
                                     QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
        if reply == QMessageBox.Yes:
            del self.macros[macro_name]
            self.list_widget.takeItem(self.list_widget.row(selected_item))


# Main Application Window
class MacroRecorderPlayer(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Advanced Macro Recorder")
        self.setGeometry(100, 100, 1200, 800)  # Smaller window size
        self.setWindowIcon(QIcon.fromTheme("media-record"))

        self.macros = {}  # Dictionary to hold multiple macros
        self.current_macro = None
        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(200)
        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(20)

        # Control Buttons with Icons and Animations
        button_style = """
            QPushButton {
                background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #2c2c2c, stop:1 #1a1a1a);
                border: none;
                border-radius: 12px;
                padding: 12px;
                font-size: 14px;
                text-align: left;
                margin-bottom: 15px;
                color: #ffffff;
                transition: background 0.3s, transform 0.1s;
            }
            QPushButton:hover {
                background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #ff3b30, stop:1 #cc2900);
                transform: scale(1.02);
            }
            QPushButton:pressed {
                background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #cc2900, stop:1 #a70000);
                transform: scale(0.98);
            }
            QPushButton:disabled {
                background: #555555;
                color: #aaaaaa;
            }
        """

        self.record_button = QPushButton("  Record")
        self.record_button.setIcon(QIcon.fromTheme("media-record"))
        self.record_button.setStyleSheet(button_style)
        self.record_button.setIconSize(QSize(24, 24))
        self.record_button.setToolTip("Start Recording Macro")
        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(button_style)
        self.play_button.setIconSize(QSize(24, 24))
        self.play_button.setEnabled(False)
        self.play_button.setToolTip("Play Current Macro")
        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(button_style)
        self.save_button.setIconSize(QSize(24, 24))
        self.save_button.setEnabled(False)
        self.save_button.setToolTip("Save Current Macro")
        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(button_style)
        self.load_button.setIconSize(QSize(24, 24))
        self.load_button.setEnabled(True)
        self.load_button.setToolTip("Load a Macro")
        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(button_style)
        self.clear_button.setIconSize(QSize(24, 24))
        self.clear_button.setEnabled(False)
        self.clear_button.setToolTip("Clear Current Macro")
        self.clear_button.clicked.connect(self.clear_events)
        sidebar_layout.addWidget(self.clear_button)

        # Macro Management Button
        self.manage_macros_button = QPushButton("  Manage Macros")
        self.manage_macros_button.setIcon(QIcon.fromTheme("view-list-icons"))
        self.manage_macros_button.setStyleSheet(button_style)
        self.manage_macros_button.setIconSize(QSize(24, 24))
        self.manage_macros_button.setToolTip("Manage Multiple Macros")
        self.manage_macros_button.clicked.connect(self.manage_macros)
        sidebar_layout.addWidget(self.manage_macros_button)

        # Spacer
        sidebar_layout.addStretch()

        # Settings Button
        self.settings_button = QPushButton("  Settings")
        self.settings_button.setIcon(QIcon.fromTheme("preferences-system"))
        self.settings_button.setStyleSheet(button_style)
        self.settings_button.setIconSize(QSize(24, 24))
        self.settings_button.setToolTip("Settings")
        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 = QStatusBar()
        self.setStatusBar(self.status_bar)
        self.status_bar_label = QLabel("Status: Idle")
        self.status_bar.addPermanentWidget(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()
        search_label_style = "QLabel { font-size: 14px; }"
        self.search_label = QLabel("Search:")
        self.search_label.setStyleSheet(search_label_style)
        self.search_input = QLineEdit()
        self.search_input.setPlaceholderText("Search Events...")
        self.search_input.setStyleSheet("""
            QLineEdit {
                padding: 8px;
                border-radius: 8px;
                border: 1px solid #444444;
                background-color: #2c2c2c;
                color: #ffffff;
                font-size: 14px;
            }
            QLineEdit::focus {
                border: 1px solid #ff3b30;
            }
        """)
        self.search_input.textChanged.connect(self.search_events)
        search_layout.addWidget(self.search_label)
        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)
        self.tabs.setStyleSheet("""
            QTabWidget::pane {
                border: none;
            }
            QTabBar::tab {
                background: #2c2c2c;
                color: #ffffff;
                padding: 10px;
                border: 1px solid #444444;
                border-bottom: none;
                border-top-left-radius: 12px;
                border-top-right-radius: 12px;
            }
            QTabBar::tab:selected {
                background: #ff3b30;
                color: #ffffff;
            }
        """)

        # 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.mouse_move_tab.setStyleSheet("""
            QListWidget {
                background-color: #1e1e1e;
                border: none;
                padding: 10px;
                border-radius: 8px;
                font-size: 14px;
            }
            QListWidget::item:selected {
                background-color: #ff3b30;
                color: #ffffff;
            }
        """)
        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.mouse_click_tab.setStyleSheet("""
            QListWidget {
                background-color: #1e1e1e;
                border: none;
                padding: 10px;
                border-radius: 8px;
                font-size: 14px;
            }
            QListWidget::item:selected {
                background-color: #ff3b30;
                color: #ffffff;
            }
        """)
        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.mouse_scroll_tab.setStyleSheet("""
            QListWidget {
                background-color: #1e1e1e;
                border: none;
                padding: 10px;
                border-radius: 8px;
                font-size: 14px;
            }
            QListWidget::item:selected {
                background-color: #ff3b30;
                color: #ffffff;
            }
        """)
        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.keyboard_tab.setStyleSheet("""
            QListWidget {
                background-color: #1e1e1e;
                border: none;
                padding: 10px;
                border-radius: 8px;
                font-size: 14px;
            }
            QListWidget::item:selected {
                background-color: #ff3b30;
                color: #ffffff;
            }
        """)
        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)
        self.details_panel.setStyleSheet("""
            QTextEdit {
                background-color: #2c2c2c;
                color: #ffffff;
                border: 1px solid #444444;
                border-radius: 8px;
                padding: 10px;
                font-size: 14px;
            }
        """)
        splitter.addWidget(self.details_panel)

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

        content_layout.addWidget(splitter)

        # Progress Bar
        self.progress_bar = QProgressBar()
        self.progress_bar.setValue(0)
        self.progress_bar.setVisible(False)
        self.progress_bar.setStyleSheet("""
            QProgressBar {
                border: 1px solid #ff3b30;
                border-radius: 12px;
                text-align: center;
                color: #ffffff;
                font-size: 14px;
                height: 20px;
            }
            QProgressBar::chunk {
                background-color: #ff3b30;
                border-radius: 12px;
            }
        """)
        content_layout.addWidget(self.progress_bar)

        # Continuous Playback Checkbox
        self.continuous_playback_checkbox = QCheckBox("Enable Continuous Playback")
        self.continuous_playback_checkbox.setStyleSheet("QCheckBox { color: #ffffff; font-size: 14px; }")
        content_layout.addWidget(self.continuous_playback_checkbox)

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

        # Set the application's overall style
        self.apply_modern_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(200)
            self.toggle_sidebar_btn.setIcon(QIcon.fromTheme("go-next"))
            # Restore button texts with animation
            for button in [self.record_button, self.play_button, self.save_button, self.load_button, self.clear_button,
                          self.settings_button, self.manage_macros_button]:
                if not button.text():
                    animation = QPropertyAnimation(button, b"minimumWidth")
                    animation.setDuration(300)
                    animation.setStartValue(0)
                    animation.setEndValue(200)
                    animation.setEasingCurve(QEasingCurve.InOutQuart)
                    animation.start()
                    button.setText(button.toolTip())
        else:
            self.sidebar.setFixedWidth(60)
            self.toggle_sidebar_btn.setIcon(QIcon.fromTheme("go-previous"))
            # Hide button texts with animation
            for button in [self.record_button, self.play_button, self.save_button, self.load_button, self.clear_button,
                          self.settings_button, self.manage_macros_button]:
                if button.text():
                    animation = QPropertyAnimation(button, b"minimumWidth")
                    animation.setDuration(300)
                    animation.setStartValue(200)
                    animation.setEndValue(0)
                    animation.setEasingCurve(QEasingCurve.InOutQuart)
                    animation.start()
                    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):
        # Select or create a new macro
        if not self.current_macro:
            macro_name, ok = QtWidgets.QInputDialog.getText(self, "New Macro", "Enter macro name:")
            if ok and macro_name:
                if macro_name in self.macros:
                    QMessageBox.warning(self, "Duplicate Macro", "A macro with this name already exists.")
                    return
                self.macros[macro_name] = []
                self.current_macro = macro_name
            else:
                return

        self.is_recording = True
        self.events = []
        self.clear_event_lists()
        self.record_button.setText("  Stop")
        self.record_button.setToolTip("Stop Recording Macro")
        self.record_button.setEnabled(True)
        self.play_button.setEnabled(False)
        self.save_button.setEnabled(False)
        self.clear_button.setEnabled(False)
        self.manage_macros_button.setEnabled(False)
        self.status_bar_label.setText(f"Status: Recording '{self.current_macro}'...")
        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.setToolTip("Start Recording Macro")
        self.play_button.setEnabled(True)
        self.save_button.setEnabled(True)
        self.clear_button.setEnabled(True)
        self.manage_macros_button.setEnabled(True)
        self.status_bar_label.setText(f"Status: Idle")
        self.hotkey_listener.enable_hotkeys()

    def on_recording_finished(self):
        self.is_recording = False
        self.record_button.setText("  Record")
        self.record_button.setToolTip("Start Recording Macro")
        self.play_button.setEnabled(True)
        self.save_button.setEnabled(True)
        self.clear_button.setEnabled(True)
        self.manage_macros_button.setEnabled(True)
        self.status_bar_label.setText(f"Status: Idle")
        self.hotkey_listener.enable_hotkeys()

    def on_event_recorded(self, event):
        self.events.append(event)
        self.macros[self.current_macro].append(event)
        self.add_event_to_list(event)

    def add_event_to_list(self, event):
        if event.event_type == 'mouse_move':
            text = f"Position: {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"
            button = event.details['button']
            text = f"{action} {button} at {event.details['position']} | Delay: {event.timestamp:.2f}s"
            self.mouse_click_tab.addItem(text)
        elif event.event_type == 'mouse_scroll':
            dx = event.details['dx']
            dy = event.details['dy']
            position = event.details['position']
            text = f"Scroll ({dx}, {dy}) at {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.upper() if key.char else str(key).capitalize()
        else:
            return str(key).replace("Key.", "").capitalize()

    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.current_macro or not self.macros.get(self.current_macro):
            QMessageBox.warning(self, "Play Macro", "No macro selected or macro is empty.")
            return

        self.events = self.macros[self.current_macro]
        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.manage_macros_button.setEnabled(False)
        self.status_bar_label.setText(f"Status: Playing '{self.current_macro}'...")
        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.manage_macros_button.setEnabled(True)
        self.progress_bar.setVisible(False)
        self.status_bar_label.setText("Status: Idle")
        self.hotkey_listener.enable_hotkeys()
        QMessageBox.information(self, "Playback", f"Macro '{self.current_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.manage_macros_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.current_macro or not self.macros.get(self.current_macro):
            QMessageBox.warning(self, "Save Macro", "No macro selected or macro is empty.")
            return

        options = QFileDialog.Options()
        file_path, _ = QFileDialog.getSaveFileName(
            self, "Save Macro", f"{self.current_macro}.macro", "Macro Files (*.macro);;All Files (*)", options=options
        )
        if file_path:
            try:
                with open(file_path, 'wb') as f:
                    pickle.dump(self.macros[self.current_macro], f)
                QMessageBox.information(self, "Save Macro",
                                        f"Macro '{self.current_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_paths, _ = QFileDialog.getOpenFileNames(
            self, "Load Macro", "", "Macro Files (*.macro);;All Files (*)", options=options
        )
        if file_paths:
            for file_path in file_paths:
                try:
                    with open(file_path, 'rb') as f:
                        events = pickle.load(f)
                    macro_name = file_path.split("/")[-1].split(".macro")[0]
                    if macro_name in self.macros:
                        QMessageBox.warning(self, "Load Macro",
                                            f"A macro named '{macro_name}' already exists. Skipping.")
                        continue
                    self.macros[macro_name] = events
                    QMessageBox.information(self, "Load Macro",
                                            f"Macro '{macro_name}' loaded successfully.")
                except Exception as e:
                    QMessageBox.critical(self, "Load Macro",
                                         f"Failed to load macro from {file_path}:\n{e}")

    def manage_macros(self):
        if not self.macros:
            QMessageBox.information(self, "Manage Macros", "No macros available to manage.")
            return
        dialog = MacroManagerDialog(self, macros=self.macros)
        dialog.exec_()
        # Update current macro if deleted
        if self.current_macro not in self.macros:
            self.current_macro = None
            self.clear_event_lists()
            self.play_button.setEnabled(False)
            self.save_button.setEnabled(False)
            self.clear_button.setEnabled(False)
            QMessageBox.information(self, "Manage Macros", "Current macro has been deleted.")

    def clear_events(self):
        if self.current_macro:
            reply = QMessageBox.question(self, "Clear Macro",
                                         f"Are you sure you want to clear all events from macro '{self.current_macro}'?",
                                         QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
            if reply == QMessageBox.Yes:
                self.macros[self.current_macro] = []
                self.clear_event_lists()
                self.play_button.setEnabled(False)
                self.save_button.setEnabled(False)
                self.clear_button.setEnabled(False)
        else:
            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 apply_modern_theme(self):
        modern_stylesheet = """
            /* Modern Red-Black Dark Theme with Advanced Styling */
            QWidget {
                background-color: #121212;
                color: #ffffff;
                font-family: 'Roboto', sans-serif;
            }

            QPushButton {
                background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #2c2c2c, stop:1 #1a1a1a);
                border: none;
                border-radius: 12px;
                padding: 12px;
                font-size: 14px;
                text-align: left;
                transition: background 0.3s, transform 0.1s;
            }

            QPushButton:hover {
                background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #ff3b30, stop:1 #cc2900);
                transform: scale(1.02);
            }

            QPushButton:pressed {
                background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #cc2900, stop:1 #a70000);
                transform: scale(0.98);
            }

            QPushButton:disabled {
                background: #555555;
                color: #aaaaaa;
            }

            QLineEdit, QTextEdit {
                background-color: #1e1e1e;
                border: 1px solid #444444;
                border-radius: 8px;
                padding: 10px;
                color: #ffffff;
                font-size: 14px;
            }

            QLineEdit::focus, QTextEdit::focus {
                border: 1px solid #ff3b30;
            }

            QListWidget {
                background-color: #1e1e1e;
                border: none;
                padding: 10px;
                border-radius: 8px;
                font-size: 14px;
            }

            QListWidget::item:selected {
                background-color: #ff3b30;
                color: #ffffff;
            }

            QTabWidget::pane {
                border: none;
            }

            QTabBar::tab {
                background: #2c2c2c;
                color: #ffffff;
                padding: 10px;
                border: 1px solid #444444;
                border-bottom: none;
                border-top-left-radius: 12px;
                border-top-right-radius: 12px;
            }

            QTabBar::tab:selected {
                background: #ff3b30;
                color: #ffffff;
            }

            QProgressBar {
                border: 1px solid #ff3b30;
                border-radius: 12px;
                text-align: center;
                color: #ffffff;
                font-size: 14px;
                height: 20px;
            }

            QProgressBar::chunk {
                background-color: #ff3b30;
                border-radius: 12px;
            }

            QCheckBox {
                color: #ffffff;
                font-size: 14px;
            }

            QLabel {
                font-size: 14px;
            }

            QToolButton {
                background-color: #1a1a1a;
                color: #ffffff;
                border: none;
                padding: 12px;
                border-radius: 12px;
                transition: background 0.3s, transform 0.1s;
            }

            QToolButton:hover {
                background-color: #ff3b30;
                transform: scale(1.02);
            }

            QToolButton:pressed {
                background-color: #cc2900;
                transform: scale(0.98);
            }

            /* Scrollbar Styles */
            QScrollBar:vertical {
                border: none;
                background: #1e1e1e;
                width: 12px;
                margin: 0px 0px 0px 0px;
            }

            QScrollBar::handle:vertical {
                background: #444444;
                min-height: 20px;
                border-radius: 6px;
            }

            QScrollBar::handle:vertical:hover {
                background: #ff3b30;
            }

            QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {
                background: none;
                height: 0px;
            }

            /* Tooltip Styles */
            QToolTip {
                background-color: #1e1e1e;
                color: #ffffff;
                border: 1px solid #ff3b30;
                padding: 5px;
                border-radius: 8px;
                font-size: 12px;
            }
        """
        self.setStyleSheet(modern_stylesheet)


def check_exit():
    QApplication.quit()


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

    # Set a modern font
    font = QFont("Roboto", 10)
    app.setFont(font)

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

    sys.exit(app.exec_())


if __name__ == "__main__":
    main() do it alot more attracitrve designed alot more code please all implemented ofc and things improved alot also logics improved