# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is the Python Computer Graphics Kit.
#
# The Initial Developer of the Original Code is Matthias Baas.
# Portions created by the Initial Developer are Copyright (C) 2004
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
# $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
from _core import ISlot, IArraySlot

# 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
