###########################################################################
# cgkit - Python Computer Graphics Kit
# Copyright (C) 2004 Matthias Baas (baas@ira.uka.de)
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# cgkit homepage: http://cgkit.sourceforge.net
###########################################################################
# $Id: slots.py,v 1.7 2005/08/28 19:55:40 mbaas Exp $

## \file slots.py
## Contains the standard slot classes.

#from _core import ISlot, DoubleSlot, IntSlot, BoolSlot, PySlot
#from _core import Vec3Slot, Vec4Slot, Mat3Slot, Mat4Slot
from cgtypes import vec3, vec4, mat3, mat4, quat
import _core
from _core import Dependent, UserSizeConstraint, LinearSizeConstraint

# Factory functions for the individual slots:

def DoubleSlot(value=0.0, flags=0):
    return _core.DoubleSlot(value, flags)

def IntSlot(value=0, flags=0):
    return _core.IntSlot(value, flags)

def BoolSlot(value=False, flags=0):
    return _core.BoolSlot(value, flags)

def StrSlot(value="", flags=0):
    return _core.StrSlot(value, flags)

def PySlot(value=None, flags=0):
    return _core.PySlot(value, flags)

def Vec3Slot(value=vec3(), flags=0):
    return _core.Vec3Slot(value, flags)

def Vec4Slot(value=vec4(), flags=0):
    return _core.Vec4Slot(value, flags)

def Mat3Slot(value=mat3(), flags=0):
    return _core.Mat3Slot(value, flags)

def Mat4Slot(value=mat4(), flags=0):
    return _core.Mat4Slot(value, flags)

def QuatSlot(value=quat(), flags=0):
    return _core.QuatSlot(value, flags)



def DoubleArraySlot(multiplicity=1, constraint=None):
    return _core.DoubleArraySlot(multiplicity, constraint)

def IntArraySlot(multiplicity=1, constraint=None):
    return _core.IntArraySlot(multiplicity, constraint)

def BoolArraySlot(multiplicity=1, constraint=None):
    return _core.BoolArraySlot(multiplicity, constraint)

def StrArraySlot(multiplicity=1, constraint=None):
    return _core.StrArraySlot(multiplicity, constraint)

def Vec3ArraySlot(multiplicity=1, constraint=None):
    return _core.Vec3ArraySlot(multiplicity, constraint)

def Vec4ArraySlot(multiplicity=1, constraint=None):
    return _core.Vec4ArraySlot(multiplicity, constraint)

#def Mat3ArraySlot(multiplicity=1, constraint=None):
#    return _core.Mat3ArraySlot(multiplicity, constraint)

def Mat4ArraySlot(multiplicity=1, constraint=None):
    return _core.Mat4ArraySlot(multiplicity, constraint)

#def QuatArraySlot(multiplicity=1, constraint=None):
#    return _core.QuatArraySlot(multiplicity, constraint)


# Test

class ProceduralDoubleSlot(_core.DoubleSlot):
    def __init__(self, proc):
        _core.DoubleSlot.__init__(self, 0.0, 2) # 2 = NO_INPUT_CONNECTIONS
        self._proc = proc

    def computeValue(self):
        self._value = self._proc()

class ProceduralIntSlot(_core.IntSlot):
    def __init__(self, proc):
        _core.IntSlot.__init__(self, 0, 2) # 2 = NO_INPUT_CONNECTIONS
        self._proc = proc

    def computeValue(self):
        self._value = self._proc()

class ProceduralVec3Slot(_core.Vec3Slot):
    def __init__(self, proc):
        _core.Vec3Slot.__init__(self, vec3(), 2) # 2 = NO_INPUT_CONNECTIONS
        self._proc = proc

    def computeValue(self):
        self._value = self._proc()

class ProceduralVec4Slot(_core.Vec4Slot):
    def __init__(self, proc):
        _core.Vec4Slot.__init__(self, vec4(), 2) # 2 = NO_INPUT_CONNECTIONS
        self._proc = proc

    def computeValue(self):
        self._value = self._proc()

class ProceduralMat3Slot(_core.Mat3Slot):
    def __init__(self, proc):
        _core.Mat3Slot.__init__(self, mat3(1), 2) # 2 = NO_INPUT_CONNECTIONS
        self._proc = proc

    def computeValue(self):
        self._value = self._proc()

class ProceduralMat4Slot(_core.Mat4Slot):
    def __init__(self, proc):
        _core.Mat4Slot.__init__(self, mat4(1), 2) # 2 = NO_INPUT_CONNECTIONS
        self._proc = proc

    def computeValue(self):
        self._value = self._proc()

class ProceduralQuatSlot(_core.QuatSlot):
    def __init__(self, proc):
        _core.QuatSlot.__init__(self, quat(), 2) # 2 = NO_INPUT_CONNECTIONS
        self._proc = proc

    def computeValue(self):
        self._value = self._proc()


# NotificationForwarder
class NotificationForwarder(_core.Dependent):
    """Forwards slot notifications.

    This class has the same functionality than the C++ class with the
    same name. However, it's not wrapping the C++ class but is pure
    Python.
    """
    def __init__(self, onvalchanged, onresize=None):
        """Constructor.

        If the forwarder is used with an array slot, the onvalchanged
        callback must take two arguments start and end. If it's used with
        a normal slot, then it does not take any arguments.

        \param onvalchanged A callable object that gets called whenever the value changes
        \param onresize A callable object that gets called whenever the size of an array slot changes
        """
        _core.Dependent.__init__(self)
#        self.callable = callable
        self.onvalchanged = onvalchanged
        self.onresize = onresize
        
    def onValueChanged(self, start=None, end=None):
        # Does the call come from a normal slot?
        if start==None:
            self.onvalchanged()
        # or an array slot?
        else:
            self.onvalchanged(start, end)

    def onResize(self, size):
        if self.onresize!=None:
            self.onresize(size)


# slotPropertyCode
def slotPropertyCode(name, slotname=None):
    """Create the code to add a slot property to a class.

    This is a helper function that creates the code string which
    adds a get() method, a set() method and a property to access
    the value of a slot. The resulting string can then be executed
    inside a class definition via an exec statement. The slot itself
    must be present as a variable in the class.

    The returned string looks like this:
    
    \code
    # Property: <name>
    def _get<Name>(self):
        return self.<slotname>.getValue()

    def _set<Name>(self, val):
        self.<slotname>.setValue(val)

    <name> = property(_get<Name>, _set<Name>, None, "<name>")
    \endcode

    In a %component class (or whatever class) the property is created
    as shown in the following example:

    \code
    class Sphere:
        
        exec slotPropertyCode("radius")

        def __init__(self, ...):
            self.radius_slot = ...
    \endcode

    If no slot name is given then the name is assumed to be '<name>_slot'.
    
    \param name (\c str) Name of the new attribute (property)
    \param slotname (\c str) Name of the corresponding slot
    """
    capname = name.capitalize()
    getmethname = "_get%s"%(capname)
    setmethname = "_set%s"%(capname)
    if slotname==None:
        slotname = "%s_slot"%name

    s="""# Property: %s
def %s(self):
    return self.%s.getValue()

def %s(self, val):
    if isinstance(val, _core.Component):
        val.output_slot.connect(self.%s)
    else:
        self.%s.setValue(val)

%s = property(%s, %s, None, "%s")
"""%(name,
     getmethname,slotname,
     setmethname,slotname,slotname,
     name,getmethname,setmethname,capname)
    return s
