"""
Copyright 2007-2011 Free Software Foundation, Inc.
This file is part of GNU Radio

GNU Radio Companion is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

GNU Radio Companion is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
"""

from __future__ import absolute_import

import six

import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import GObject

from . import Preferences

NO_MODS_MASK = 0


########################################################################
# Actions API
########################################################################
_actions_keypress_dict = dict()
_keymap = Gdk.Keymap.get_default()
_used_mods_mask = NO_MODS_MASK


def handle_key_press(event):
    """
    Call the action associated with the key press event.
    Both the key value and the mask must have a match.

    Args:
        event: a gtk key press event

    Returns:
        true if handled
    """
    # extract the key value and the consumed modifiers
    _unused, keyval, egroup, level, consumed = _keymap.translate_keyboard_state(
        event.hardware_keycode, event.get_state(), event.group)
    # get the modifier mask and ignore irrelevant modifiers
    mod_mask = event.get_state() & ~consumed & _used_mods_mask
    # look up the keypress and call the action
    try:
        _actions_keypress_dict[(keyval, mod_mask)]()
    except KeyError:
        return False  # not handled
    else:
        return True  # handled here

_all_actions_list = list()


def get_all_actions():
    return _all_actions_list

_accel_group = Gtk.AccelGroup()


def get_accel_group():
    return _accel_group


class _ActionBase(object):
    """
    Base class for Action and ToggleAction
    Register actions and keypresses with this module.
    """
    def __init__(self, label, keypresses):
        global _used_mods_mask

        _all_actions_list.append(self)
        for i in range(len(keypresses)/2):
            keyval, mod_mask = keypresses[i*2:(i+1)*2]
            # register this keypress
            if (keyval, mod_mask) in _actions_keypress_dict:
                raise KeyError('keyval/mod_mask pair already registered "%s"' % str((keyval, mod_mask)))
            _actions_keypress_dict[(keyval, mod_mask)] = self
            _used_mods_mask |= mod_mask
            # set the accelerator group, and accelerator path
            # register the key name and mod mask with the accelerator path
            if label is None:
                continue  # dont register accel
            accel_path = '<main>/' + self.get_name()
            self.set_accel_group(get_accel_group())
            self.set_accel_path(accel_path)
            Gtk.AccelMap.add_entry(accel_path, keyval, mod_mask)
        self.args = None

    def __str__(self):
        """
        The string representation should be the name of the action id.
        Try to find the action id for this action by searching this module.
        """
        for name, value in six.iteritems(globals()):
            if value == self:
                return name
        return self.get_name()

    def __repr__(self): return str(self)

    def __call__(self, *args):
        """
        Emit the activate signal when called with ().
        """
        self.args = args
        self.emit('activate')


class Action(Gtk.Action, _ActionBase):
    """
    A custom Action class based on Gtk.Action.
    Pass additional arguments such as keypresses.
    """

    def __init__(self, keypresses=(), name=None, label=None, tooltip=None,
                 stock_id=None):
        """
        Create a new Action instance.

        Args:
            key_presses: a tuple of (keyval1, mod_mask1, keyval2, mod_mask2, ...)
            the: regular Gtk.Action parameters (defaults to None)
        """
        if name is None:
            name = label
        GObject.GObject.__init__(self, name=name, label=label, tooltip=tooltip,
                            stock_id=stock_id)
        _ActionBase.__init__(self, label, keypresses)


class ToggleAction(Gtk.ToggleAction, _ActionBase):
    """
    A custom Action class based on Gtk.ToggleAction.
    Pass additional arguments such as keypresses.
    """

    def __init__(self, keypresses=(), name=None, label=None, tooltip=None,
                 stock_id=None, preference_name=None, default=True):
        """
        Create a new ToggleAction instance.

        Args:
            key_presses: a tuple of (keyval1, mod_mask1, keyval2, mod_mask2, ...)
            the: regular Gtk.Action parameters (defaults to None)
        """
        if name is None:
            name = label
        GObject.GObject.__init__(self, name=name, label=label,
                                  tooltip=tooltip, stock_id=stock_id)
        _ActionBase.__init__(self, label, keypresses)
        self.preference_name = preference_name
        self.default = default

    def load_from_preferences(self):
        if self.preference_name is not None:
            self.set_active(Preferences.entry(
                self.preference_name, default=bool(self.default)))

    def save_to_preferences(self):
        if self.preference_name is not None:
            Preferences.entry(self.preference_name, value=self.get_active())

########################################################################
# Actions
########################################################################
PAGE_CHANGE = Action()
EXTERNAL_UPDATE = Action()
VARIABLE_EDITOR_UPDATE = Action()
FLOW_GRAPH_NEW = Action(
    label='_New',
    tooltip='Create a new flow graph',
    stock_id=Gtk.STOCK_NEW,
    keypresses=(Gdk.KEY_n, Gdk.ModifierType.CONTROL_MASK),
)
FLOW_GRAPH_OPEN = Action(
    label='_Open',
    tooltip='Open an existing flow graph',
    stock_id=Gtk.STOCK_OPEN,
    keypresses=(Gdk.KEY_o, Gdk.ModifierType.CONTROL_MASK),
)
FLOW_GRAPH_OPEN_RECENT = Action(
    label='Open _Recent',
    tooltip='Open a recently used flow graph',
    stock_id=Gtk.STOCK_OPEN,
)
FLOW_GRAPH_SAVE = Action(
    label='_Save',
    tooltip='Save the current flow graph',
    stock_id=Gtk.STOCK_SAVE,
    keypresses=(Gdk.KEY_s, Gdk.ModifierType.CONTROL_MASK),
)
FLOW_GRAPH_SAVE_AS = Action(
    label='Save _As',
    tooltip='Save the current flow graph as...',
    stock_id=Gtk.STOCK_SAVE_AS,
    keypresses=(Gdk.KEY_s, Gdk.ModifierType.CONTROL_MASK | Gdk.ModifierType.SHIFT_MASK),
)
FLOW_GRAPH_CLOSE = Action(
    label='_Close',
    tooltip='Close the current flow graph',
    stock_id=Gtk.STOCK_CLOSE,
    keypresses=(Gdk.KEY_w, Gdk.ModifierType.CONTROL_MASK),
)
APPLICATION_INITIALIZE = Action()
APPLICATION_QUIT = Action(
    label='_Quit',
    tooltip='Quit program',
    stock_id=Gtk.STOCK_QUIT,
    keypresses=(Gdk.KEY_q, Gdk.ModifierType.CONTROL_MASK),
)
FLOW_GRAPH_UNDO = Action(
    label='_Undo',
    tooltip='Undo a change to the flow graph',
    stock_id=Gtk.STOCK_UNDO,
    keypresses=(Gdk.KEY_z, Gdk.ModifierType.CONTROL_MASK),
)
FLOW_GRAPH_REDO = Action(
    label='_Redo',
    tooltip='Redo a change to the flow graph',
    stock_id=Gtk.STOCK_REDO,
    keypresses=(Gdk.KEY_y, Gdk.ModifierType.CONTROL_MASK),
)
NOTHING_SELECT = Action()
SELECT_ALL = Action(
    label='Select _All',
    tooltip='Select all blocks and connections in the flow graph',
    stock_id=Gtk.STOCK_SELECT_ALL,
    keypresses=(Gdk.KEY_a, Gdk.ModifierType.CONTROL_MASK),
)
ELEMENT_SELECT = Action()
ELEMENT_CREATE = Action()
ELEMENT_DELETE = Action(
    label='_Delete',
    tooltip='Delete the selected blocks',
    stock_id=Gtk.STOCK_DELETE,
    keypresses=(Gdk.KEY_Delete, NO_MODS_MASK),
)
BLOCK_MOVE = Action()
BLOCK_ROTATE_CCW = Action(
    label='Rotate Counterclockwise',
    tooltip='Rotate the selected blocks 90 degrees to the left',
    stock_id=Gtk.STOCK_GO_BACK,
    keypresses=(Gdk.KEY_Left, NO_MODS_MASK),
)
BLOCK_ROTATE_CW = Action(
    label='Rotate Clockwise',
    tooltip='Rotate the selected blocks 90 degrees to the right',
    stock_id=Gtk.STOCK_GO_FORWARD,
    keypresses=(Gdk.KEY_Right, NO_MODS_MASK),
)
BLOCK_VALIGN_TOP = Action(
    label='Vertical Align Top',
    tooltip='Align tops of selected blocks',
    keypresses=(Gdk.KEY_t, Gdk.ModifierType.SHIFT_MASK),
)
BLOCK_VALIGN_MIDDLE = Action(
    label='Vertical Align Middle',
    tooltip='Align centers of selected blocks vertically',
    keypresses=(Gdk.KEY_m, Gdk.ModifierType.SHIFT_MASK),
)
BLOCK_VALIGN_BOTTOM = Action(
    label='Vertical Align Bottom',
    tooltip='Align bottoms of selected blocks',
    keypresses=(Gdk.KEY_b, Gdk.ModifierType.SHIFT_MASK),
)
BLOCK_HALIGN_LEFT = Action(
    label='Horizontal Align Left',
    tooltip='Align left edges of blocks selected blocks',
    keypresses=(Gdk.KEY_l, Gdk.ModifierType.SHIFT_MASK),
)
BLOCK_HALIGN_CENTER = Action(
    label='Horizontal Align Center',
    tooltip='Align centers of selected blocks horizontally',
    keypresses=(Gdk.KEY_c, Gdk.ModifierType.SHIFT_MASK),
)
BLOCK_HALIGN_RIGHT = Action(
    label='Horizontal Align Right',
    tooltip='Align right edges of selected blocks',
    keypresses=(Gdk.KEY_r, Gdk.ModifierType.SHIFT_MASK),
)
BLOCK_ALIGNMENTS = [
    BLOCK_VALIGN_TOP,
    BLOCK_VALIGN_MIDDLE,
    BLOCK_VALIGN_BOTTOM,
    None,
    BLOCK_HALIGN_LEFT,
    BLOCK_HALIGN_CENTER,
    BLOCK_HALIGN_RIGHT,
]
BLOCK_PARAM_MODIFY = Action(
    label='_Properties',
    tooltip='Modify params for the selected block',
    stock_id=Gtk.STOCK_PROPERTIES,
    keypresses=(Gdk.KEY_Return, NO_MODS_MASK),
)
BLOCK_ENABLE = Action(
    label='E_nable',
    tooltip='Enable the selected blocks',
    stock_id=Gtk.STOCK_CONNECT,
    keypresses=(Gdk.KEY_e, NO_MODS_MASK),
)
BLOCK_DISABLE = Action(
    label='D_isable',
    tooltip='Disable the selected blocks',
    stock_id=Gtk.STOCK_DISCONNECT,
    keypresses=(Gdk.KEY_d, NO_MODS_MASK),
)
BLOCK_BYPASS = Action(
    label='_Bypass',
    tooltip='Bypass the selected block',
    stock_id=Gtk.STOCK_MEDIA_FORWARD,
    keypresses=(Gdk.KEY_b, NO_MODS_MASK),
)
TOGGLE_SNAP_TO_GRID = ToggleAction(
    label='_Snap to grid',
    tooltip='Snap blocks to a grid for an easier connection alignment',
    preference_name='snap_to_grid'
)
TOGGLE_HIDE_DISABLED_BLOCKS = ToggleAction(
    label='Hide _Disabled Blocks',
    tooltip='Toggle visibility of disabled blocks and connections',
    stock_id=Gtk.STOCK_MISSING_IMAGE,
    keypresses=(Gdk.KEY_d, Gdk.ModifierType.CONTROL_MASK),
)
TOGGLE_HIDE_VARIABLES = ToggleAction(
    label='Hide Variables',
    tooltip='Hide all variable blocks',
    preference_name='hide_variables',
    default=False,
)
TOGGLE_FLOW_GRAPH_VAR_EDITOR = ToggleAction(
    label='Show _Variable Editor',
    tooltip='Show the variable editor. Modify variables and imports in this flow graph',
    stock_id=Gtk.STOCK_EDIT,
    default=True,
    keypresses=(Gdk.KEY_e, Gdk.ModifierType.CONTROL_MASK),
    preference_name='variable_editor_visable',
)
TOGGLE_FLOW_GRAPH_VAR_EDITOR_SIDEBAR = ToggleAction(
    label='Move the Variable Editor to the Sidebar',
    tooltip='Move the variable editor to the sidebar',
    default=False,
    preference_name='variable_editor_sidebar',
)
TOGGLE_AUTO_HIDE_PORT_LABELS = ToggleAction(
    label='Auto-Hide _Port Labels',
    tooltip='Automatically hide port labels',
    preference_name='auto_hide_port_labels'
)
TOGGLE_SHOW_BLOCK_COMMENTS = ToggleAction(
    label='Show Block Comments',
    tooltip="Show comment beneath each block",
    preference_name='show_block_comments'
)
TOGGLE_SHOW_CODE_PREVIEW_TAB = ToggleAction(
    label='Generated Code Preview',
    tooltip="Show a preview of the code generated for each Block in its "
            "Properties Dialog",
    preference_name='show_generated_code_tab',
    default=False,
)
TOGGLE_SHOW_FLOWGRAPH_COMPLEXITY = ToggleAction(
    label='Show Flowgraph Complexity',
    tooltip="How many Balints is the flowgraph...",
    preference_name='show_flowgraph_complexity',
    default=False,
)
BLOCK_CREATE_HIER = Action(
    label='C_reate Hier',
    tooltip='Create hier block from selected blocks',
    stock_id=Gtk.STOCK_CONNECT,
#   keypresses=(Gdk.KEY_c, NO_MODS_MASK),
)
BLOCK_CUT = Action(
    label='Cu_t',
    tooltip='Cut',
    stock_id=Gtk.STOCK_CUT,
    keypresses=(Gdk.KEY_x, Gdk.ModifierType.CONTROL_MASK),
)
BLOCK_COPY = Action(
    label='_Copy',
    tooltip='Copy',
    stock_id=Gtk.STOCK_COPY,
    keypresses=(Gdk.KEY_c, Gdk.ModifierType.CONTROL_MASK),
)
BLOCK_PASTE = Action(
    label='_Paste',
    tooltip='Paste',
    stock_id=Gtk.STOCK_PASTE,
    keypresses=(Gdk.KEY_v, Gdk.ModifierType.CONTROL_MASK),
)
ERRORS_WINDOW_DISPLAY = Action(
    label='Flowgraph _Errors',
    tooltip='View flow graph errors',
    stock_id=Gtk.STOCK_DIALOG_ERROR,
)
TOGGLE_CONSOLE_WINDOW = ToggleAction(
    label='Show _Console Panel',
    tooltip='Toggle visibility of the console',
    keypresses=(Gdk.KEY_r, Gdk.ModifierType.CONTROL_MASK),
    preference_name='console_window_visible'
)
TOGGLE_BLOCKS_WINDOW = ToggleAction(
    label='Show _Block Tree Panel',
    tooltip='Toggle visibility of the block tree widget',
    keypresses=(Gdk.KEY_b, Gdk.ModifierType.CONTROL_MASK),
    preference_name='blocks_window_visible'
)
TOGGLE_SCROLL_LOCK = ToggleAction(
    label='Console Scroll _Lock',
    tooltip='Toggle scroll lock for the console window',
    preference_name='scroll_lock'
)
ABOUT_WINDOW_DISPLAY = Action(
    label='_About',
    tooltip='About this program',
    stock_id=Gtk.STOCK_ABOUT,
)
HELP_WINDOW_DISPLAY = Action(
    label='_Help',
    tooltip='Usage tips',
    stock_id=Gtk.STOCK_HELP,
    keypresses=(Gdk.KEY_F1, NO_MODS_MASK),
)
TYPES_WINDOW_DISPLAY = Action(
    label='_Types',
    tooltip='Types color mapping',
    stock_id=Gtk.STOCK_DIALOG_INFO,
)
FLOW_GRAPH_GEN = Action(
    label='_Generate',
    tooltip='Generate the flow graph',
    stock_id=Gtk.STOCK_CONVERT,
    keypresses=(Gdk.KEY_F5, NO_MODS_MASK),
)
FLOW_GRAPH_EXEC = Action(
    label='_Execute',
    tooltip='Execute the flow graph',
    stock_id=Gtk.STOCK_MEDIA_PLAY,
    keypresses=(Gdk.KEY_F6, NO_MODS_MASK),
)
FLOW_GRAPH_KILL = Action(
    label='_Kill',
    tooltip='Kill the flow graph',
    stock_id=Gtk.STOCK_STOP,
    keypresses=(Gdk.KEY_F7, NO_MODS_MASK),
)
FLOW_GRAPH_SCREEN_CAPTURE = Action(
    label='Screen Ca_pture',
    tooltip='Create a screen capture of the flow graph',
    stock_id=Gtk.STOCK_PRINT,
    keypresses=(Gdk.KEY_Print, NO_MODS_MASK),
)
PORT_CONTROLLER_DEC = Action(
    keypresses=(Gdk.KEY_minus, NO_MODS_MASK, Gdk.KEY_KP_Subtract, NO_MODS_MASK),
)
PORT_CONTROLLER_INC = Action(
    keypresses=(Gdk.KEY_plus, NO_MODS_MASK, Gdk.KEY_KP_Add, NO_MODS_MASK),
)
BLOCK_INC_TYPE = Action(
    keypresses=(Gdk.KEY_Down, NO_MODS_MASK),
)
BLOCK_DEC_TYPE = Action(
    keypresses=(Gdk.KEY_Up, NO_MODS_MASK),
)
RELOAD_BLOCKS = Action(
    label='Reload _Blocks',
    tooltip='Reload Blocks',
    stock_id=Gtk.STOCK_REFRESH
)
FIND_BLOCKS = Action(
    label='_Find Blocks',
    tooltip='Search for a block by name (and key)',
    stock_id=Gtk.STOCK_FIND,
    keypresses=(Gdk.KEY_f, Gdk.ModifierType.CONTROL_MASK,
                Gdk.KEY_slash, NO_MODS_MASK),
)
CLEAR_CONSOLE = Action(
    label='_Clear Console',
    tooltip='Clear Console',
    stock_id=Gtk.STOCK_CLEAR,
)
SAVE_CONSOLE = Action(
    label='_Save Console',
    tooltip='Save Console',
    stock_id=Gtk.STOCK_SAVE,
)
OPEN_HIER = Action(
    label='Open H_ier',
    tooltip='Open the source of the selected hierarchical block',
    stock_id=Gtk.STOCK_JUMP_TO,
)
BUSSIFY_SOURCES = Action(
    label='Toggle So_urce Bus',
    tooltip='Gang source ports into a single bus port',
    stock_id=Gtk.STOCK_JUMP_TO,
)
BUSSIFY_SINKS = Action(
    label='Toggle S_ink Bus',
    tooltip='Gang sink ports into a single bus port',
    stock_id=Gtk.STOCK_JUMP_TO,
)
XML_PARSER_ERRORS_DISPLAY = Action(
    label='_Parser Errors',
    tooltip='View errors that occurred while parsing XML files',
    stock_id=Gtk.STOCK_DIALOG_ERROR,
)
FLOW_GRAPH_OPEN_QSS_THEME = Action(
    label='Set Default QT GUI _Theme',
    tooltip='Set a default QT Style Sheet file to use for QT GUI',
    stock_id=Gtk.STOCK_OPEN,
)
TOOLS_RUN_FDESIGN = Action(
    label='Filter Design Tool',
    tooltip='Execute gr_filter_design',
    stock_id=Gtk.STOCK_EXECUTE,
)
TOOLS_MORE_TO_COME = Action(
    label='More to come',
)