import typing

from qtpy import QtCore as QC
from qtpy import QtWidgets as QW
from qtpy import QtGui as QG

from hydrus.core import HydrusConstants as HC
from hydrus.core import HydrusData
from hydrus.core import HydrusGlobals as HG
from hydrus.core import HydrusProfiling
from hydrus.core import HydrusText

from hydrus.client import ClientGlobals as CG

from hydrus.client.gui import QtPorting as QP

def AppendMenu( menu, submenu, label ):
    
    label = SanitiseLabel( label )
    
    submenu.setTitle( label )
    menu_action = menu.addMenu( submenu )
    
    return menu_action
    

def AppendMenuIconItem( menu: QW.QMenu, label: str, description: str, icon: QG.QIcon, callable, *args, **kwargs ):
    
    menu_item = QW.QAction( menu )
    
    SetMenuTexts( menu_item, label, description )
    
    menu_item.setIcon( icon )
    
    menu.addAction(menu_item)
    
    BindMenuItem( menu_item, callable, *args, **kwargs )
    
    return menu_item
    

def AppendMenuBitmapItem( menu, label, description, bitmap, callable, *args, **kwargs ):
    
    return AppendMenuIconItem(menu, label, description, QG.QIcon( bitmap ), callable, *args, **kwargs)
    

def AppendMenuCheckItem( menu, label, description, initial_value, callable, *args, **kwargs ):
    
    menu_item = QW.QAction( menu )
    
    SetMenuTexts( menu_item, label, description )
    
    menu_item.setCheckable( True )
    menu_item.setChecked( initial_value )
    
    menu.addAction( menu_item )
    
    BindMenuItem( menu_item, callable, *args, **kwargs )
    
    return menu_item
    
def AppendMenuItem( menu, label, description, callable, *args, role: QW.QAction.MenuRole = None, **kwargs ):
    
    menu_item = QW.QAction( menu )
    
    if HC.PLATFORM_MACOS:
        
        menu_item.setMenuRole( role if role is not None else QW.QAction.MenuRole.NoRole )
    
    
    SetMenuTexts( menu_item, label, description )
    
    menu.addAction( menu_item )
    
    BindMenuItem( menu_item, callable, *args, **kwargs )
    
    return menu_item
    

def AppendMenuLabel( menu, label, description = '', copy_text = '' ):
    
    if description == label:
        
        description = ''
        
    
    if copy_text == '':
        
        copy_text = label
        
    
    if description == '':
        
        description = f'copy "{copy_text}" to clipboard'
        
    
    menu_item = QW.QAction( menu )
    
    SetMenuTexts( menu_item, label, description )
    
    menu.addAction( menu_item )
    
    BindMenuItem( menu_item, CG.client_controller.pub, 'clipboard', 'text', copy_text )
    
    return menu_item
    
def AppendMenuOrItem( menu, submenu_name, menu_tuples, sort_tuples = True ):
    
    if sort_tuples:
        
        try:
            
            menu_tuples = sorted( menu_tuples )
            
        except:
            
            pass
            
        
    
    if len( menu_tuples ) == 1:
        
        submenu = menu
        
        item_prefix = '{} '.format( submenu_name )
        
    else:
        
        submenu = GenerateMenu( menu )
        
        AppendMenu( menu, submenu, submenu_name )
        
        item_prefix = ''
        
    
    for ( label, description, call ) in menu_tuples:
        
        label = '{}{}'.format( item_prefix, label )
        
        AppendMenuItem( submenu, label, description, call )
        
    
def AppendSeparator( menu ):
    
    num_items = len( menu.actions() )
    
    if num_items > 0:
        
        last_item = menu.actions()[-1]
        
        # got this once, who knows what happened, so we test for QAction now
        # 'PySide2.QtGui.QStandardItem' object has no attribute 'isSeparator'
        last_item_is_separator = isinstance( last_item, QW.QAction ) and last_item.isSeparator()
        
        if not last_item_is_separator:
            
            menu.addSeparator()
            
        
    

def BindMenuItem( menu_item, callable, *args, **kwargs ):
    
    event_callable = GetEventCallable( callable, *args, **kwargs )
    
    menu_item.triggered.connect( event_callable )
    

def DestroyMenu( menu ):
    
    if menu is None:
        
        return
        
    
    if QP.isValid( menu ):
        
        menu.deleteLater()
        
    


class StatusBarRedirectFilter( QC.QObject ):
    
    def eventFilter( self, watched, event ):
        
        try:
            
            if event.type() == QC.QEvent.StatusTip:
                
                QW.QApplication.instance().sendEvent( CG.client_controller.gui, event )
                
                return True
                
            
        except Exception as e:
            
            HydrusData.ShowException( e )
            
            return True
            
        
        return False
        
    

def GenerateMenu( parent: QW.QWidget ) -> QW.QMenu:
    
    menu = QW.QMenu( parent )
    
    menu.setToolTipsVisible( True )
    
    menu.installEventFilter( StatusBarRedirectFilter( menu ) )
    
    return menu
    

def GetEventCallable( callable, *args, **kwargs ):
    
    def event_callable( checked_state ):
        
        if HG.profile_mode:
            
            summary = 'Profiling menu: ' + repr( callable )
            
            HydrusProfiling.Profile( summary, 'callable( *args, **kwargs )', globals(), locals(), min_duration_ms = HG.menu_profile_min_job_time_ms )
            
        else:
            
            callable( *args, **kwargs )
            
        
    
    return event_callable
    
def SanitiseLabel( label: str ) -> str:
    
    if label == '':
        
        label = '-invalid label-'
        
    
    return label.replace( '&', '&&' )
    

def SetMenuItemLabel( menu_item: QW.QAction, label: str ):
    
    label = SanitiseLabel( label )
    
    menu_item.setText( label )
    

def SetMenuTexts( menu_item: QW.QAction, label: str, description: str ):
    
    label = SanitiseLabel( label )
    
    elided_label = HydrusText.ElideText( label, 128, elide_center = True )
    
    menu_item.setText( elided_label )
    
    menu_item.setStatusTip( description )
    
    if label != elided_label:
        
        menu_item.setToolTip( label )
        
    elif description != label and description != '':
        
        menu_item.setToolTip( description )
        
    
    menu_item.setWhatsThis( description )
    

def SetMenuTitle( menu: QW.QMenu, label: str ):
    
    label = SanitiseLabel( label )
    
    menu.setTitle( label )
    

def SpamItems( menu: QW.QMenu, labels_descriptions_and_calls: typing.Collection[ typing.Tuple[ str, str, typing.Callable ] ], max_allowed: int ):
    
    if len( labels_descriptions_and_calls ) > max_allowed:
        
        num_to_show = max_allowed - 1
        
    else:
        
        num_to_show = max_allowed
        
    
    for ( label, description, call ) in list( labels_descriptions_and_calls )[:num_to_show]:
        
        AppendMenuItem( menu, label, description, call )
        
    
    if len( labels_descriptions_and_calls ) > num_to_show:
        
        # maybe one day this becomes a thing that extends the menu to show them all
        
        AppendMenuLabel( menu, '{} more...'.format( len( labels_descriptions_and_calls ) - num_to_show ) )
        
    

def SpamLabels( menu: QW.QMenu, labels_and_copy_texts: typing.Collection[ typing.Tuple[ str, str ] ], max_allowed: int ):
    
    if len( labels_and_copy_texts ) > max_allowed:
        
        num_to_show = max_allowed - 1
        
    else:
        
        num_to_show = max_allowed
        
    
    for ( label, copy_text ) in list( labels_and_copy_texts )[:num_to_show]:
        
        AppendMenuLabel( menu, label, copy_text = copy_text )
        
    
    if len( labels_and_copy_texts ) > num_to_show:
        
        # maybe one day this becomes a thing that extends the menu to show them all
        
        AppendMenuLabel( menu, '{} more...'.format( len( labels_and_copy_texts ) - num_to_show ) )
        
    
