from orangescape.model.engine.engineconstants import *
from exFunctions import getNumber, getString, validateOperand, getConvertedNumber
from orangescape.model.engine.util.NumericUtil import is_a_number
from orangescape.model.engine.exception.Error import InstanceNotFoundError, FormulaArgumentError, NumberFormatError, FormulaExecutionError
from orangescape.model.engine.exception.ErrorMessage import Engine2023, Engine2025, Engine2026
from orangescape.model.server.Interface import BaseNode
import logging
import datetime
from orangescape.model.server.util.ServerUtil import __resolve_search_criteria__, getFormula, getFormulaContainer, getFormulaContainerWithSheet
from orangescape.model.server.util.metadatautil import sheetContainsCell, getDynamicFormulaNode,getRootBranch
import appconfig as config
from orangescape.model.server.PerfMonitor import stopPerfMonitor, startSessionFormulaMonitor

def cellinfo(cell):
    return '%s.%s'%(cell.getRowMetadata().getSheetMetadata().getSheetName(), cell)

class ExecutionTree:
    tree = None
    nodeList = None
    session = None
    def __init__(self, session, oldReferenceChanges = None):
        self.tree = dict()
        self.nodeList = list()
        self.session = session
        self.referenceChanges = dict()
        self.oldReferenceChanges = oldReferenceChanges

    def getSession(self):
        return self.session

    def execute(self):
        for cell in self.nodeList:
            try:
                if len(cell.precedentNodeList) == 0:
                    """Double check if an independent formula get added twice"""
                    cell.execute();
            except:
                msg = 'Error while executing the cell %s(%s)(%s) in the sheet %s '%(cell.getCellMetadata().getCellDisplayName(), cell.getCellMetadata().getCellName(), cell.getCellMetadata().getSheetId(), cell.getCellMetadata().getRowMetadata().getSheetMetadata().getSheetName())
                logging.error(msg)
                raise
        return self.referenceChanges

    def addPrivateCell(self, sheet, cell, refCellChange=False):
        if cell.isFormula():
            self.addIndependentFormula(sheet, cell, refCellChange)
        else:
            if cell.isNamedCell():
                self.addInputNode(sheet, cell, sheet.get(cell), refCellChange)
            else:
                self.addInputNode(sheet, cell, cell.getCellValue(), refCellChange)

    def addIndependentFormula(self, sheet, formulaCell, refCellChange=False):
        tMap = self.tree.get(sheet.getSheetId())
        if tMap is None:
            tMap = dict()
            self.tree[sheet.getSheetId()] = tMap
        fc = tMap.get(formulaCell.getSheetId())
        if fc is None:
            fc = FormulaCell(sheet, formulaCell, self, refCellChange)
            tMap[formulaCell.getSheetId()] = fc
            self.nodeList.append(fc)

    def createFormulaNode(self, sheet, formulaCell, refCellChange=False):
        tMap = self.tree.get(sheet.getSheetId())
        if tMap is None:
            tMap = dict()
            self.tree[sheet.getSheetId()] = tMap
        fc = tMap.get(formulaCell.getSheetId())
        if fc is None:
            fc = FormulaCell(sheet, formulaCell, self, refCellChange)
            tMap[formulaCell.getSheetId()] = fc
        return fc

    def addInputNode(self, sheet, inputCell, value, refCellChange=False):
        tMap = self.tree.get(sheet.getSheetId())
        if tMap is None:
            tMap = dict()
            self.tree[sheet.getSheetId()] = tMap
        ipCell = tMap.get(inputCell.getSheetId())
        if ipCell is None:
            ipCell = InputCell(sheet, inputCell, value, self, refCellChange)
            tMap[inputCell.getSheetId()] = ipCell
            self.nodeList.append(ipCell)
        else:
            "Overriding the previously added input node"
            ipCell.overRideInputValue(value)

    def executeFormula(self, formula, target):
        rootNode = formula.getNodeList()[0]
        try:
            return self.executeNode(rootNode, target)
        except Exception, e:
            msg = Engine2025%(target.getSheetMetadata().getSheetName(), getFormulaContainer(formula), target.getSheetName())
            logging.error(msg)
            raise

    def executeNode(self, node, sheet):
        if hasattr(self, 'execute'+node.getNodeType()):
            result = getattr(self, 'execute'+node.getNodeType())(node, sheet)
        else:
            raise Exception('Execution for '+node.getNodeType()+' Not Implemented ')
        return result

    def executeConstant(self, node, sheet):
        if node.isString():
            return node.getNodeValue()
        if node.getNodeFloatValue() is not None:
            return node.getNodeFloatValue()
        """For backword compatability"""
        return getConvertedNumber(node.getNodeValue())

    def executeCellReference(self, node, sheet):
        return sheet.get(node.getCellMetadata())

    def execute3dCellReference(self, node, sheet):
        return self.executeCellReference(node, sheet)

    def executeIf(self, node, sheet):
        nodeList = node.getNodeList()
        conditionResult = self.executeNode(nodeList[0], sheet)
        if conditionResult:
            if len(nodeList)>1:
                return self.executeNode(nodeList[1], sheet)
            return 1.0
        if len(nodeList)>2:
            return self.executeNode(nodeList[2], sheet)
        return 0.0

    def executeDSEARCH(self, node, sheet):
        nodeList = node.getNodeList()

        if nodeList[0].getNodeType() == CONST:
            i = 0
            logging.warn('DSEARCH function changed. Remove the first and second argument %s'%(getFormulaContainerWithSheet(getFormula(node))))
        else:
            i = 2
        criteriaNode = self.getCriteriaNode(node.getNodeList()[2-i], sheet)
        if config.env != config.SL and logging.getLogger().level == logging.DEBUG:
            logging.debug('Executing DSEARCH %s for the instance %s'%(getFormulaContainerWithSheet(getFormula(node)), sheet.getSheetId()))

        orderBy=reverse=None
        if len(nodeList) > 3-i:
            orderBy = nodeList[3-i].getCellMetadata()
            reverse = False
            if len(nodeList) > 4-i:
                reverse = self.executeNode(nodeList[4-i], sheet)
                try:
                    reverse = int(reverse)
                except Exception, e:
                    reverse = False

            if not orderBy:
                logging.error('Arg %s. Invalid sort order for DSEARCH %s'%(4-i, getFormulaContainerWithSheet(getFormula(node))))
            else:
                orderBy = orderBy.getSheetId()

        searchList = __resolve_search_criteria__(self.session, sheet, criteriaNode, 2, orderBy, reverse)

        if config.env != config.SL and logging.getLogger().level == logging.DEBUG:
            logging.debug('DSEARCH Query Results: %s'%([instance.getSheetId() for instance in searchList]))

#        logging.info('Executing DSEARCH %s for the instance %s. Result :%s'%(getFormulaContainerWithSheet(getFormula(node)), sheet.getSheetId(), [instance.getSheetId() for instance in searchList]))

        while len(searchList)>0:
            searchSheet = searchList.pop(0)
            if searchSheet.getSheetId() != sheet.getSheetId():
                if i==0:
                    return self.executeNode(node.getNodeList()[1], searchSheet)
                else:
                    return searchSheet.getSheetId()
        return None

    def executeFunction(self, node, sheet):
        opList = list()
        result = None
        if node.getNodeValue()=='IF':
            return self.executeIf(node, sheet)
        if node.getNodeValue()=='CURRENTUSER':
            return self.session.getUser().getSheetId()
        nodeList = node.getNodeList()
        for opNode in nodeList:
            opValue = self.executeNode(opNode, sheet)
            opList.append(opValue)
        if node.getNodeValue()=='EVAL':
            return self.executeEval(sheet, opList)
        category = validateOperand(node, opList)
        pm =startSessionFormulaMonitor({'name':'Formula','sheetName':sheet.getSheetMetadata().getSheetName(),'type':'Formula','inputValues':opList,'snapshot':None},node.getFormula())
        result = self.triggerFunction(category, node.getNodeValue(), opList)
        stopPerfMonitor(pm, result)
        return result

    def getCriteriaNode(self, criteriaNode, sheet):
        rootNode = BaseNode()
        self.resolveRHS(criteriaNode, sheet, rootNode)
        return rootNode

    def resolveRHS(self, criteriaNode, sheet, rootNode):
        if criteriaNode.getNodeType()==CELL_REF:
            rootNode.nodeType = CONST
            rootNode.argNo = criteriaNode.getArgNo()
            rootNode.str = criteriaNode.getCellMetadata().isStr()
            if rootNode.str:
                rootNode.nodeValue = self.executeCellReference(criteriaNode, sheet)
            else:
                rootNode.nodeFloatValue = self.executeCellReference(criteriaNode, sheet)

        else:
            rootNode.str = criteriaNode.isString()
            rootNode.nodeValue = criteriaNode.getNodeValue()
            rootNode.nodeFloatValue = criteriaNode.getNodeFloatValue()
            rootNode.argNo = criteriaNode.getArgNo()
            rootNode.nodeType = criteriaNode.getNodeType()
            rootNode.cellMetadata = criteriaNode.getCellMetadata()
            if criteriaNode.getNodeType()==FUNCTION:
                for node in criteriaNode.getNodeList():
                    cNode = BaseNode()
                    cNode.node = rootNode
                    rootNode.nodeList.append(cNode)
                    self.resolveRHS(node, sheet, cNode)

    def executeEval(self, sheet, argList):
        evalString = argList.pop(0)
        if not evalString:
            return None
        formulaString = evalString

        i = len(argList)-1
        while i>= 0:
            value = argList[i]
            if value is None or isinstance(value, str) or isinstance(value, unicode):
                val = '"'+(value or '')+'"'
            else:
                val = value or 0.0
            formulaString = formulaString.replace(':%d'%(i+1), str(val))
            i-=1

        dynNode = getDynamicFormulaNode(sheet, formulaString)
        return self.executeNode(dynNode, sheet)

    def triggerFunction(self, category, functionName, argList):
        path = 'orangescape.model.engine.execution.'
        path = path + category
        if config.env==config.SL:
            if hasattr(__import__(category, globals(), locals(), ['']), functionName):
                result = getattr(__import__(category, globals(), locals(), ['']), functionName)(argList)
            else:
                raise Exception('Function '+functionName+' is not implemented')
        else:
            if hasattr(__import__(path, globals(), locals(), ['']), functionName):
                result = getattr(__import__(path, globals(), locals(), ['']), functionName)(argList)
            else:
                raise Exception('Function '+functionName+' is not implemented')
        return result

    def evaluateAggrigate3DFunction(self, sheet, referenceCell, aggNode, fnNode, criteria, orderBy, reverse):
        if criteria:
            childList = self.session.filter(sheet, referenceCell, criteria)
        else:
            childList = self.session.getList(sheet, referenceCell)

        if len(childList) > 500:
            formulaContainer = getFormulaContainerWithSheet(getFormula(fnNode))
            if referenceCell.isCascade():
                action = 'Removing Connect as Parent for the reference cell :%s'%(cellinfo(referenceCell))
            else:
                action = 'Removing the Sync TRUE in the formula %s %s'%(fnNode.getNodeValue(), formulaContainer)
            args = (len(childList), fnNode.getNodeValue(), formulaContainer, action)
            msg = """
This transaction is loading %s objects in memory which will cause Serious Performance & Memory overhead.
\tOffending Transaction :\t%s formula in %s
Consider %s
"""%args
            logging.warn(msg)

        if orderBy:
            childList.sort(lambda x, y:cmp(x.get(orderBy), y.get(orderBy)), reverse=reverse)
        valueList = [self.executeNode(aggNode, child) for child in childList]

        functionName = fnNode.getNodeValue()
        category = validateOperand(fnNode, valueList)
        if category == 'Database':
            pm =startSessionFormulaMonitor({'name':'Formula','sheetName':sheet.getSheetMetadata().getSheetName(),'inputValues':valueList,'snapshot':functionName},fnNode.getFormula())
        result = self.triggerFunction(category, fnNode.getNodeValue(), valueList)
        if category == 'Database':
                stopPerfMonitor(pm, result)
        return result

    def executeAggrigate3DFunction(self, fnNode, sheet):
        if fnNode.getNodeValue()=='CRITERIA':
            raise FormulaExecutionError, Engine2026% (getFormulaContainerWithSheet(getFormula(fnNode)))
        if fnNode.getNodeValue()=='DFIND':
            result = self.executeDFIND(fnNode, sheet)
            return result
        valueList = list()
        nodeList = fnNode.getNodeList()
        refCellNode = nodeList[0]
        aggNode = nodeList[1]

        criteria = orderBy = reverse = None
        if len(nodeList)>2:
            criteriaNode = nodeList[2]
            if criteriaNode.getNodeType()==CONST:
                if criteriaNode.getNodeFloatValue()==0:
                    result = self.triggerFunction('Database', fnNode.getNodeValue(), [])
                    return result
            else:
                rootNode = BaseNode()
                self.resolveRHS(criteriaNode, sheet, rootNode)
                criteria = rootNode

        if len(nodeList)>4:
            orderBy = nodeList[4].getCellMetadata()
            reverse = False
            if len(nodeList)>5:
                reverse = self.executeNode(nodeList[5], sheet)
                try:
                    reverse = int(reverse)
                except Exception, e:
                    reverse = False
            if not orderBy:
                logging.error('Arg %s. Invalid sort order for function %s %s'%(5,fnNode.getNodeValue(), getFormulaContainerWithSheet(getFormula(fnNode))))

#        if aggNode.getCellMetadata() and not refCellNode.getCellMetadata().isCascade() and not fnNode.getNodeValue()=='DCONCATENATE' and not fnNode.getNodeValue()=='DCOUNT':
#            result = self.session.executeAggregateFunction(self, sheet, refCellNode.getCellMetadata(), aggNode, fnNode, criteria, orderBy, reverse)
#            pm.stop()
#            return result
#        else:
        result = self.evaluateAggrigate3DFunction(sheet, refCellNode.getCellMetadata(), aggNode, fnNode, criteria, orderBy, reverse)
        return result

    def __getParentSheet__(self, sheet, refCellList):
        if len(refCellList)==1:
            return sheet
        if sheet.get(refCellList[0]) is None or str(sheet.get(refCellList[0])).strip()=='':
            return None
        return self.__getParentSheet__(self.session.get(sheet.get(refCellList[0]), refCellList[0].getReferredSheetMetadata()), refCellList[1:])

    def executeDFIND(self, node, sheet):
        nodeList = node.getNodeList()
        if nodeList[0].getNodeType() == CELL_REF3D:
            logging.warn('DFIND function changed. Remove the first argument in the formula %s'%(getFormulaContainerWithSheet(getFormula(node))))
            i = 0
        else:
            i = 1

        refCellList = [node.getCellMetadata() for node in nodeList[5-i:]]
        criteriaNode = nodeList[1-i]
        orderBy = nodeList[2-i].getCellMetadata()
        reverse = self.executeNode(nodeList[3-i], sheet)
        try:
            reverse = int(reverse)
        except Exception, e:
            reverse = False

        referenceCell = refCellList[-1]
        parentSheet = self.__getParentSheet__(sheet, refCellList)
        if parentSheet is None:
            return None
        if criteriaNode.getNodeType()==CONST:
            childList = self.session.getList(parentSheet, referenceCell) if criteriaNode.getNodeFloatValue()>0 else []
        else:
            rootNode = BaseNode()
            self.resolveRHS(criteriaNode, sheet, rootNode)
            childList = self.session.filter(parentSheet, referenceCell, rootNode)

        if orderBy:
            childList.sort(lambda x, y:cmp(x.get(orderBy), y.get(orderBy)), reverse=reverse)
        else:
            logging.error('Arg %s. Invalid sort order for function DFIND %s'%(3-i, getFormulaContainerWithSheet(getFormula(node))))
        if len(childList) > 0:
            if i==0:
                valueList = [self.executeNode(nodeList[0], child) for child in childList]
                return valueList[0]
            else:
                return childList[0].getSheetId()
        return None

    def executeFetch3DFunction(self, fnNode, sheet):
        if fnNode.getNodeValue()=='DSEARCH':
            return self.executeDSEARCH(fnNode, sheet)
        nodeList = fnNode.getNodeList()
        refCellNode = nodeList[0]
        node = nodeList[1]
        try:
            parentId = sheet.get(refCellNode.getCellMetadata())
            if refCellNode.getCellMetadata().getReferredSheetMetadata() is None:
                msg = 'Non-reference cell %s used in DGET %s'%(refCellNode.getCellMetadata(), getFormulaContainerWithSheet(getFormula(fnNode)))
                logging.error(msg)
                raise FormulaArgumentError(msg)
            parentSheetMetadata = refCellNode.getCellMetadata().getReferredSheetMetadata()
            if parentId is None or len(parentId)==0:
#                logging.info('Executing DGET %s for the instance %s. Result is empty since reference cell (%s) value is empty'%(getFormulaContainerWithSheet(getFormula(fnNode)), sheet.getSheetId(), refCellNode.getCellMetadata().getCellName() or refCellNode.getCellMetadata().getCellDisplayName()))
                return None
            parent = self.session.get(parentId, parentSheetMetadata)
#            result = parent.get(node.getCellMetadata())
            result = self.executeNode(node, parent)
#            logging.info('Executing DGET %s for the instance %s. Result :%s'%(getFormulaContainerWithSheet(getFormula(fnNode)), sheet.getSheetId(), result))
            return result
        except InstanceNotFoundError, e:
            return None

class Cell:
    cellMetadata = None
    target = None
    executionTree = None
    sheet = None

    decendentNodeList = None
    precedentNodeList = None
    oldValue = None

    def getCellMetadata(self):
        return self.cellMetadata

    def __init__(self, targ, cell, et, refCellChange=False):
        self.decendentNodeList = list()
        self.precedentNodeList = list()
        self.cellMetadata = cell
        self.executionTree = et
        self.target = targ

        dNodeList = cell.getDescendentNodeList()
        for dNode in dNodeList:
            dCell = dNode.getCellMetadata()
            nodeType = dNode.getNodeType()
            try:
                if nodeType.__eq__(FUNCTION) and not refCellChange:
                    self.createFormulaCell(self.target, dCell, et)
                elif nodeType.__eq__(AGG3DFUNC):
                    self.createAgg3DFormula(targ, dCell, et, dNode)
                elif nodeType.__eq__(FETCH3DFUNC) and not refCellChange:
                    self.createFetch3DFormula(targ, dCell, et, dNode)
            except Exception, e:
                dCellInfo = '%s(%s)'%(dCell.getRowMetadata().getSheetMetadata().getSheetName(), dCell.getCellDisplayName())
                cellInfo = '%s(%s)'%(cell.getRowMetadata().getSheetMetadata().getSheetName(), cell.getCellDisplayName())
                logging.error('Error while creating formula cell %s for the dependent cell %s'%(dCellInfo, cellInfo))
                raise

    def createAgg3DFormula(self, targ, dCell, et, dNode):
        executeNode = dNode.isSynchronous()
        if not executeNode:
            executeNode = sum([refCell.isCascade() for refCell in dNode.getReferenceCellMetadata() if refCell.isCascade() is not None])

        if executeNode:
            referenceCell = dNode.getReferenceCellMetadata()[-1]
            parentSheetMetadata = referenceCell.getReferredSheetMetadata()
            foreignKeyValue = targ.get(referenceCell)

            if foreignKeyValue is not None and foreignKeyValue != 'None' and foreignKeyValue != '':
                parentSheet = self.executionTree.getSession().get(foreignKeyValue, parentSheetMetadata)

                if sheetContainsCell(parentSheetMetadata, dCell):
                    self.createFormulaCell(parentSheet, dCell, et)
                else:
                    childRefCellList = dNode.getReferenceCellMetadata()[:-1]
                    childRefCellList.reverse()
                    self.__create_agg_formulacell__(parentSheet, dCell, childRefCellList, et)

    def __create_agg_formulacell__(self, sheet, dCell, refCellList, et):

        for childSheet in self.executionTree.getSession().getList(sheet, refCellList[0]):
            if len(refCellList)==1:
                self.createFormulaCell(childSheet, dCell, et)
            else:
                self.__create_agg_formulacell__(childSheet, dCell, refCellList[1:], et)

    def createFetch3DFormula(self, targ, dCell, et, dNode):
        referenceCell = dNode.getReferenceCellMetadata()[0]
        if referenceCell.getSheetId().__eq__(self.cellMetadata.getSheetId()):
            self.createFormulaCell(targ, dCell, et)
        else:
            executeNode = dNode.isSynchronous()
            if not executeNode:
                executeNode = referenceCell.isCascade()
            if executeNode:
                childSheets = et.getSession().getList(targ, referenceCell)
                if len(childSheets) > 500:
                    if referenceCell.isCascade():
                        action = 'Removing Connect as Parent for the reference cell :%s'%(cellinfo(referenceCell))
                    else:
                        action = 'Removing the Sync TRUE in the DGET in the cell :%s'%(cellinfo(dCell))
                    args = (len(childSheets), cellinfo(dCell), cellinfo(self.cellMetadata), action)
                    msg = """
This transaction is loading %s objects in memory which will cause Serious Performance & Memory overhead.
\tOffending Transaction :\tDGET formula in %s
\tCaused By                 :\t%s
Consider %s
"""%args
                    logging.warn(msg)
#                    logging.warn('\nExecuting the formula in the cell %s(%s) in the model %s is loading %s objects. This will cause serious performance and memory over head.\nYou might have to check and %s.'%args)
#                    if referenceCell.isCascade():
#                        logging.warn('%s Parent-Child DGET in %s refcell %s triggered by %s from instance %s'%(thread.get_ident(), cellinfo(dCell), referenceCell.getCellName() or referenceCell.getCellDisplayName(), cellinfo(self.cellMetadata), targ.getSheetId()))
#                    else:
#                        logging.warn('%s Sync True DGET in %s refcell %s  triggered by %s from instance %s'%(thread.get_ident(), cellinfo(dCell), referenceCell.getCellName() or referenceCell.getCellDisplayName(), cellinfo(self.cellMetadata), targ.getSheetId()))
                for sheet in childSheets:
                    self.createFormulaCell(sheet, dCell, et)

    def createFormulaCell(self, sheet, formulaCell, et):
        fc = et.createFormulaNode(sheet, formulaCell)
        self.decendentNodeList.append(fc)
        fc.precedentNodeList.append(self)

    def execute(self):
        cellMap = self.executionTree.tree.get(self.getSheetId())
        if cellMap is None and len(cellMap)==0:
            raise Exception(self.getCellMetadata()+" not registered for "+self.target)
        fc = cellMap.get(self.getCellMetadata().getSheetId())
        if fc is None:
            raise Exception(self.getCellMetadata().getSheetId()+" is not registered for "+self.getCellMetadata().getSheetId())
        if self == fc:
            result = self.evaluate()

            self.oldValue = self.target.get(self.getCellMetadata())

            if self.oldValue != result:
                if self.cellMetadata.isStr():
                    if result is None:
                        pass
                    elif isinstance(result, float):
                        pass
                    elif isinstance(result, str) and result == '':
                        result = None
                    self.target.set(self.getCellMetadata(), result)
                else:
                    if result is None:
                        self.target.set(self.getCellMetadata(), None)
                    else:
                        if not is_a_number(result):
                            raise NumberFormatError, Engine2023% (result, self.cellMetadata, self.target.getSheetMetadataName())
                        self.target.set(self.getCellMetadata(), float(result) or None)

                self.executionTree.getSession().put(self.target)

                if result and self.getCellMetadata().isReferenceCell():
                    foreignKeyValue = result
                    if not self.executionTree.oldReferenceChanges or (not self.executionTree.oldReferenceChanges.__contains__(self.target.getSheetId())):
                        instnceCellList = self.executionTree.referenceChanges.get(self.target.getSheetId())
                        if instnceCellList is None:
                            instnceCellList = list()
                            self.executionTree.referenceChanges[self.target.getSheetId()] = instnceCellList
                        if not instnceCellList.__contains__(self.getCellMetadata()):
                            instnceCellList.append(self.getCellMetadata())
                        else:
                            raise Exception('Cell %s is executing more then once for the instance %s '%(self.target.getSheetMetadata(), self.target.getSheetId()))

            for des in self.decendentNodeList:
                des.precedentNodeList.remove(self)
                if len(des.precedentNodeList) == 0:
                    des.execute()
        else:
            raise Exception("Unknown "+fc.getCellMetadata().getSheetId()+" defined for "+self.getCellMetadata().getSheetId()+" for "+self.getCellMetadata().getSheetId()+'---'+id(self)+'--'+id(fc))

    def getSheetId(self):
        return self.target.getSheetId()

    def evaluate(self):
        raise Exception('Not Implemented error')

class InputCell(Cell):

    inputValue = None
    def __init__(self, targ, cell, val, et, refCellChange=False):
        self.inputValue = val
        Cell.__init__(self, targ, cell, et, refCellChange)

    def evaluate(self):
        return self.inputValue

    def getInputValue(self):
        return self.inputValue

    def overRideInputValue(self, value):
        self.inputValue = value

class FormulaCell(Cell):

    def __init__(self, targ, cell, et, refCellChange=False):
        Cell.__init__(self, targ, cell, et, refCellChange)

    def evaluate(self):
        farmula = self.cellMetadata.getFormulaList()[0]
        res = self.executionTree.executeFormula(farmula, self.target)
        return res

    def getInputValue(self):
        raise Exception("Formula cell doesn't have input value")
