########################################################################
#
# File Name:            NamedExpressions.py
#
#
"""
Versa variables
WWW: http://4suite.org/4RDF        e-mail: support@4suite.org

Copyright 2001-2002 Fourthought Inc, USA.
See  http://4suite.org/COPYRIGHT  for license and copyright information
"""


import types
import DataTypes, CoreFunctions
from Ft.Xml.Lib.XmlString import XmlStrStrip, SplitQName
#FIXME: Move to Versa exception
from Ft.Rdf import RDF_MS_BASE, RDF_SCHEMA_BASE, OBJECT_TYPE_RESOURCE, OBJECT_TYPE_UNKNOWN
from Ft.Rdf.Parsers.Versa import g_extFunctions, RuntimeException


class VariableReference:
    """
    A variable reference
    """ 
    def __init__(self, expr):
        self.expr = expr
        return

    def evaluate(self, con):
        from Ft.Xml.XPath import Util
        expanded = Util.ExpandQName(self.expr, namespaces=con.nsMapping)
        try:
            return con.varBindings[expanded]
        except KeyError:
            raise RuntimeException(RuntimeException.UNDEFINED_VARIABLE,
                                   expanded[0], expanded[1])


def FunctionCallExprConstructor(name, args=None):
    args = args or []
    name = XmlStrStrip(name)
    key = SplitQName(name)
    count = len(args)
    if count == 0:
        return FunctionCall(name, key, args)
    if count == 1:
        return FunctionCall1(name, key, args)
    if count == 2:
        return FunctionCall2(name, key, args)
    if count == 3:
        return FunctionCall3(name, key, args)
    return FunctionCallN(name, key, args)


class FunctionCall:
    def __init__(self, name, key, args):
        self._name = name
        self._key = key
        self._args = args
        self._func = None
        return

    def pprint(self, indent=''):
        print indent + str(self)
        for arg in self._args:
            arg.pprint(indent + '  ')

    def error(self, *args):
        raise Exception('Unknown function call: %s' % self._name)

    def evaluate(self, context):
        """Call the function"""
        if not self._func:
            (prefix, local) = self._key
            if prefix:
                try:
                    expanded = (context.processorNss[prefix], local)
                except:
                    raise RuntimeException(RuntimeException.UNDEFINED_PREFIX, prefix)
            else:
                expanded = self._key
            self._func = (g_extFunctions.get(expanded) or
                          CoreFunctions.FUNCTIONS.get(expanded, self.error))
        try:
            result = self._func(context)
        except TypeError, e:
            if _IsExceptionForImmediatelyCalledFunc(e, self._func):
                raise RuntimeException(RuntimeException.WRONG_ARGUMENTS, str(expanded), '')
            raise
        return result

    def __getinitargs__(self):
        return (self._name, self._key, self._args)

    def __getstate__(self):
        state = vars(self).copy()
        del state['_func']
        return state

    def __str__(self):
        return '<%s at %x: %s>' % (self.__class__.__name__, id(self), repr(self))

    def __repr__(self):
        result = self._name + '('
        if len(self._args):
            result = result + repr(self._args[0])
            for arg in self._args[1:]:
                result = result + ', ' + repr(arg)
        return result + ')'


class FunctionCall1(FunctionCall):
    def __init__(self, name, key, args):
        FunctionCall.__init__(self, name, key, args)
        self._arg0 = args[0]
        return

    def evaluate(self, context):
        arg0 = self._arg0.evaluate(context)
        if not self._func:
            (prefix, local) = self._key
            if prefix:
                try:
                    expanded = (context.processorNss[prefix], local)
                except:
                    raise RuntimeException(RuntimeException.UNDEFINED_PREFIX, prefix)
            else:
                expanded = self._key
            self._func = (g_extFunctions.get(expanded) or
                          CoreFunctions.FUNCTIONS.get(expanded, self.error))
        try:
            result = self._func(context, arg0)
        except TypeError, e:
            if _IsExceptionForImmediatelyCalledFunc(e, self._func):
                raise RuntimeException(RuntimeException.WRONG_ARGUMENTS, str(expanded), '')
            raise
        return result


class FunctionCall2(FunctionCall):
    def __init__(self, name, key, args):
        FunctionCall.__init__(self, name, key, args)
        self._arg0 = args[0]
        self._arg1 = args[1]
        return

    def evaluate(self, context):
        arg0 = self._arg0.evaluate(context)
        arg1 = self._arg1.evaluate(context)
        if not self._func:
            (prefix, local) = self._key
            if prefix:
                try:
                    expanded = (context.processorNss[prefix], local)
                except:
                    raise RuntimeException(RuntimeException.UNDEFINED_PREFIX, prefix)
            else:
                expanded = self._key
            self._func = (g_extFunctions.get(expanded) or
                          CoreFunctions.FUNCTIONS.get(expanded, self.error))
        try:
            result = self._func(context, arg0, arg1)
        except TypeError, e:
            if _IsExceptionForImmediatelyCalledFunc(e, self._func):
                raise RuntimeException(RuntimeException.WRONG_ARGUMENTS, str(expanded), '')
            raise
        return result


class FunctionCall3(FunctionCall):
    def __init__(self, name, key, args):
        FunctionCall.__init__(self, name, key, args)
        self._arg0 = args[0]
        self._arg1 = args[1]
        self._arg2 = args[2]
        return

    def evaluate(self, context):
        arg0 = self._arg0.evaluate(context)
        arg1 = self._arg1.evaluate(context)
        arg2 = self._arg2.evaluate(context)
        if not self._func:
            (prefix, local) = self._key
            if prefix:
                try:
                    expanded = (context.processorNss[prefix], local)
                except:
                    raise RuntimeException(RuntimeException.UNDEFINED_PREFIX, prefix)
            else:
                expanded = self._key
            self._func = (g_extFunctions.get(expanded) or
                          CoreFunctions.FUNCTIONS.get(expanded, self.error))
        try:
            result = self._func(context, arg0, arg1, arg2)
        except TypeError, e:
            if _IsExceptionForImmediatelyCalledFunc(e, self._func):
                raise RuntimeException(RuntimeException.WRONG_ARGUMENTS, str(expanded), '')
            raise
        return result


class FunctionCallN(FunctionCall):
    def __init__(self, name, key, args):
        FunctionCall.__init__(self, name, key, args)
        return

    def evaluate(self, context):
        args = [context] + map(lambda x, c=context:
                               x.evaluate(c),
                               self._args)
        if not self._func:
            (prefix, local) = self._key
            if prefix:
                try:
                    expanded = (context.processorNss[prefix], local)
                except:
                    raise RuntimeException(RuntimeException.UNDEFINED_PREFIX, prefix)
            else:
                expanded = self._key
            self._func = (g_extFunctions.get(expanded) or
                          CoreFunctions.FUNCTIONS.get(expanded, self.error))
        try:
            result = apply(self._func, args)
        except TypeError, e:
            if _IsExceptionForImmediatelyCalledFunc(e, self._func):
                raise RuntimeException(RuntimeException.WRONG_ARGUMENTS, str(expanded), '')
            raise
        return result


def _IsExceptionForImmediatelyCalledFunc(e, f):
    i = str(e).find('(')
    called_function_name = str(f).split()[1]
    if i != -1 and str(e)[:i] == called_function_name:
        return 1
    return 0
    
    
