import sys

from PyQt5.QtWidgets import (QApplication, QWidget, QVBoxLayout, QMessageBox,
                             QInputDialog, QMenu, QLineEdit)
from PyQt5.QtGui import QIcon
from PyQtAds import QtAds

from dockindockmanager import DockInDockManager
from perspectiveactions import LoadPerspectiveAction, RemovePerspectiveAction


class DockInDockWidget(QWidget):
    def __init__(self, parent, perspectives_manager: 'PerspectivesManager', can_create_new_groups: bool = False, top_level_widget = None):
        super().__init__(parent)
        
        if top_level_widget is not None:
            self.__can_create_new_groups = top_level_widget.can_create_new_groups
        else:
            self.__can_create_new_groups = can_create_new_groups
        self.__top_level_dock_widget = top_level_widget if top_level_widget else self
        self.__perspectives_manager = perspectives_manager        
        self.__new_perspective_default_name: str = ''
        
        layout = QVBoxLayout(self)
        layout.setContentsMargins(0,0,0,0)
        self.__mgr = DockInDockManager(self)
        layout.addWidget(self.__mgr)
        
    def getManager(self) -> 'DockInDockManager':
        return self.__mgr
        
    def getTopLevelDockWidget(self) -> 'DockInDockWidget':
        return self.__top_level_dock_widget
        
    def canCreateNewGroups(self) -> bool:
        return self.__can_create_new_groups
        
    def getPerspectivesManager(self) -> 'PerspectivesManager':
        return self.__perspectives_manager
        
    def addTabWidget(self, widget: QWidget, name: str, after: QtAds.CDockAreaWidget, icon = QIcon()) -> QtAds.CDockAreaWidget:
        for existing in self.getTopLevelDockWidget().getManager().allDockWidgets(True, True):
            if existing[1].objectName() == name:
                QMessageBox.critical(self, "Error", "Name '" + name + "' already in use")
                return
                
        dock_widget = QtAds.CDockWidget(name)
        dock_widget.setWidget(widget)
        dock_widget.setIcon(icon)
        
        # Add the dock widget to the top dock widget area
        return self.__mgr.addDockWidget(QtAds.CenterDockWidgetArea, dock_widget, after)
        
    def isTopLevel(self) -> bool:
        return not self.objectName()
        
    def getGroupNameError(self, group_name: str) -> str:
        if not group_name:
            return "Group must have a non-empty name"
            
        dock_managers = self.__mgr.allManagers(True, True)
        for mgr in dock_managers:
            if mgr.getGroupName() == group_name:
                return "Group name '" + group_name + "' already used"
                
        return ""
        
    def createGroup(self, group_name: str, insert_pos: QtAds.CDockAreaWidget, icon = QIcon()) -> 'DockInDockWidget':
        error = self.getGroupNameError(group_name)
        if error:
            QMessageBox.critical(None, "Error", error)
            return
            
        child = DockInDockWidget(self, self.__top_level_dock_widget, self.__perspectives_manager)
        child.setObjectName(group_name)
        
        dock_widget = QtAds.CDockWidget(group_name)
        dock_widget.setWidget(child)
        dock_widget.setIcon(icon)
        
        insert_pos = self.__mgr.addDockWidget(QtAds.CenterDockWidgetArea, dock_widget, insert_pos)

        return child, insert_pos
        
    def destroyGroup(self, widget_to_remove: 'DockInDockWidget') -> None:
        top_level_widget = widget_to_remove.getTopLevelDockWidget()
        
        if top_level_widget and top_level_widget != widget_to_remove:
            for dock_widget in widget_to_remove.getManager().getWidgetsInGUIOrder(): #don't use allDockWidgets to preserve sub-groups
                MoveDockWidgetAction.move(dock_widget, top_level_widget.getManager())
            assert not widget_to_remove.getManager().allDockWidgets(True, True)

            # find widget's parent:
            for dock_widget in top_level_widget.getManager().allDockWidgets(True, True):
                if dockwidget[1].widget() == widget_to_remove:
                    dockwidget[0].removeDockWidget(dockwidget[1])
                    del dockwidget[1]
                    # delete widgetToRemove; automatically deleted when dockWidget is deleted
                    widget_to_remove = None
                    break

            assert widget_to_remove == None
        else:
            assert False
            
    def attachViewMenu(self, menu: QMenu) -> None:
        menu.aboutToShow.connect(self.autoFillAttachedViewMenu)
        
    def autoFillAttachedViewMenu(self) -> None:
        menu = self.sender()
        
        if menu:
            menu.clear()
            self.setupViewMenu(menu)
        else:
            assert False
            
    def setupViewMenu(self, menu):
        dock_managers = self.__mgr.allManagers(True, True)
        
        has_perspectives_menu = False
        if self.getTopLevelDockWidget() == self:
            has_perspectives_menu = (self.__perspectives_manager != None)
        else:
            assert False
            
        organize = menu
        if has_perspectives_menu:
            organize = menu.addMenu("Organize")
            
        self.setupMenu(organize, dock_managers)
        
        if has_perspectives_menu:
            perspectives = menu.addMenu("Perspectives")
            self.fillPerspectivesMenu(perspectives)
            
    def setupMenu(self, menu: QMenu, move_to: 'list[DockInDockManager]') -> None:
        self.__mgr.fillViewMenu(menu, move_to)
        menu.addSeparator()
        move_menu = menu.addMenu("Move")
        self.__mgr.fillMoveMenu(move_menu, move_to)
            
    def fillPerspectivesMenu(self, menu: QMenu):
        menu.addAction("Create perspective...", self.createPerspective)
        perspectives_names = []
        if self.__perspectives_manager:
            perspectives_names = self.__perspectives_manager.perspectiveNames()
            
        if perspectives_names:
            load = menu.addMenu("Load perspective")
            for name in perspectives_names:
                load.addAction(LoadPerspectiveAction(load, name, self))
            remove = menu.addMenu("Remove perspective")
            for name in perspectives_names:
                remove.addAction(RemovePerspectiveAction(remove, name, self))
                
    def setNewPerspectiveDefaultName(default_name: str) -> None:
        self.__new_perspective_default_name = default_name
        
    def createPerspective(self) -> None:
        if not self.__perspectives_manager:
            return
            
        name = self.__new_perspective_default_name
        if self.__new_perspective_default_name:
            index = 2
            while name in self.__perspectives_manager.perspectiveNames():
                name = f"{self.__new_perspective_default_name}({index})"
                index += 1
                
        while True:
            name, ok = QInputDialog.getText(None, "Create perspective", "Enter perspective name", QLineEdit.Normal, name)
            if ok:
                if not name:
                    QMessageBox.critical(None, "Error", "Perspective name cannot be empty")
                    continue
                elif name in self.__perspectives_manager.perspectiveNames():
                    if QMessageBox.critical(None, "Error", f"Perspective '{name}' already exists, overwrite it?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) == QMessageBox.No:
                        continue
                    
                self.__perspectives_manager.addPerspective(name, self)
                break
            else:
                break
                
    def dumpStatus(self, echo: callable = print, widget: QtAds.CDockWidget = None, tab: str = '', suffix: str = '') -> str:
        if widget is not None:
            as_mgr = DockInDockManager.dockInAManager(widget)
            if as_mgr:
                as_mgr.parent().dumpStatus(tab=tab)
            else:
                echo(tab + widget.objectName() + suffix)
        else:
            echo(tab + "Group: " + self.getManager().getGroupName())
            tab += "   "
            visible_widgets = set()
            for widget in self.getManager().getWidgetsInGUIOrder():
                visible_widgets.add(widget)
                self.dumpStatus(widget=widget, tab=tab)
                
            for closed in self.getManager().dockWidgetsMap().values():
                if not closed in visible_widgets:
                    self.dumpStatus(widget=closed, tab=tab, suffix=" (closed)")