#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sys
import pkgutil
import importlib
import inspect
import types
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QWidget
from PyQt5.QtCore import Qt, QUrl, QTimer
from PyQt5.QtGui import QGuiApplication
from PyQt5.QtQuick import QQuickView

# OdenGraphQt Fix: Monkey-patch QUndoStack
import OdenGraphQt.base.graph as base_graph
from PyQt5 import QtWidgets
base_graph.QtGui.QUndoStack = QtWidgets.QUndoStack

import OdenGraphQt.base.commands as base_commands
_original_redo = base_commands.NodesRemovedCmd.redo
_original_undo = base_commands.NodesRemovedCmd.undo

def _patched_redo(self):
    try:
        _original_redo(self)
    except TypeError as e:
        if "unexpected type" in str(e) and hasattr(self, 'node'):
            node_ids = []
            if isinstance(self.node, list):
                node_ids = [getattr(n, 'id', str(n)) for n in self.node]
            else:
                node_ids = [getattr(self.node, 'id', str(self.node))]
            self.graph.nodes_deleted.emit(node_ids)
        else:
            raise

def _patched_undo(self):
    try:
        _original_undo(self)
    except TypeError as e:
        if "unexpected type" in str(e) and hasattr(self, 'node'):
            node_ids = []
            if isinstance(self.node, list):
                node_ids = [getattr(n, 'id', str(n)) for n in self.node]
            else:
                node_ids = [getattr(self.node, 'id', str(self.node))]
            self.graph.nodes_deleted.emit(node_ids)
        else:
            raise

base_commands.NodesRemovedCmd.redo = _patched_redo
base_commands.NodesRemovedCmd.undo = _patched_undo

# OdenGraphQt Transparent Viewer
from OdenGraphQt.widgets.viewer import NodeViewer

class TransparentViewer(NodeViewer):
    """A NodeViewer that does not paint anything in drawBackground() -> Fully transparent."""
    def drawBackground(self, painter, rect):
        pass  # Do nothing, ensuring transparency.

# NodeGraph & Node Import Helpers
from OdenGraphQt import NodeGraph, BaseNode

def import_nodes_from_folder(package_name):
    imported_nodes = []
    package = importlib.import_module(package_name)
    for loader, module_name, is_pkg in pkgutil.walk_packages(
            package.__path__, package.__name__ + "."):
        module = importlib.import_module(module_name)
        for name, obj in inspect.getmembers(module, inspect.isclass):
            if issubclass(obj, BaseNode) and obj.__module__ == module.__name__:
                imported_nodes.append(obj)
    return imported_nodes

def make_node_command(graph, node_type):
    def command():
        try:
            graph.create_node(node_type)
        except Exception as e:
            print(f"Error creating node of type {node_type}: {e}")
    return command

# Edit Mode Button
class EditButton(QPushButton):
    """A small, frameless button to toggle edit mode."""
    def __init__(self, parent=None):
        super().__init__("Toggle Edit Mode", parent)
        self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
        # Dark gray background with white text.
        self.setStyleSheet("background-color: #444444; border: 1px solid black; color: white;")
        self.resize(140, 40)

# Main Overlay Window
class MainWindow(QMainWindow):
    """A frameless, transparent overlay with OdenGraphQt nodes & edit mode toggle."""
    def __init__(self):
        super().__init__()

        # Full-screen overlay
        app = QApplication.instance()
        screen_geo = app.primaryScreen().geometry()
        self.setGeometry(screen_geo)

        # Frameless, top-most, fully transparent
        self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
        self.setAttribute(Qt.WA_TranslucentBackground, True)

        # QML Background
        self.qml_view = QQuickView()
        self.qml_view.setSource(QUrl("qml/background_grid.qml"))
        self.qml_view.setFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
        self.qml_view.setClearBeforeRendering(True)
        self.qml_view.setColor(Qt.transparent)
        self.qml_view.show()

        # Save the QML root object for later property sync
        self.qml_root = self.qml_view.rootObject()

        # NodeGraph with TransparentViewer
        self.graph = NodeGraph(viewer=TransparentViewer())
        self.nodeGraphWidget = self.graph.widget
        self.nodeGraphWidget.setStyleSheet("background: transparent; border: none;")

        # Transparent central widget
        central = QWidget(self)
        central.setAttribute(Qt.WA_TranslucentBackground, True)
        self.setCentralWidget(central)

        self.nodeGraphWidget.setParent(central)
        self.nodeGraphWidget.setGeometry(central.rect())

        # Edit Mode Button (Python controlled)
        self.editButton = EditButton(self)
        self.editButton.move(10, 10)
        self.editButton.clicked.connect(self.toggleEditMode)
        self.isEditMode = True  # Set edit mode enabled by default

        # Ensure QML grid overlay is enabled at startup
        if self.qml_root:
            self.qml_root.setProperty("editMode", self.isEditMode)

        # Import custom nodes
        try:
            custom_nodes = import_nodes_from_folder('Nodes')
            for node_class in custom_nodes:
                self.graph.register_node(node_class)

            graph_menu = self.graph.get_context_menu('graph')
            for node_class in custom_nodes:
                node_type = f"{node_class.__identifier__}.{node_class.__name__}"
                node_name = node_class.NODE_NAME
                graph_menu.add_command(
                    f"Add {node_name}",
                    make_node_command(self.graph, node_type)
                )
        except Exception as e:
            print(f"Error setting up custom nodes: {e}")

        # Global update timer
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.global_update)
        self.timer.start(500)
        
        # Timer to ensure the button stays on top (hacky, but effective)
        self.raiseTimer = QTimer(self)
        self.raiseTimer.timeout.connect(self.editButton.raise_)
        self.raiseTimer.start(1000)  # Raise the button every 1 second

        self.show()
        self.nodeGraphWidget.setAttribute(Qt.WA_TransparentForMouseEvents, not self.isEditMode)

    def toggleEditMode(self):
        """Toggle edit mode (pass-through clicks vs interactive)."""
        self.isEditMode = not self.isEditMode
        self.nodeGraphWidget.setAttribute(Qt.WA_TransparentForMouseEvents, not self.isEditMode)
        # Button text remains constant.
        self.editButton.setText("Toggle Edit Mode")
        if self.qml_root:
            self.qml_root.setProperty("editMode", self.isEditMode)

    def global_update(self):
        """Update all nodes periodically."""
        for node in self.graph.all_nodes():
            if hasattr(node, "process_input"):
                node.process_input()

# Entry Point
if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())