from orangescape.model.server.Interface import DescendantNode, BaseNode
from orangescape.model.engine.engineconstants import AGG3DFUNC, FETCH3DFUNC, CELL_REF3D, FUNCTION
from orangescape.model.engine.engineconstants import CONST, CELL_REF, CELL_REF3D, FUNCTION, AGG3DFUNC, FETCH3DFUNC
from orangescape.model.engine.util.enginegrammarLexer import enginegrammarLexer
from orangescape.model.engine.util.enginegrammarParser import enginegrammarParser
from orangescape.model.engine.util.antlr_python_runtime import ANTLRStringStream,CommonTokenStream

def getFormula(node):
    if node.getFormula():
        return node.getFormula()
    if not node.getNode():
        return None
    return getFormula(node.getNode())

def getDecendentCell(node):
    formula = getFormula(node)
    if formula:
        return formula.getCellMetadata()
    return None

def get3dFunctionNode(node):
    if node.getNodeType()==FETCH3DFUNC or node.getNodeType()==AGG3DFUNC:
        return node
    return get3dFunctionNode(node.getNode())

def _resolve_dfind_referencecell_(_3dfnNode, dt):
    _3dfnNodeArgList = _3dfnNode.getNodeList()
    i = 0
    if _3dfnNodeArgList[0].getNodeType() != CELL_REF3D:
        i = 1

    dt['Sync'] = _3dfnNodeArgList[4-i].getNodeFloatValue()
    dt['ReferenceCellMetadata'] = [node.getCellMetadata() for node in _3dfnNodeArgList[5-i:]]

def constructDescendantNodeList(nodeList):
    dNodeList = list()
    for node in nodeList:
        if node:
            dCell = getDecendentCell(node)
            if dCell:
                dt = {'CellMetadata':None, 'NodeType':None, 'ReferenceCellMetadata':None, 'Sync':0.0}
                dt['CellMetadata'] = dCell
                if node.getNodeType()==CELL_REF3D:
                    _3dfnNode = get3dFunctionNode(node)
                    if _3dfnNode.getNodeValue()=='DSEARCH':
                        continue
                    dt['NodeType'] = _3dfnNode.getNodeType()
                    if _3dfnNode.getNodeValue()=='DFIND':
                        _resolve_dfind_referencecell_(_3dfnNode, dt)
                    else:
                        _3dfnNodeArgList = _3dfnNode.getNodeList()
                        dt['ReferenceCellMetadata'] = [_3dfnNodeArgList[0].getCellMetadata()]
                        if _3dfnNode.getNodeType()==FETCH3DFUNC and len(_3dfnNodeArgList)>2:
                            dt['Sync'] = _3dfnNodeArgList[2].getNodeFloatValue()

                        if _3dfnNode.getNodeType()==AGG3DFUNC and len(_3dfnNodeArgList)>3:
                            dt['Sync'] = _3dfnNodeArgList[3].getNodeFloatValue()

                else:
                    dt['NodeType'] = FUNCTION
                dNodeList.append(DescendantNode(dt))
    return dNodeList

def sheetContainsCell(sheetMetadata, searchCell):
    for row in sheetMetadata.getRowMetadataList():
        for cell in row.getCellMetadataList():
            if cell and cell.getSheetId()==searchCell.getSheetId():
                return True
    return False

def getRootBranch(branch):
    if branch.getSheetMetadata():
        return branch
    return getRootBranch(branch.getActivityDefinition().getBranch())

def getRootBranchByName(metadata, name):
        branchList = metadata.getBranchList()
        bList = [branch for branch in branchList if branch.getSheetName()==name]
        if len(bList)>0:
            return bList.pop()
        raise Exception('%s not found in %s'%(name, metadata.getSheetName()))

def getShortCutByName(sheetMetadata, shortCutName):
    shortCutList = [shortCut for shortCut in sheetMetadata.getShortCutList() if shortCut.getSheetName()==shortCutName]
    if len(shortCutList)>0:
        return shortCutList[0]
    return None

def getCellByName(metadata, searchCell):
    for cellName, cell in metadata.getPublicCellMetadataDict().iteritems():
        if searchCell.lower()==cellName.lower():
            return metadata.getPublicCellMetadata(cellName)
    return None

def serializeNode(sheet, rpnList, argNo = 1):
    if len(rpnList) == 0:
        raise Exception, "Insufficient number of arguments for .."
    nodeDict = dict(rpnList.pop())
    pNode = BaseNode(nodeType = nodeDict['nodeType'], argNo = argNo, isStr = nodeDict.get('isStr'), nodeValue = nodeDict.get('value'), nodeFloatValue = nodeDict.get('nodeFloatValue'))

    if nodeDict['nodeType'] in (FUNCTION, AGG3DFUNC, FETCH3DFUNC):
        args = nodeDict.get('arguments')
        for i in range(0, int(args)):
#            print args-i, args, i, nodeDict
            pNode.nodeList.append(serializeNode(sheet, rpnList, args-i))
    return pNode

def parseFormula(formulaString):
    if formulaString[0]=='=':
        formulaString = formulaString[1:]
    char_stream = ANTLRStringStream(formulaString)
    lexer = enginegrammarLexer(char_stream)
    tokens = CommonTokenStream(lexer)
    parser = enginegrammarParser(tokens)
    result = parser.formula()
    return parser.rpnList

def getDynamicFormulaNode(sheet, formulaString):
    return serializeNode(sheet, parseFormula(formulaString))

def searchModelByName(metadataContext, searchModel):
    for modelName in metadataContext.getSheetMetadataDict():
        if modelName.lower() == searchModel.lower():
            return metadataContext.getSheetMetadata(modelName)
    raise MetadataError, Server2002% (searchModel)
    return None