#
##
##  This file is part of pyFormex 1.0.5  (Sat Feb 16 10:40:32 CET 2019)
##  pyFormex is a tool for generating, manipulating and transforming 3D
##  geometrical models by sequences of mathematical operations.
##  Home page: http://pyformex.org
##  Project page:  http://savannah.nongnu.org/projects/pyformex/
##  Copyright 2004-2018 (C) Benedict Verhegghe (benedict.verhegghe@ugent.be)
##  Distributed under the GNU General Public License version 3 or later.
##
##  This program 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 3 of the License, or
##  (at your option) any later version.
##
##  This program 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, see http://www.gnu.org/licenses/.
##
"""Toolbars for the pyFormex GUI.

This module defines the functions for creating the pyFormex window toolbars.
"""
from __future__ import absolute_import, division, print_function

import pyformex as pf
from pyformex import utils
from pyformex.gui import QtGui, widgets


################### General Button Functions ###########

def addButton(toolbar,tooltip,icon,func,repeat=False,toggle=False,checked=False,icon0=None,enabled=True):
    """Add a button to a toolbar.

    - `toolbar`: the toolbar where the button will be added
    - `tooltip`: the text to appear as tooltip
    - `icon`: name of the icon to be displayed on the button,
    - `func`: function to be called when the button is pressed,
    - `repeat`: if True, the `func` will repeatedly be called if button is
      held down.
    - `toggle`: if True, the button is a toggle and stays in depressed state
      until pressed again.
    - `checked`: initial state for a toggle buton.
    - `icon1`: for a toggle button, icon to display when button is not checked.
    """
    iconset = QtGui.QIcon()
    icon_on = QtGui.QPixmap(utils.findIcon(icon))
    iconset.addPixmap(icon_on, QtGui.QIcon.Normal, QtGui.QIcon.On)
    if toggle and icon0:
        icon_off = QtGui.QPixmap(utils.findIcon(icon0))
        iconset.addPixmap(icon_off, QtGui.QIcon.Normal, QtGui.QIcon.Off)

    a = toolbar.addAction(iconset, tooltip, func)
    a.setEnabled(enabled)

    b = toolbar.widgetForAction(a)

    if repeat:
        b.setAutoRepeat(True)
        b.setAutoRepeatDelay(500)
        b.clicked.connect(a.trigger)

    if toggle:
        b.setCheckable(True)
        b.clicked.connect(b.toggle)
        b.setChecked(checked)

    b.setToolTip(tooltip)

    return b


def removeButton(toolbar, button):
    """Remove a button from a toolbar."""
    toolbar.removeAction(button)


################### Script action toolbar ###########


def addActionButtons(toolbar):
    """Add the script action buttons to the toolbar."""
    from pyformex.gui import draw
    from pyformex.gui.menus import File
    action = {}
    avail_buttons = [
        ( "Play", "next", draw.play, False ),
        ( "ReRun", "rerun", draw.replay, False ),
        ## ( "Step", "nextstop", draw.step, False ),
        ( "Continue", "ff", draw.fforward, False ),
        ( "Stop", "stop", draw.raiseExit, False ),
        ( "Edit", "pencil", File.editApp, False ),
        ( "Info", "info", draw.showDoc, False ),
        ]
    # Filter configured buttons
    show_buttons = pf.cfg['gui/actionbuttons']
    show_buttons = [ b for b in avail_buttons if b[0].lower() in show_buttons ]
    for name, icon, func, enabled in show_buttons:
        action[name] = addButton(toolbar, '', icon, func, enabled=enabled)
    return action


################# Camera action toolbar ###############


def addCameraButtons(toolbar):
    """Add the camera buttons to a toolbar."""
    # The buttons have the following fields:
    #  0 : tooltip
    #  1 : icon
    #  2 : function
    # optional:
    #  3 : REPEAT  (default True)
    from pyformex.gui.menus import Camera
    buttons = [ [ "Rotate left", "rotleft", Camera.rotLeft ],
                [ "Rotate right", "rotright", Camera.rotRight ],
                [ "Rotate up", "rotup", Camera.rotUp ],
                [ "Rotate down", "rotdown", Camera.rotDown ],
                [ "Twist left", "twistleft", Camera.twistLeft ],
                [ "Twist right", "twistright", Camera.twistRight ],
                [ "Translate left", "left", Camera.panLeft ],
                [ "Translate right", "right", Camera.panRight ],
                [ "Translate down", "down", Camera.panDown ],
                [ "Translate up", "up", Camera.panUp ],
                [ "Zoom Out", "zoomout", Camera.dollyOut ],
                [ "Zoom In", "zoomin", Camera.dollyIn ],
                [ "Zoom Rectangle", "zoomrect", Camera.zoomRectangle, False ],
                [ "Zoom All", "zoomall", Camera.zoomAll, False ],
                ]
    for but in buttons:
        icon = widgets.pyformexIcon(but[1])
        a = toolbar.addAction(icon, but[0], but[2])
        b = toolbar.children()[-1] # Get the QToolButton for the last action
        if len(but) < 4 or but[3]:
            b.setAutoRepeat(True)
            b.setAutoRepeatDelay(500)
            # This causes a double action on the button click
            # only use it if we can detach the button press from trigger
            #b.released.connect(a.trigger)
        if len(but) >= 5:
            # This is currently not used
            b.setCheckable(but[4])
            b.released.connect(a.toggle)

        b.setToolTip(but[0])


#######################################################################
# Viewport Toggle buttons #
###########################


class Toggle(object):
    def __init__(self,state=False):
        self.state = state
    def toggle(self,onoff=None):
        if onoff is None:
            onoff = not self.state
        self.state = onoff



class ViewportToggleButton(object):
    def __init__(self,toolbar,tooltip,icon,func,attr,checked=False,icon0=None):
        self.button = addButton(toolbar, tooltip, icon, func, toggle=True, icon0=icon0)
        self.attr = attr

    def updateButton(self):
        """Update the button to current viewport state."""
        #print "UPDATE TOGGLE %s" % attr
        vp = pf.GUI.viewports.current
        if vp == pf.canvas:
            self.setChecked(vp.settings[self.attr])
        pf.GUI.processEvents()

    def toggle(self,attr,state=None):
        """Update the corresponding viewport attribute.

        This does not update the button state.
        """
        vp = pf.GUI.viewports.current
        vp.setToggle(attr, state)
        vp.update()
        pf.GUI.processEvents()


def toggleButton(attr,state=None):
    """Update the corresponding viewport attribute.

    This does not update the button state.
    """
    vp = pf.GUI.viewports.current
    vp.setToggle(attr, state)
    vp.update()
    pf.GUI.processEvents()

def updateButton(button, attr):
    """Update the button to correct state."""
    if button:
        vp = pf.GUI.viewports.current
        button.setChecked(vp.settings[attr])
        pf.GUI.processEvents()


def updateViewportButtons(vp):
    ## if vp != pf.GUI.viewports.current:
    ##     print "viewport %s is not current" % pf.GUI.viewports.viewIndex(vp)
    if vp.focus:
        #print "viewport %s has focus" % pf.GUI.viewports.viewIndex(vp)
        updateWireButton()
        updateTransparencyButton()
        updateLightButton()
        updateNormalsButton()


#
# BEWARE: The toggle functions are currently not passed the state!
#         It should be got from
#

################# Wire Button ###############

wire_button = None # the toggle wire button

def addWireButton(toolbar):
    global wire_button
    wire_button = addButton(toolbar, 'Toggle Wire Mode',
                            'wireall', toggleWire, icon0='wirenone',
                            toggle=True)

def toggleWire(state=None):
    vp = pf.GUI.viewports.current
    state = wire_button.isChecked()
    # TODO:
    # WELL, it looks like we get here BEFORE the button is updated,
    # so we need to change the state.
    # WE should really connect this to a signal that is raised
    # AFTER the button state is update
    state = not state
    vp.setWireMode(state)
    vp.update()
    pf.GUI.processEvents()


def updateWireButton():
    """Update the wire button to correct state."""
    if wire_button:
        vp = pf.GUI.viewports.current
        wire_button.setChecked(vp.settings['wiremode'] > 0)
        pf.GUI.processEvents()


################# Transparency Button ###############

transparency_button = None # the toggle transparency button

def addTransparencyButton(toolbar):
    global transparency_button
    transparency_button = addButton(toolbar, 'Toggle Transparent Mode',
                                    'transparent', toggleTransparency,
                                    toggle=True)

def toggleTransparency(state=None):
    toggleButton('alphablend', state)

def updateTransparencyButton():
    """Update the transparency button to correct state."""
    updateButton(transparency_button, 'alphablend')


################# Lights Button ###############

light_button = None

def addLightButton(toolbar):
    global light_button
    light_button = addButton(toolbar, 'Toggle Lights',
                             'lamp-on', toggleLight, icon0='lamp',
                             toggle=True, checked=True)

def toggleLight(state=None):
    toggleButton('lighting', state)

def updateLightButton():
    """Update the light button to correct state."""
    updateButton(light_button, 'lighting')


################# Normals Button ###############

normals_button = None

def addNormalsButton(toolbar):
    global normals_button
    normals_button = addButton(toolbar, 'Toggle Normals',
                               'normals-avg', toggleNormals, icon0='normals-ind',
                               toggle=True, checked=False)

def toggleNormals(state=None):
    toggleButton('avgnormals', state)

def updateNormalsButton(state=True):
    """Update the normals button to correct state."""
    updateButton(normals_button, 'avgnormals')


################# Perspective Button ###############

perspective_button = None # the toggle perspective button

def togglePerspective(mode=None): # Called by the button, not by user
    vp = pf.GUI.viewports.current
    if mode is None:
        mode = not vp.camera.perspective
    vp.camera.setPerspective(mode)
    vp.display()
    vp.update()
    pf.GUI.processEvents()

def addPerspectiveButton(toolbar):
    global perspective_button
    perspective_button = addButton(toolbar, 'Toggle Perspective/Projective Mode',
                                   'perspect', togglePerspective,
                                   toggle=True, icon0='project', checked=True)

def updatePerspectiveButton():
    """Update the normals button to correct state."""
    #updateButton(perspective_button,'avgnormals')
    vp = pf.GUI.viewports.current
    if vp == pf.canvas:
        perspective_button.setChecked(vp.camera.perspective)
    pf.GUI.processEvents()

def setPerspective():
    togglePerspective(True)
    updatePerspectiveButton()

def setProjection():
    togglePerspective(False)
    updatePerspectiveButton()

################# Shrink Button ###############

#
# The shrink button currently does not redraw, it only sets the default
# shrink factor and clears the viewport
#
shrink_button = None # the toggle shrink button

def toggleShrink(): # Called by the button
    from pyformex.gui import draw
    mode = draw.DrawOptions.get('shrink', None)
    if mode is None:
        mode = 0.8
    else:
        mode = None
    draw.shrink(mode)

def addShrinkButton(toolbar):
    global shrink_button
    shrink_button = addButton(toolbar, 'Toggle Shrink Mode',
                              'shrink', toggleShrink,
                              toggle=True)

def setShrink(mode):
    from pyformex.gui import draw
    draw.shrink(mode)
    if shrink_button:
        shrink_button.setChecked(mode != None)

################# Timeout Button ###############

timeout_button = None # the timeout toggle button

def toggleTimeout(onoff=None):
    if onoff is None:
        onoff = widgets.input_timeout < 0
    if onoff:
        timeout = pf.cfg.get('gui/timeoutvalue', -1)
    else:
        timeout = -1

    widgets.setInputTimeout(timeout)
    onoff = widgets.input_timeout > 0
    if onoff:
        # THIS SUSPENDS ALL WAITING! WE SHOULD IMPLEMENT A TIMEOUT!
        # BY FORCING ALL INDEFINITE PAUSES TO A WAIT TIME EQUAL TO
        # WIDGET INPUT TIMEOUT
        pf.debug("FREEING the draw lock")
        pf.GUI.drawlock.free()
    else:
        pf.debug("ALLOWING the draw lock")
        pf.GUI.drawlock.allow()
    return onoff


def addTimeoutButton(toolbar):
    """Add or remove the timeout button,depending on cfg."""
    global timeout_button
    if pf.cfg['gui/timeoutbutton']:
        if timeout_button is None:
            timeout_button = addButton(toolbar, 'Toggle Timeout', 'clock', toggleTimeout, toggle=True, checked=False)
    else:
        if timeout_button is not None:
            removeButton(toolbar, timeout_button)
            timeout_button = None


def timeout(onoff=None):
    """Programmatically toggle the timeout button"""
    if timeout_button is not None:
        timeout_button.setChecked(toggleTimeout(onoff))



# End
