########################################################################
#
# File Name: 	        Expression.py
#
# Documentation:	http://docs.ftsuite.com/4ODS/Expression.py.html
#
"""
Implements the Expression meta-data interface.
WWW: http://4suite.org/4ODS         e-mail: support@4suite.org

Copyright (c) 1999 Fourthought, Inc., USA.   All Rights Reserved.
See  http://4suite.org/COPYRIGHT  for license and copyright information
"""


from Ft.Ods.MetaData import Operand
from Ft.Ods.MetaData import MetaKind
from Ft.Ods.StorageManager.Adapters import Constants

from Ft.Ods import PersistentObject
td = PersistentObject.TupleDefinitions
from Ft.Ods.Exception import FtodsUnsupportedError


class Expression(Operand.Operand):
    def __init__(self,db,data):
        Operand.Operand.__init__(self,db,data,MetaKind.mk_expression)
        if data is None:
            self.__dict__['operator'] = ''


    def value(self):
        #Our override of value
        func = g_operatorMap.get(self.operator)
        if not func:
            raise FtodsUnsupportedError("Operation '%s'" % self.operator)
        return func(self)

    def add_the_operands(self,target,inverse = 1):
        self._4ods_addRelationship('the_operands',Operand.Operand,'operand_in','form',target,inverse)
    form_the_operands = add_the_operands

    def remove_the_operands(self,target,inverse = 1):
        self._4ods_removeRelationship('the_operands','operand_in','drop',target,inverse)
    drop_the_operands=remove_the_operands

    def _and(self):
        if len(self.the_operands) < 2:
            TypeError("not enough arguments for %s expression, expected 2 got %d" % (self.operator,
                                                                                     len(self.the_operands)))
        return self.the_operands[0].value() & self.the_operands[1].value()

    def _or(self):
        if len(self.the_operands) < 2:
            TypeError("not enough arguments for %s expression, expected 2 got %d" % (self.operator,
                                                                                     len(self.the_operands)))
        return self.the_operands[0].value() | self.the_operands[1].value()

    def _xor(self):
        if len(self.the_operands) < 2:
            TypeError("not enough arguments for %s expression, expected 2 got %d" % (self.operator,
                                                                                     len(self.the_operands)))
        return self.the_operands[0].value() ^ self.the_operands[1].value()

    def _not(self):
        if len(self.the_operands) < 1:
            TypeError("not enough arguments for %s expression, expected 1 got %d" % (self.operator,
                                                                                     len(self.the_operands)))
        return ~ self.the_operands[0].value()

    def _paren(self):
        if len(self.the_operands) < 1:
            TypeError("not enough arguments for %s expression, expected 1 got %d" % (self.operator,
                                                                                     len(self.the_operands)))
        return self.the_operands[0].value()

    def _minus(self):
        if len(self.the_operands) < 1:
            TypeError("not enough arguments for %s expression, expected 1 got %d" % (self.operator,
                                                                                     len(self.the_operands)))
        elif len(self.the_operands) < 2:
            left = 0
            right = self.the_operands[0].value()
        else:
            left = self.the_operands[0].value()
            right = self.the_operands[1].value()
            
        return left - right

    def _plus(self):
        if len(self.the_operands) < 1:
            TypeError("not enough arguments for %s expression, expected 1 got %d" % (self.operator,
                                                                                     len(self.the_operands)))
        elif len(self.the_operands) < 2:
            right = self.the_operands[0].value()
            if right < 0:
                return 0.0 - right
            return right
        else:
            left = self.the_operands[0].value()
            right = self.the_operands[1].value()
            
        return left + right


    def _lshift(self):
        if len(self.the_operands) < 2:
            TypeError("not enough arguments for %s expression, expected 1 got %d" % (self.operator,
                                                                                     len(self.the_operands)))

        left = self.the_operands[0].value()
        right = self.the_operands[1].value()
            
        return left << right

    def _rshift(self):
        if len(self.the_operands) < 2:
            TypeError("not enough arguments for %s expression, expected 1 got %d" % (self.operator,
                                                                                     len(self.the_operands)))

        left = self.the_operands[0].value()
        right = self.the_operands[1].value()
            
        return left >> right

    def _mult(self):
        if len(self.the_operands) < 2:
            TypeError("not enough arguments for %s expression, expected 1 got %d" % (self.operator,
                                                                                     len(self.the_operands)))

        left = self.the_operands[0].value()
        right = self.the_operands[1].value()
            
        return left * right

    def _div(self):
        if len(self.the_operands) < 2:
            TypeError("not enough arguments for %s expression, expected 1 got %d" % (self.operator,
                                                                                     len(self.the_operands)))

        left = self.the_operands[0].value()
        right = self.the_operands[1].value()
            
        return left / right

    def _mod(self):
        if len(self.the_operands) < 2:
            TypeError("not enough arguments for %s expression, expected 1 got %d" % (self.operator,
                                                                                     len(self.the_operands)))

        left = self.the_operands[0].value()
        right = self.the_operands[1].value()
            
        return left % right



    def _4ods_getOdl(self,indent):
        if len(self.the_operands) == 1:
            if self.operator == '()':
                return indent + "( %s )" % (self.the_operands[0]._4ods_getOdl(''))
            else:
                return indent + "%s%s" % (self.operator, self.the_operands[0]._4ods_getOdl(''))
        else:
            return indent + "%s %s %s" % (self.the_operands[0]._4ods_getOdl(''),
                                          self.operator,
                                          self.the_operands[1]._4ods_getOdl(''))
            


    _tupleNames =  (('operator',),('the_operands',))

    _tupleDefinitions = {'operator':{td.TYPE:Constants.Types.STRING,
                                     },
                         'the_operands':{td.TYPE:Constants.Types.LIST_COLLECTION,
                                         td.READONLY:1,
                                         td.RELATIONSHIP:1,
                                         td.COLLECTION_SUBTYPE:Constants.Types.ROBJECT,
                                         },
                         }

    _localExtents = ()


g_operatorMap = {'&':Expression._and,
                          '|':Expression._or,
                          '^':Expression._xor,
                          '~':Expression._not,
                          '()':Expression._paren,
                          '-':Expression._minus,
                          '+':Expression._plus,
                          '<<':Expression._lshift,
                          '>>':Expression._rshift,
                          '*':Expression._mult,
                          '/':Expression._div,
                          '%':Expression._mod,
                          }
