#############################################################################
#
# Author: Anna Omelchenko
#
# Copyright: M. Sanner TSRI
#
#############################################################################

#
#$Header: /opt/cvs/Volume/Pvv/lightCommands.py,v 1.3 2004/07/27 21:32:28 annao Exp $
#
#$Id: lightCommands.py,v 1.3 2004/07/27 21:32:28 annao Exp $
#
"""Module implements a command to add, remove and change the direction of
one or more light sources. A light direction is specified in
world space. A light intensity is set to 1 (full intensity).
The GUI consists of eight check buttons corresponding to
a similar set of buttons of the DejaVu GUI that allow
the user to select a light source,
a switch to turn selected light on/off and
a combobox with a dropdown listbox containing the light sources
direction info. Picking one of the lists entries
selects a corresponding light source.
A set of entry fields can be used to specify the current light direction.
The current light direction can be set interactively using the mouse by
redirecting the mouse transformation to the current light source in
DejaVu Viewer GUI ('showHideDejaVuGUI' from DejaVu Macro library).
"""

from ViewerFramework.VFCommand import Command, CommandGUI
from mglutil.gui.InputForm.Tk.gui import InputFormDescr
import Tkinter, string
import Pmw
from types import IntType
from DejaVu.Slider import Slider

volGeom = None

LIGHT_ON = "red"
LIGHT_OFF = "black"

class Light(Command):
    """Command to add, remove and change the direction of
    one or more light sources. """


    def __init__(self):
        Command.__init__(self)
        self.currentValues=[-1.0, -1.0, -1.0]
        self.isDisplayed = 0
        self.idf = None
        self.lights_list = []
        self.entry = ''

    def onAddCmdToViewer(self):
        """Called when command is loaded into the viewer"""
        volGeom.lightCallBack = self.updateForm
        gui = self.vf.GUI.VIEWER.GUI
        self.LightOnOff = gui.lightOnOff
        self.CurrentLight = gui.CurrentLight
        self.updateLightsList()


    def updateLightsList(self):
        """ Updates a list containing lights directions info."""
        self.lights_list=[]
        vi = self.vf.GUI.VIEWER
        currdir = [-1.0, -1.0, -1.0]
        for l in vi.lights:
            d = l.direction
            self.lights_list.append("x:%0.2f, y:%0.2f, z:%0.2f" %
                                    (d[0]*(-1),d[1]*(-1),d[2]*(-1)))
            if l == vi.currentLight:
                currdir = d
        self.currentValues = [currdir[0], currdir[1], currdir[2]]

    def doit(self, **kw):
        """Adds, removes a light or changes the direction of a light"""

        currnum = kw.get('select')
        if currnum:
            assert currnum in range(1,9)
            volGeom.currLight = volGeom.lights[currnum-1]
            if self.CurrentLight.get() != currnum:
                self.CurrentLight.set(currnum)
            if self.isDisplayed:
                w = self.idf.entryByName['lights']['widget']
                num = int(w.curselection()[0])+1
                if num != currnum:
                    w.selectitem(currnum-1)
            self.updateSelection(currnum)

        switch = kw.get('switch')
        if switch:
            gui = self.vf.GUI.VIEWER.GUI
            currnum = self.CurrentLight.get()
            if switch =='on':
                if self.LightOnOff.get() != 1:
                    # when doit() is called from __call__()
                    self.LightOnOff.set(1)
                self.addLight()
                self.updateForm(1, currnum)
            elif switch == 'off':
                if self.LightOnOff.get() != 0:
                    # when doit() is called from __call__()
                    self.LightOnOff.set(0)
                self.removeLight()
                self.updateForm(0, currnum)
            else:
                print "wrong argument: ", switch
            gui.LightOnOff()
        d = kw.get('direction')
        if d:
            assert len(d) == 3
            x,y,z = d
            vec = (x*(-1), y*(-1), z*(-1), 0.0)
            vi = volGeom.viewer
            vi.currentLight.Set(direction = vec) 
            currnum = self.CurrentLight.get()-1
            self.setVolRenLightDirection(x, y, z)
            self.currentValues = [x, y, z]
            self.updateForm(x,y,z, currnum)
            vi.Redraw()

    def addLight(self):
        """Add a volume renderer light source to the viewer environment. """
        # Has to be implemented by a derived class.
        pass

    def removeLight(self):
        """Remove a volume renderer light source from the viewer environment. """
        # Has to be implemented by a derived class.
        pass

    def setVolRenLightDirection(self, x, y, z):
        """Set the direction of a volume renderer light."""
        # Has to be implemented by a derived class.
        pass

    def __call__(self, **kw):
        """None <- Light(**kw), where kw can be:
        select = num --- to select a light source; num  is an integer in
        range (1 ... 8);
        switch = mode -- to turn current light on or off;
                         mode is either 'on' or 'off';
        direction=(x,y,z) -- to specify new direction of current light;
        """
        # Has to be implemented by a derived class.
        pass

    def set_direction_cb(self):
        """Gets X, Y, Z direction values from the entry fields of
        the input form"""
        
        val = self.form.checkValues()
        x = float(val['x'])
        y = float(val['y'])
        z = float(val['z'])
        self.doitWrapper(direction = (x,y,z))

    def setDirection(self,x,y,z):
        """Sets new direction of a light"""
        vi = volGeom.viewer
        currnum = self.CurrentLight.get()-1
        self.setVolRenLightDirection(x, y, z)
        self.currentValues = [x, y, z]
        self.lights_list[currnum] = "x:%0.2f, y:%0.2f, z:%0.2f" % (x,y,z)
        if self.isDisplayed:
            self.idf.entryByName['lights']['widget'].delete(currnum)
            self.idf.entryByName['lights']['widget'].insert(currnum,
                                   "x:%0.2f, y:%0.2f, z:%0.2f" % (x,y,z))
            self.idf.entryByName['lights']['widget'].selectitem(currnum)
        vi.Redraw()
        
    def updateForm(self, *args):
        """Updates list of directions of available lights in the ComboBox
        of the input form"""
        if len(args)==4:
            x,y,z,currnum = args
            self.lights_list[currnum] = "x:%0.2f, y:%0.2f, z:%0.2f" % (x,y,z)
            if self.isDisplayed:
                self.idf.entryByName['lights']['widget'].delete(currnum)
                self.idf.entryByName['lights']['widget'].insert(currnum,
                                   "x:%0.2f, y:%0.2f, z:%0.2f" % (x,y,z))
                self.idf.entryByName['lights']['widget'].selectitem(currnum)
        elif len(args) == 2:
            if self.isDisplayed:
                switch, b = args
                if switch: color = LIGHT_ON
                else: color = LIGHT_OFF
                w = self.idf.entryByName['cb'+str(b)]['widget']
                w.configure(fg = color)
                w.configure(activeforeground = color)
        
    def set_current(self, x,y,z):
        """Updates current light direction entries"""
        
        self.idf.entryByName['x']['widget'].delete(0,Tkinter.END)
        self.idf.entryByName['x']['widget'].insert(Tkinter.END, "%0.2f"%x)
        self.idf.entryByName['y']['widget'].delete(0,Tkinter.END)
        self.idf.entryByName['y']['widget'].insert(Tkinter.END, "%0.2f"%y)
        self.idf.entryByName['z']['widget'].delete(0,Tkinter.END)
        self.idf.entryByName['z']['widget'].insert(Tkinter.END, "%0.2f"%z)
        

    def dismiss_cb(self):
        """Destroys the input form"""
        self.idf.form.destroy()
        self.isDisplayed=0
        self.idf = None


    def makecurrent_cb(self, value):
        """Makes selected light(from ComboBox scrolled listbox)
        current(active) light"""
        currnum = int(self.idf.entryByName['lights']['widget'].curselection()[0])+1
        self.doitWrapper(select=currnum)

    def selectlight_cb(self):
        """Called when one of the light checkbuttons is pressed.
        Makes selected light current"""
        currnum = self.CurrentLight.get()
        if currnum != 0:
            self.doitWrapper(select=currnum)
        else:
            self.LightOnOff.set(0)
            
    def updateSelection(self, currnum):
        """Updates the Viewer's GUI (DejaVu Lights) and
        current values """
        vi = self.vf.GUI.VIEWER
        vi.GUI.LightSelect()
        self.LightOnOff.set(vi.currentLight.enabled)
        vec = vi.currentLight.direction
        self.currentValues = [vec[0]*(-1), vec[1]*(-1), vec[2]*(-1)]
        if self.idf:
            self.set_current(vec[0]*(-1), vec[1]*(-1), vec[2]*(-1))

    def lightOnOff_cb(self):
        """Switches current light on/off"""
        val = self.LightOnOff.get()
        if self.CurrentLight.get()==0: return
        if val:
            switch = 'on'
        else:
            switch = 'off'
        self.doitWrapper(switch = switch)

    def guiCallback(self):
        """Creates the input form (GUI). Called every time the 'Light'
        button is pressed"""
        
        if self.isDisplayed:
            self.idf.form.lift()
            return
        self.isDisplayed=1
        idf = self.idf = InputFormDescr(title = 'Light')
        vi = self.vf.GUI.VIEWER
        currnum = int(string.split(vi.currentLight.name)[1])
        self.updateLightsList()
        idf.append({'widgetType':Tkinter.Label,
                    'wcfg': {'text':'Select Light Source:'},
                    'gridcfg':{'sticky':'we', 'columnspan':4,
                               'pady': 5}
                    })
        i = 0
        for l in vi.lights: 
            pos = divmod(i+4,4)
            if l.enabled: color = LIGHT_ON
            else: color = LIGHT_OFF
            idf.append({'widgetType':Tkinter.Checkbutton,
                        'name': 'cb'+str(i+1),
                        'wcfg':{'text':str(i+1),
                                'width':5,
                                'fg': color,
                                'activeforeground': color,
                                'variable':self.CurrentLight,
                                'onvalue': i+1, 'offvalue':0,
                                'command':self.selectlight_cb},
                        'gridcfg':{'row':pos[0], 'column':pos[1]}
                        })
            i = i + 1
        idf.append({'widgetType': Tkinter.Checkbutton,
                    'wcfg':{'text': 'Light On/Off',
                            'variable':self.LightOnOff,
                            'onvalue': 1, 'offvalue':0,
                            'command':self.lightOnOff_cb},
                    'gridcfg':{'sticky': 'we', 'columnspan':4,
                               'pady':5}
                    })        
        idf.append({'widgetType':Pmw.ComboBox,
                    'name':'lights',
                    'wcfg':{'labelpos': 'n',
                            'label_text':'Current Light Direction',
                            'scrolledlist_items': self.lights_list,
                            'entryfield_value': self.lights_list[currnum],
                            'selectioncommand':self.makecurrent_cb},
                    'gridcfg':{'sticky':'we', 'columnspan':4, 'pady':10},
                    })

        idf.append({'widgetType':Tkinter.Label,
                    'wcfg': {'text':'Set Current Light Direction:'},
                    'gridcfg':{'sticky':'we', 'columnspan':4, 'pady': 5}
                    })
        ew = 5
        idf.append({'widgetType':Pmw.EntryField,
                    'name':'x',
                    'wcfg':{'labelpos':'w',
                            'label_text':'X: ',
                            'entry_width': ew,
                            'validate':'real',
                            'value':self.currentValues[0]},
                    'gridcfg':{'sticky':'we', 'pady':5}})
        idf.append({'widgetType':Pmw.EntryField,
                    'name':'y',
                    'wcfg':{'labelpos':'w',
                            'label_text':'Y: ',
                            'entry_width': ew,
                            'validate':'real',
                            'value':self.currentValues[1]},
                    'gridcfg':{'row':-1, 'sticky':'we', 'pady':5}})
        idf.append({'widgetType':Pmw.EntryField,
                    'name':'z',
                    'wcfg':{'labelpos':'w',
                            'label_text':'Z: ',
                            'entry_width': ew,
                            'validate':'real',
                            'value':self.currentValues[2]},
                    'gridcfg':{'row':-1, 'sticky':'we', 'pady':5}})
        
        idf.append({'widgetType':Tkinter.Button,
                    'wcfg': {'text': 'Set',
                             'relief' : Tkinter.RAISED,
                             'borderwidth' : 4,
                             'width':7,
                             'command':self.set_direction_cb},
                    'gridcfg':{'row':-1, 'sticky':'we', 'pady':5},
                    })

        idf.append({'widgetType':Tkinter.Button,
                    'wcfg':{'text': '  Dismiss   ',
                            'relief' : Tkinter.RAISED,
                            'borderwidth' : 3,
                            'command':self.dismiss_cb},
                    'gridcfg':{'sticky':'we', 'columnspan':4},
                    })
        self.form = self.vf.getUserInput(idf, modal = 0, blocking=0)
        self.idf.entryByName['lights']['widget'].selectitem(currnum)





class VLILight(Light):
    """Command to add, remove and change the direction of
    one or more VLI light sources. """

    def addLight(self):
        volGeom.context.AddLight(volGeom.currLight)

    def removeLight(self):
        volGeom.context.RemoveLight(volGeom.currLight)

    def setVolRenLightDirection(self, x, y, z):
        from Volume.Renderers.VLI.vli import VLIVector3D
        vec = VLIVector3D(x, y, z)
        volGeom.currLight.SetDirection(vec)

    def SetIntensity(self, val):
        #print "intensity:", val, type(val)
        status = volGeom.currLight.SetIntensity(val)
        from Volume.Renderers.VLI.vli import kVLIOK
        if status != kVLIOK:
            print "SetIntensity() Error; Status: ", status

    def __call__(self, **kw):
        """None <- Light(**kw), where kw can be:
        select = num --- to select a light source; num  is an integer in
        range (1 ... 8);
        switch = mode -- to turn current light on or off;
                         mode is either 'on' or 'off';
        direction=(x,y,z) -- to specify new direction of current light;
        """
        
        if not volGeom.context:
            print "Context has not been created"
            return
        apply(self.doitWrapper, (), kw)

    def checkDependencies(self):
          from Volume.Renderers.VLI import vli

        

commandGUI = CommandGUI()
menuName = 'Light'
menuIndex = 8
commandNames = {'vli': "VLILight", 'utvolren': None}
commandList = [{'name':'', 'cmd':None,'gui': commandGUI}]

def initModule(viewer):
    for dict in commandList:
        if dict['name']:
            viewer.addCommand(dict['cmd'], dict['name'], dict['gui'])
