import typing

from qtpy import QtWidgets as QW

from hydrus.core import HydrusConstants as HC
from hydrus.core import HydrusExceptions
from hydrus.core import HydrusGlobals as HG

from hydrus.client import ClientApplicationCommand as CAC
from hydrus.client import ClientConstants as CC
from hydrus.client import ClientGlobals as CG
from hydrus.client.gui import ClientGUIDialogsMessage
from hydrus.client.gui import ClientGUIScrolledPanels
from hydrus.client.gui import ClientGUIShortcuts
from hydrus.client.gui import QtPorting as QP
from hydrus.client.gui.search import ClientGUIACDropdown
from hydrus.client.gui.widgets import ClientGUICommon
from hydrus.client.media import ClientMediaFileFilter

class LocalFilesSubPanel( QW.QWidget ):
    
    def __init__( self, parent: QW.QWidget ):
        
        QW.QWidget.__init__( self, parent )
        
        self._add_or_move_action = ClientGUICommon.BetterChoice( self )
        
        self._add_or_move_action.addItem( 'add to', HC.CONTENT_UPDATE_ADD )
        self._add_or_move_action.addItem( 'move to', HC.CONTENT_UPDATE_MOVE )
        
        self._add_or_move_action.SetValue( HC.CONTENT_UPDATE_ADD )
        
        self._service_keys = ClientGUICommon.BetterChoice( self )
        
        #
        
        services = CG.client_controller.services_manager.GetServices( ( HC.LOCAL_FILE_DOMAIN, ) )
        
        for service in services:
            
            service_name = service.GetName()
            service_key = service.GetServiceKey()
            
            self._service_keys.addItem( service_name, service_key )
            
        
        #
        
        vbox = QP.VBoxLayout()
        
        ratings_numerical_hbox = QP.HBoxLayout()
        
        QP.AddToLayout( vbox, self._add_or_move_action, CC.FLAGS_EXPAND_PERPENDICULAR )
        QP.AddToLayout( vbox, self._service_keys, CC.FLAGS_EXPAND_PERPENDICULAR )
        
        self.setLayout( vbox )
        
    
    def GetValue( self ):
        
        service_key = self._service_keys.GetValue()
        
        if service_key is None:
            
            raise HydrusExceptions.VetoException( 'Please select a service!' )
            
        
        action = self._add_or_move_action.GetValue()
        
        value = None
        
        return CAC.ApplicationCommand( CAC.APPLICATION_COMMAND_TYPE_CONTENT, ( service_key, HC.CONTENT_TYPE_FILES, action, value ) )
        
    
    def SetValue( self, action: int, service_key: bytes ):
        
        self._add_or_move_action.SetValue( action )
        
        self._service_keys.SetValue( service_key )
        
    
class RatingLikeSubPanel( QW.QWidget ):
    
    def __init__( self, parent: QW.QWidget ):
        
        QW.QWidget.__init__( self, parent )
        
        self._flip_or_set_action = ClientGUICommon.BetterChoice( self )
        
        self._flip_or_set_action.addItem( 'set', HC.CONTENT_UPDATE_SET )
        self._flip_or_set_action.addItem( 'flip on and off', HC.CONTENT_UPDATE_FLIP )
        
        self._flip_or_set_action.SetValue( HC.CONTENT_UPDATE_SET )
        
        self._service_keys = ClientGUICommon.BetterChoice( self )
        self._ratings_like_like = QW.QRadioButton( 'like', self )
        self._ratings_like_dislike = QW.QRadioButton( 'dislike', self )
        self._ratings_like_remove = QW.QRadioButton( 'remove rating', self )
        
        #
        
        services = CG.client_controller.services_manager.GetServices( ( HC.LOCAL_RATING_LIKE, ) )
        
        if len( services ) == 0:
            
            self._service_keys.addItem( 'you have no like/dislike rating services', None )
            
        else:
            
            for service in services:
                
                service_name = service.GetName()
                service_key = service.GetServiceKey()
                
                self._service_keys.addItem( service_name, service_key )
                
            
        
        self._ratings_like_like.setChecked( True )
        
        #
        
        vbox = QP.VBoxLayout()
        
        ratings_like_hbox = QP.HBoxLayout()
        
        QP.AddToLayout( ratings_like_hbox, self._service_keys, CC.FLAGS_EXPAND_BOTH_WAYS )
        QP.AddToLayout( ratings_like_hbox, self._ratings_like_like, CC.FLAGS_CENTER_PERPENDICULAR )
        QP.AddToLayout( ratings_like_hbox, self._ratings_like_dislike, CC.FLAGS_CENTER_PERPENDICULAR )
        QP.AddToLayout( ratings_like_hbox, self._ratings_like_remove, CC.FLAGS_CENTER_PERPENDICULAR )
        
        QP.AddToLayout( vbox, self._flip_or_set_action, CC.FLAGS_EXPAND_PERPENDICULAR )
        QP.AddToLayout( vbox, ratings_like_hbox, CC.FLAGS_EXPAND_PERPENDICULAR )
        
        self.setLayout( vbox )
        
        self._ratings_like_remove.toggled.connect( self._UpdateFlipAllowed )
        
    
    def _UpdateFlipAllowed( self ):
        
        if self._ratings_like_remove.isChecked():
            
            self._flip_or_set_action.SetValue( HC.CONTENT_UPDATE_SET )
            self._flip_or_set_action.setEnabled( False )
            
        else:
            
            self._flip_or_set_action.setEnabled( True )
            
        
    
    def GetValue( self ):
        
        service_key = self._service_keys.GetValue()
        
        if service_key is None:
            
            raise HydrusExceptions.VetoException( 'Please select a rating service!' )
            
        
        action = self._flip_or_set_action.GetValue()
        
        if self._ratings_like_like.isChecked():
            
            value = 1.0
            
        elif self._ratings_like_dislike.isChecked():
            
            value = 0.0
            
        else:
            
            value = None
            
        
        return CAC.ApplicationCommand( CAC.APPLICATION_COMMAND_TYPE_CONTENT, ( service_key, HC.CONTENT_TYPE_RATINGS, action, value ) )
        
    
    def SetValue( self, action: int, service_key: bytes, rating: typing.Optional[ float ] ):
        
        self._flip_or_set_action.SetValue( action )
        
        self._service_keys.SetValue( service_key )
        
        self._ratings_like_remove.setChecked( rating is None )
        self._ratings_like_like.setChecked( rating == 1.0 )
        self._ratings_like_dislike.setChecked( rating == 0.0 )
        
    
class RatingNumericalSubPanel( QW.QWidget ):
    
    def __init__( self, parent: QW.QWidget ):
        
        QW.QWidget.__init__( self, parent )
        
        self._current_ratings_numerical_service = None
        
        self._flip_or_set_action = ClientGUICommon.BetterChoice( self )
        
        self._flip_or_set_action.addItem( 'set', HC.CONTENT_UPDATE_SET )
        self._flip_or_set_action.addItem( 'flip on and off', HC.CONTENT_UPDATE_FLIP )
        
        self._flip_or_set_action.SetValue( HC.CONTENT_UPDATE_SET )
        
        self._service_keys = ClientGUICommon.BetterChoice( self )
        self._service_keys.currentIndexChanged.connect( self._UpdateSliderRange )
        self._ratings_numerical_slider = QP.LabelledSlider( self )
        self._ratings_numerical_remove = QW.QCheckBox( 'remove rating', self )
        
        #
        
        services = CG.client_controller.services_manager.GetServices( ( HC.LOCAL_RATING_NUMERICAL, ) )
        
        if len( services ) == 0:
            
            self._service_keys.addItem( 'you have no numerical rating services', None )
            
        else:
            
            for service in services:
                
                service_name = service.GetName()
                service_key = service.GetServiceKey()
                
                self._service_keys.addItem( service_name, service_key )
                
            
        
        self._UpdateSliderRange()
        
        #
        
        vbox = QP.VBoxLayout()
        
        ratings_numerical_hbox = QP.HBoxLayout()
        
        QP.AddToLayout( ratings_numerical_hbox, self._service_keys, CC.FLAGS_CENTER_PERPENDICULAR_EXPAND_DEPTH )
        QP.AddToLayout( ratings_numerical_hbox, self._ratings_numerical_slider, CC.FLAGS_CENTER_PERPENDICULAR )
        QP.AddToLayout( ratings_numerical_hbox, self._ratings_numerical_remove, CC.FLAGS_CENTER_PERPENDICULAR )
        
        QP.AddToLayout( vbox, self._flip_or_set_action, CC.FLAGS_EXPAND_PERPENDICULAR )
        QP.AddToLayout( vbox, ratings_numerical_hbox, CC.FLAGS_EXPAND_PERPENDICULAR )
        
        self.setLayout( vbox )
        
        self._ratings_numerical_remove.toggled.connect( self._UpdateFlipAllowed )
        
    
    def _UpdateFlipAllowed( self ):
        
        remove_on = self._ratings_numerical_remove.isChecked()
        
        if remove_on:
            
            self._flip_or_set_action.SetValue( HC.CONTENT_UPDATE_SET )
            
        
        self._flip_or_set_action.setEnabled( not remove_on )
        self._ratings_numerical_slider.setEnabled( not remove_on )
        
    
    def _UpdateSliderRange( self ):
        
        service_key = self._service_keys.GetValue()
        
        if service_key is not None:
            
            service = CG.client_controller.services_manager.GetService( service_key )
            
            self._current_ratings_numerical_service = service
            
            num_stars = service.GetNumStars()
            
            allow_zero = service.AllowZero()
            
            if allow_zero:
                
                minimum = 0
                
            else:
                
                minimum = 1
                
            
            self._ratings_numerical_slider.SetRange( minimum, num_stars )
            
        
    
    def GetValue( self ):
        
        service_key = self._service_keys.GetValue()
        
        if service_key is None:
            
            raise HydrusExceptions.VetoException( 'Please select a rating service!' )
            
        
        action = self._flip_or_set_action.GetValue()
        
        if self._ratings_numerical_remove.isChecked():
            
            rating = None
            
        else:
            
            value = self._ratings_numerical_slider.GetValue()
            
            rating = self._current_ratings_numerical_service.ConvertStarsToRating( value )
            
        
        return CAC.ApplicationCommand( CAC.APPLICATION_COMMAND_TYPE_CONTENT, ( service_key, HC.CONTENT_TYPE_RATINGS, action, rating ) )
        
    
    def SetValue( self, action: int, service_key: bytes, rating: typing.Optional[ float ] ):
        
        self._flip_or_set_action.SetValue( action )
        
        self._service_keys.SetValue( service_key )
        
        self._UpdateSliderRange()
        
        if rating is None:
            
            self._ratings_numerical_remove.setChecked( True )
            
        else:
            
            slider_value = self._current_ratings_numerical_service.ConvertRatingToStars( rating )
            
            self._ratings_numerical_slider.SetValue( slider_value )
            
        
    

class RatingIncDecSubPanel( QW.QWidget ):
    
    def __init__( self, parent: QW.QWidget ):
        
        QW.QWidget.__init__( self, parent )
        
        self._service_keys = ClientGUICommon.BetterChoice( self )
        
        self._ratings_incdec = ClientGUICommon.BetterChoice( self )
        
        self._ratings_incdec.addItem( HC.content_update_string_lookup[ HC.CONTENT_UPDATE_INCREMENT ], HC.CONTENT_UPDATE_INCREMENT )
        self._ratings_incdec.addItem( HC.content_update_string_lookup[ HC.CONTENT_UPDATE_DECREMENT ], HC.CONTENT_UPDATE_DECREMENT )
        
        #
        
        services = CG.client_controller.services_manager.GetServices( ( HC.LOCAL_RATING_NUMERICAL, HC.LOCAL_RATING_INCDEC ) )
        
        if len( services ) == 0:
            
            self._service_keys.addItem( 'you have no numerical or inc/dec rating services', None )
            
        else:
            
            for service in services:
                
                service_name = service.GetName()
                service_key = service.GetServiceKey()
                
                self._service_keys.addItem( service_name, service_key )
                
            
        
        #
        
        hbox = QP.HBoxLayout()
        
        QP.AddToLayout( hbox, self._service_keys, CC.FLAGS_CENTER_PERPENDICULAR_EXPAND_DEPTH )
        QP.AddToLayout( hbox, self._ratings_incdec, CC.FLAGS_CENTER_PERPENDICULAR )
        
        self.setLayout( hbox )
        
    
    def GetValue( self ):
        
        service_key = self._service_keys.GetValue()
        
        if service_key is None:
            
            raise HydrusExceptions.VetoException( 'Please select a rating service!' )
            
        
        action = self._ratings_incdec.GetValue()
        
        distance = 1
        
        return CAC.ApplicationCommand( CAC.APPLICATION_COMMAND_TYPE_CONTENT, ( service_key, HC.CONTENT_TYPE_RATINGS, action, distance ) )
        
    
    def SetValue( self, action: int, service_key: bytes, distance: int ):
        
        self._service_keys.SetValue( service_key )
        
        self._ratings_incdec.SetValue( action )
        
    

class SimpleSubPanel( QW.QWidget ):
    
    def __init__( self, parent: QW.QWidget, shortcuts_name: str ):
        
        QW.QWidget.__init__( self, parent )
        
        if shortcuts_name in ClientGUIShortcuts.SHORTCUTS_RESERVED_NAMES:
            
            simple_types = ClientGUIShortcuts.simple_shortcut_name_to_action_lookup[ shortcuts_name ]
            
        else:
            
            simple_types = ClientGUIShortcuts.simple_shortcut_name_to_action_lookup[ 'custom' ]
            
        
        choices = sorted( [ ( CAC.simple_enum_to_str_lookup[ simple_type ], simple_type ) for simple_type in simple_types ] )
        
        self._simple_actions = ClientGUICommon.BetterChoice( self )
        
        for ( display_string, data ) in choices:
            
            self._simple_actions.addItem( display_string, data )
            
        
        #
        
        self._duplicates_type_panel = QW.QWidget( self )
        
        choices = [ ( HC.duplicate_type_string_lookup[ t ], t ) for t in ( HC.DUPLICATE_MEMBER, HC.DUPLICATE_ALTERNATE, HC.DUPLICATE_FALSE_POSITIVE, HC.DUPLICATE_POTENTIAL ) ]
        
        self._duplicate_type = ClientGUICommon.BetterRadioBox( self._duplicates_type_panel, choices = choices )
        
        #
        
        self._thumbnail_rearrange_panel = QW.QWidget( self )
        
        self._thumbnail_rearrange_type = ClientGUICommon.BetterChoice( self )
        
        for rearrange_type in [
            CAC.MOVE_HOME,
            CAC.MOVE_END,
            CAC.MOVE_LEFT,
            CAC.MOVE_RIGHT,
            CAC.MOVE_TO_FOCUS
        ]:
            
            self._thumbnail_rearrange_type.addItem( CAC.move_enum_to_str_lookup[ rearrange_type ], rearrange_type )
            
        
        #
        
        vbox = QP.VBoxLayout()
        
        QP.AddToLayout( vbox, self._thumbnail_rearrange_type, CC.FLAGS_EXPAND_BOTH_WAYS )
        
        self._thumbnail_rearrange_panel.setLayout( vbox )
        
        #
        
        vbox = QP.VBoxLayout()
        
        QP.AddToLayout( vbox, self._duplicate_type, CC.FLAGS_EXPAND_BOTH_WAYS )
        
        self._duplicates_type_panel.setLayout( vbox )
        
        #
        
        self._seek_panel = QW.QWidget( self )
        
        choices = [
            ( 'back', -1 ),
            ( 'forwards', 1 )
        ]
        
        self._seek_direction = ClientGUICommon.BetterRadioBox( self._seek_panel, choices = choices )
        
        self._seek_duration_s = ClientGUICommon.BetterSpinBox( self._seek_panel, max=3599, width = 60 )
        self._seek_duration_ms = ClientGUICommon.BetterSpinBox( self._seek_panel, max=999, width = 60 )
        
        #
        
        self._seek_duration_s.setValue( 5 )
        self._seek_duration_ms.setValue( 0 )
        
        self._seek_duration_s.value() * 1000 + self._seek_duration_ms.value()
        
        #
        
        hbox = QP.HBoxLayout()
        
        QP.AddToLayout( hbox, self._seek_direction, CC.FLAGS_EXPAND_BOTH_WAYS )
        QP.AddToLayout( hbox, self._seek_duration_s, CC.FLAGS_CENTER )
        QP.AddToLayout( hbox, ClientGUICommon.BetterStaticText( self._seek_panel, label = 's' ), CC.FLAGS_CENTER )
        QP.AddToLayout( hbox, self._seek_duration_ms, CC.FLAGS_CENTER )
        QP.AddToLayout( hbox, ClientGUICommon.BetterStaticText( self._seek_panel, label = 'ms' ), CC.FLAGS_CENTER )
        
        self._seek_panel.setLayout( hbox )
        
        #
        
        self._thumbnail_move_panel = QW.QWidget( self )
        
        choices = [ ( CAC.selection_status_enum_to_str_lookup[ s ], s ) for s in [ CAC.SELECTION_STATUS_NORMAL, CAC.SELECTION_STATUS_SHIFT ] ]
        
        self._selection_status = ClientGUICommon.BetterRadioBox( self._thumbnail_move_panel, choices = choices )
        
        self._selection_status.SetValue( CAC.SELECTION_STATUS_NORMAL )
        
        self._move_direction = ClientGUICommon.BetterChoice( self._thumbnail_move_panel )
        
        for m in [ CAC.MOVE_LEFT, CAC.MOVE_RIGHT, CAC.MOVE_UP, CAC.MOVE_DOWN, CAC.MOVE_HOME, CAC.MOVE_END, CAC.MOVE_PAGE_UP, CAC.MOVE_PAGE_DOWN ]:
            
            self._move_direction.addItem( CAC.move_enum_to_str_lookup[ m ], m )
            
        
        #
        
        hbox = QP.HBoxLayout()
        
        QP.AddToLayout( hbox, self._selection_status, CC.FLAGS_CENTER )
        QP.AddToLayout( hbox, self._move_direction, CC.FLAGS_CENTER )
        
        self._thumbnail_move_panel.setLayout( hbox )
        
        #
        
        self._file_filter_panel = QW.QWidget( self )
        
        self._file_filter = ClientGUICommon.BetterChoice( self._file_filter_panel )
        
        for file_filter in [
            ClientMediaFileFilter.FileFilter( ClientMediaFileFilter.FILE_FILTER_ALL ),
            ClientMediaFileFilter.FileFilter( ClientMediaFileFilter.FILE_FILTER_NONE ),
            ClientMediaFileFilter.FileFilter( ClientMediaFileFilter.FILE_FILTER_INBOX ),
            ClientMediaFileFilter.FileFilter( ClientMediaFileFilter.FILE_FILTER_ARCHIVE ),
            ClientMediaFileFilter.FileFilter( ClientMediaFileFilter.FILE_FILTER_LOCAL ),
            ClientMediaFileFilter.FileFilter( ClientMediaFileFilter.FILE_FILTER_FILE_SERVICE, filter_data = CC.TRASH_SERVICE_KEY )
        ]:
            
            self._file_filter.addItem( file_filter.ToString(), file_filter )
            
        
        #
        
        hbox = QP.HBoxLayout()
        
        QP.AddToLayout( hbox, self._file_filter, CC.FLAGS_CENTER )
        
        self._file_filter_panel.setLayout( hbox )
        
        #
        
        vbox = QP.VBoxLayout()
        
        QP.AddToLayout( vbox, self._simple_actions, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
        QP.AddToLayout( vbox, self._duplicates_type_panel, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
        QP.AddToLayout( vbox, self._seek_panel, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
        QP.AddToLayout( vbox, self._thumbnail_move_panel, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
        QP.AddToLayout( vbox, self._file_filter_panel, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
        QP.AddToLayout( vbox, self._thumbnail_rearrange_panel, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
        
        self.setLayout( vbox )
        
        self._simple_actions.currentIndexChanged.connect( self._UpdateControls )
        
        self._UpdateControls()
        
    
    def _UpdateControls( self ):
        
        action = self._simple_actions.GetValue()
        
        self._thumbnail_rearrange_panel.setVisible( action == CAC.SIMPLE_REARRANGE_THUMBNAILS )
        self._duplicates_type_panel.setVisible( action == CAC.SIMPLE_SHOW_DUPLICATES )
        self._seek_panel.setVisible( action == CAC.SIMPLE_MEDIA_SEEK_DELTA )
        self._thumbnail_move_panel.setVisible( action == CAC.SIMPLE_MOVE_THUMBNAIL_FOCUS )
        self._file_filter_panel.setVisible( action == CAC.SIMPLE_SELECT_FILES )
        
    
    def GetValue( self ):
        
        action = self._simple_actions.GetValue()
        
        if action == '':
            
            raise HydrusExceptions.VetoException( 'Please select an action!' )
            
        else:
            
            if action == CAC.SIMPLE_SHOW_DUPLICATES:
                
                duplicate_type = self._duplicate_type.GetValue()
                
                simple_data = duplicate_type
                
            elif action == CAC.SIMPLE_MEDIA_SEEK_DELTA:
                
                direction = self._seek_direction.GetValue()
                
                s = self._seek_duration_s.value()
                ms = self._seek_duration_ms.value() + ( 1000 * s )
                
                simple_data = ( direction, ms )
                
            elif action == CAC.SIMPLE_MOVE_THUMBNAIL_FOCUS:
                
                move_direction = self._move_direction.GetValue()
                selection_status = self._selection_status.GetValue()
                
                simple_data = ( move_direction, selection_status )
                
            elif action == CAC.SIMPLE_SELECT_FILES:
                
                file_filter = self._file_filter.GetValue()
                
                simple_data = file_filter
                
            elif action == CAC.SIMPLE_REARRANGE_THUMBNAILS:
                
                rearrange_type = self._thumbnail_rearrange_type.GetValue()
                
                simple_data = ( CAC.REARRANGE_THUMBNAILS_TYPE_COMMAND, rearrange_type )
                
            else:
                
                simple_data = None
                
            
            return CAC.ApplicationCommand.STATICCreateSimpleCommand( action, simple_data = simple_data )
            
        
    
    def SetValue( self, command: CAC.ApplicationCommand ):
        
        action = command.GetSimpleAction()
        
        self._simple_actions.SetValue( action )
        
        if action == CAC.SIMPLE_SHOW_DUPLICATES:
            
            duplicate_type = command.GetSimpleData()
            
            self._duplicate_type.SetValue( duplicate_type )
            
        elif action == CAC.SIMPLE_MEDIA_SEEK_DELTA:
            
            ( direction, ms ) = command.GetSimpleData()
            
            self._seek_direction.SetValue( direction )
            
            s = ms // 1000
            
            ms = ms % 1000
            
            self._seek_duration_s.setValue( s )
            self._seek_duration_ms.setValue( ms )
            
        elif action == CAC.SIMPLE_MOVE_THUMBNAIL_FOCUS:
            
            ( move_direction, selection_status ) = command.GetSimpleData()
            
            self._move_direction.SetValue( move_direction )
            self._selection_status.SetValue( selection_status )
            
        elif action == CAC.SIMPLE_SELECT_FILES:
            
            file_filter = command.GetSimpleData()
            
            self._file_filter.SetValue( file_filter )
            
        elif action == CAC.SIMPLE_REARRANGE_THUMBNAILS:
            
            ( rearrange_type, rearrange_data ) = command.GetSimpleData()
            
            self._thumbnail_rearrange_type.SetValue( rearrange_data )
            
        
        self._UpdateControls()
        
    
class TagSubPanel( QW.QWidget ):
    
    def __init__( self, parent: QW.QWidget ):
        
        QW.QWidget.__init__( self, parent )
        
        self._flip_or_set_action = ClientGUICommon.BetterChoice( self )
        
        self._flip_or_set_action.addItem( 'set', HC.CONTENT_UPDATE_SET )
        self._flip_or_set_action.addItem( 'flip on and off', HC.CONTENT_UPDATE_FLIP )
        
        self._flip_or_set_action.SetValue( HC.CONTENT_UPDATE_SET )
        
        self._service_keys = ClientGUICommon.BetterChoice( self )
        
        self._tag_value = QW.QLineEdit( self )
        self._tag_value.setReadOnly( True )
        
        default_location_context = CG.client_controller.new_options.GetDefaultLocalLocationContext()
        
        self._tag_input = ClientGUIACDropdown.AutoCompleteDropdownTagsWrite( self, self.SetTags, default_location_context, CC.COMBINED_TAG_SERVICE_KEY )
        
        #
        
        services = CG.client_controller.services_manager.GetServices( ( HC.LOCAL_TAG, HC.TAG_REPOSITORY ) )
        
        for service in services:
            
            service_name = service.GetName()
            service_key = service.GetServiceKey()
            
            self._service_keys.addItem( service_name, service_key )
            
        
        self._tag_input.SetTagServiceKey( self._service_keys.GetValue() )
        
        #
        
        vbox = QP.VBoxLayout()
        
        tag_sub_vbox = QP.VBoxLayout()
        
        QP.AddToLayout( tag_sub_vbox, self._tag_value, CC.FLAGS_EXPAND_PERPENDICULAR )
        QP.AddToLayout( tag_sub_vbox, self._tag_input, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
        
        tag_hbox = QP.HBoxLayout()
        
        QP.AddToLayout( tag_hbox, self._service_keys, CC.FLAGS_CENTER_PERPENDICULAR_EXPAND_DEPTH )
        QP.AddToLayout( tag_hbox, tag_sub_vbox, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
        
        QP.AddToLayout( vbox, self._flip_or_set_action, CC.FLAGS_EXPAND_PERPENDICULAR )
        QP.AddToLayout( vbox, tag_hbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
        
        self.setLayout( vbox )
        
        #
        
        self._service_keys.currentIndexChanged.connect( self._NewServiceKey )
        
    
    def _NewServiceKey( self ):
        
        self._tag_input.SetTagServiceKey( self._service_keys.GetValue() )
        
    
    def GetValue( self ):
        
        service_key = self._service_keys.GetValue()
        
        if service_key is None:
            
            raise HydrusExceptions.VetoException( 'Please select a tag service!' )
            
        
        action = self._flip_or_set_action.GetValue()
        
        tag = self._tag_value.text()
        
        if tag == '':
            
            raise HydrusExceptions.VetoException( 'Please enter a tag!' )
            
        
        return CAC.ApplicationCommand( CAC.APPLICATION_COMMAND_TYPE_CONTENT, ( service_key, HC.CONTENT_TYPE_MAPPINGS, action, tag ) )
        
    
    def SetTags( self, tags ):
        
        if len( tags ) > 0:
            
            tag = list( tags )[0]
            
            self._tag_value.setText( tag )
            
        
    
    def SetValue( self, action: int, service_key: bytes, tag: str ):
        
        self._flip_or_set_action.SetValue( action )
        
        self._service_keys.SetValue( service_key )
        
        self._tag_value.setText( tag )
        
    

class ApplicationCommandWidget( ClientGUIScrolledPanels.EditPanel ):
    
    PANEL_SIMPLE = 0
    PANEL_TAG = 1
    PANEL_RATING_LIKE = 2
    PANEL_RATING_NUMERICAL = 3
    PANEL_RATING_INCDEC = 4
    PANEL_LOCAL_FILES = 5
    
    def __init__( self, parent: QW.QWidget, command: CAC.ApplicationCommand, shortcuts_name: str ):
        
        ClientGUIScrolledPanels.EditPanel.__init__( self, parent )
        
        #
        
        is_custom_or_media = shortcuts_name not in ClientGUIShortcuts.SHORTCUTS_RESERVED_NAMES or shortcuts_name == 'media'
        
        self._panel_choice = ClientGUICommon.BetterChoice( self )
        
        self._panel_choice.addItem( 'simple command', self.PANEL_SIMPLE )
        
        if is_custom_or_media:
            
            self._panel_choice.addItem( 'tag command', self.PANEL_TAG )
            self._panel_choice.addItem( 'local file command', self.PANEL_LOCAL_FILES )
            self._panel_choice.addItem( 'like/dislike rating command', self.PANEL_RATING_LIKE )
            self._panel_choice.addItem( 'numerical rating command', self.PANEL_RATING_NUMERICAL )
            self._panel_choice.addItem( 'rating increment/decrement command', self.PANEL_RATING_INCDEC )
            
        else:
            
            self._panel_choice.hide()
            
        
        self._simple_sub_panel = SimpleSubPanel( self, shortcuts_name )
        
        self._tag_sub_panel = TagSubPanel( self )
        
        self._rating_like_sub_panel = RatingLikeSubPanel( self )
        
        self._rating_numerical_sub_panel = RatingNumericalSubPanel( self )
        
        self._rating_inc_dec_sub_panel = RatingIncDecSubPanel( self )
        
        self._local_files_sub_panel = LocalFilesSubPanel( self )
        
        #
        
        if command.IsSimpleCommand():
            
            self._simple_sub_panel.SetValue( command )
            
            self._panel_choice.SetValue( self.PANEL_SIMPLE )
            
        elif command.IsContentCommand():
            
            service_key = command.GetContentServiceKey()
            
            if CG.client_controller.services_manager.ServiceExists( service_key ):
                
                service = CG.client_controller.services_manager.GetService( service_key )
                
                action = command.GetContentAction()
                value = command.GetContentValue()
                
            else:
                
                ClientGUIDialogsMessage.ShowWarning( self, 'The service that this command relies upon no longer exists! This command will reset to a default form.' )
                
                local_tag_services = list( CG.client_controller.services_manager.GetServices( ( HC.LOCAL_TAG, ) ) )
                
                service = local_tag_services[0]
                
                service_key = service.GetServiceKey()
                action = HC.CONTENT_UPDATE_SET
                value = 'tag'
                
            
            service_type = service.GetServiceType()
            
            if service_type in HC.REAL_TAG_SERVICES:
                
                tag = value
                
                self._tag_sub_panel.SetValue( action, service_key, tag )
                
                self._panel_choice.SetValue( self.PANEL_TAG )
                
            elif service_type == HC.LOCAL_FILE_DOMAIN:
                
                self._local_files_sub_panel.SetValue( action, service_key )
                
                self._panel_choice.SetValue( self.PANEL_LOCAL_FILES )
                
            elif service_type == HC.LOCAL_RATING_LIKE:
                
                rating = value
                
                self._rating_like_sub_panel.SetValue( action, service_key, rating )
                
                self._panel_choice.SetValue( self.PANEL_RATING_LIKE )
                
            elif service_type == ( HC.LOCAL_RATING_NUMERICAL, HC.LOCAL_RATING_INCDEC ):
                
                if action in ( HC.CONTENT_UPDATE_SET, HC.CONTENT_UPDATE_FLIP ):
                    
                    rating = value
                    
                    self._rating_numerical_sub_panel.SetValue( action, service_key, rating )
                    
                    self._panel_choice.SetValue( self.PANEL_RATING_NUMERICAL )
                    
                elif action in ( HC.CONTENT_UPDATE_INCREMENT, HC.CONTENT_UPDATE_DECREMENT ):
                    
                    distance = 1
                    
                    self._rating_inc_dec_sub_panel.SetValue( action, service_key, distance )
                    
                    self._panel_choice.SetValue( self.PANEL_RATING_INCDEC )
                    
                
            
        
        #
        
        vbox = QP.VBoxLayout()
        
        QP.AddToLayout( vbox, self._panel_choice, CC.FLAGS_EXPAND_PERPENDICULAR )
        QP.AddToLayout( vbox, self._simple_sub_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
        QP.AddToLayout( vbox, self._tag_sub_panel, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
        QP.AddToLayout( vbox, self._local_files_sub_panel, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
        QP.AddToLayout( vbox, self._rating_like_sub_panel, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
        QP.AddToLayout( vbox, self._rating_numerical_sub_panel, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
        QP.AddToLayout( vbox, self._rating_inc_dec_sub_panel, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
        vbox.addStretch( 1 )
        
        self.widget().setLayout( vbox )
        
        self._UpdateVisibleControls()
        
        #
        
        self._panel_choice.currentIndexChanged.connect( self._UpdateVisibleControls )
        
    
    def _UpdateVisibleControls( self ):
        
        panel_type = self._panel_choice.GetValue()
        
        self._simple_sub_panel.setVisible( panel_type == self.PANEL_SIMPLE )
        self._tag_sub_panel.setVisible( panel_type == self.PANEL_TAG )
        self._local_files_sub_panel.setVisible( panel_type == self.PANEL_LOCAL_FILES )
        self._rating_like_sub_panel.setVisible( panel_type == self.PANEL_RATING_LIKE )
        self._rating_numerical_sub_panel.setVisible( panel_type == self.PANEL_RATING_NUMERICAL )
        self._rating_inc_dec_sub_panel.setVisible( panel_type == self.PANEL_RATING_INCDEC )
        
    
    def GetValue( self ):
        
        panel_type = self._panel_choice.GetValue()
        
        if panel_type == self.PANEL_SIMPLE:
            
            return self._simple_sub_panel.GetValue()
            
        elif panel_type == self.PANEL_TAG:
            
            return self._tag_sub_panel.GetValue()
            
        elif panel_type == self.PANEL_LOCAL_FILES:
            
            return self._local_files_sub_panel.GetValue()
            
        elif panel_type == self.PANEL_RATING_LIKE:
            
            return self._rating_like_sub_panel.GetValue()
            
        elif panel_type == self.PANEL_RATING_NUMERICAL:
            
            return self._rating_numerical_sub_panel.GetValue()
            
        elif panel_type == self.PANEL_RATING_INCDEC:
            
            return self._rating_inc_dec_sub_panel.GetValue()
            
        
    
