import logging
from orangescape.model.engine.engineconstants import FUNCTION, CELL_REF, CELL_REF3D, WORKFLOW, COMMAND, CONST, ACTION, AGG3DFUNC
from orangescape.model.engine.exception.Error import FormulaArgumentError
from orangescape.model.engine.exception.ErrorMessage import Engine2024
from orangescape.model.server.util.metadatautil import getRootBranch
from orangescape.model.server.Constants import PARAM_TYPE_VALUE, PARAM_TYPE_USERINPUT, PARAM_TYPE_MASTERLOOKUP, PARAM_TYPE_CELL, PARAM_TYPE_FORMULA

operFn_operatorDict = {'eqOper':'=', 'ltOper':'<', 'gtOper':'>', 'gtEqOper':'>=', 'notEqOper':'!=', 'ltEqOper':'<='}

def _get_func(fullFuncName):
    lastDot = fullFuncName.rfind(u".")
    funcName = fullFuncName[lastDot + 1:]
    modPath = fullFuncName[:lastDot]
    return getattr(__import__(modPath, globals(), locals(), ['']),funcName)

def _get_class(fullClassName):
    return _get_func(fullClassName)

def getFormula(node):
    return node.getFormula() or getFormula(node.getNode())

def getFormulaContainer(formula):
    if formula.getCellMetadata():
        return 'Cell %s'%(formula.getCellMetadata())
    if formula.getActivityDefinition():
        activity = formula.getActivityDefinition()
        if activity.getProcessType()==COMMAND:
            return 'Command %s'%(activity.getSheetName())
        return 'Workflow definition %s'%(activity.getSheetName())
    if formula.getInputportMetadata():
        activity = formula.getInputportMetadata().getActivityDefinition()
        s = 'Activity '+ activity.getSheetName()
        if activity.getProcessType()==ACTION:
            root = getRootBranch(activity.getBranch())
            s = 'Command %s in the action %s'%(activity.getNodeType(), root.getSheetName())
        return '%s in %s'%(formula.getInputportMetadata().getParamName(), s)
#    return formula.getCellMetadata() or formula.getActivityDefinition() or formula.getBranch() or formula.getInputportMetadata()

def getFormulaContainerWithSheet(formula):
    if formula.getCellMetadata():
        sheetMetadataName = formula.getCellMetadata().getRowMetadata().getSheetMetadata().getSheetName()
        return 'in %s.%s'%(sheetMetadataName, formula.getCellMetadata())
    if formula.getActivityDefinition():
        sheetMetadataName = getRootBranch(formula.getActivityDefinition().getBranch()).getSheetMetadata().getSheetName()
        activity = formula.getActivityDefinition()
        if activity.getProcessType()==COMMAND:
            return 'in the Command \"%s\" of model \"%s\"'%(activity.getSheetName(),sheetMetadataName)
        return 'in the Workflow definition \"%s\" of model \"%s\"'%(activity.getSheetName(),sheetMetadataName)
    if formula.getInputportMetadata():
        activity = formula.getInputportMetadata().getActivityDefinition()
        root = getRootBranch(activity.getBranch())
        sheetMetadataName = root.getSheetMetadata().getSheetName()

        s = 'Activity '+ activity.getSheetName()
        if activity.getProcessType()==ACTION:
            s = 'Command \"%s\" in the action \"%s\"'%(activity.getNodeType(), root.getSheetName())
        return 'in the model \"%s\" in the param \"%s\" in %s'%(sheetMetadataName, formula.getInputportMetadata().getParamName(), s)


def __create_query_filter__(sheet, operatorNode, conditionlist):
    operatorValue = operatorNode.getNodeValue()
    if operFn_operatorDict.__contains__(operatorValue):
        nodeList = operatorNode.getNodeList()
        lhsNode = nodeList[0]
        rhsNode = nodeList[1]
        if lhsNode.getNodeType()==CELL_REF3D:
            searchMetadata = lhsNode.getCellMetadata().getSheetMetadata()
            if rhsNode.isString():
                rhsValue = rhsNode.getNodeValue()
            else:
                rhsValue = rhsNode.getNodeFloatValue()
            conditionlist.append((lhsNode.getCellMetadata().getSheetId(), operFn_operatorDict[operatorNode.getNodeValue()], rhsValue))
            return searchMetadata
        else:
            raise FormulaArgumentError, Engine2024% (sheet.getSheetMetadata().getSheetName(), getFormulaContainer(getFormula(lhsNode)))
#            received = lhsNode.getNodeType()
#            if lhsNode.getNodeType()==FUNCTION:
#                received = 'Function %s'%(lhsNode.getNodeValue())
#            elif lhsNode.getNodeType()==CONST:
#                received = 'Constant %s'%(lhsNode.getNodeValue())
#            raise FormulaArgumentError, Engine2024% (sheet.getSheetMetadata().getSheetName(), getFormulaContainer(getFormula(lhsNode)), received)

def __construct_basequery__(sheet, node, conditionlist):
    if node.getNodeType()==FUNCTION:
        nodeValue = node.getNodeValue()
        if nodeValue=='AND':
            for operatorNode in node.getNodeList():
                if operFn_operatorDict.__contains__(operatorNode.getNodeValue()):
                    searchMetadata = __create_query_filter__(sheet, operatorNode, conditionlist)
                else:
                    raise Exception('Unexpected operator argument %s. Expected =, <>, >, <, <=, >='%(operatorNode.getNodeValue()))
        elif operFn_operatorDict.__contains__(nodeValue):
            searchMetadata = __create_query_filter__(sheet, node, conditionlist)
        else:
            logging.info('Unsupported nodevalue %s in criteria. Expected AND, =, <>, >, <, <=, >= '%(nodeValue))
    else:
        raise Exception('Unsupported nodetype in criteria %s'%(node.getNodeType()))
    return searchMetadata

def __resolve_search_criteria__(session, sheet, criteriaNode, limit, orderBy=None, isDescending = False):
    conditionlist = list()
    searchMetadata = __construct_basequery__(session, criteriaNode,conditionlist )
    basequery = ('and', conditionlist)
    searchList = session.advQuery(metadata = searchMetadata, limit=limit, baseQuery=basequery, orderBy=orderBy, isDescending=isDescending)
    return searchList

def createBaseQueryCriteria(baseQuery,lhsCell,operator,rhsValue,criteriaCell=None,parentCell=None):
    rhsList=[rhsValue]
    if criteriaCell:
        rhsList.append(criteriaCell)
    if parentCell:
        rhsList.append(parentCell)
    baseQuery[(lhsCell.getSheetId(),operator)]=rhsList
    return baseQuery

def createAdvBaseQuery(session, criteria, rhsSheet, params = None, basequeryval = None):
        conditionlist=criteria.getConditionList()
        conditionType = "and"
        if criteria.isOrOper():
            conditionType = "or"
        listOfCondition = list()
        if basequeryval:
            listOfCondition = basequeryval
        for condition in conditionlist:
            operator='='
            if condition.getOperator():
                operator = ''+condition.getOperator()
            lhsCellMetadata = condition.getLHSCellMetadata()
            if lhsCellMetadata:
                rhsValue = None
                if condition.getRHSParamType()==PARAM_TYPE_VALUE:
                    rhsValue = condition.getRHSValue()
                    if lhsCellMetadata.isStr() != 1:
                        rhsValue=float(condition.getRHSValue() or 0)
                elif condition.getRHSParamType()==PARAM_TYPE_USERINPUT or condition.getRHSParamType()==PARAM_TYPE_MASTERLOOKUP:
                    if params and condition.getRHSValue():
                        paramName=condition.getRHSValue()
                        for key,value in params.items():
                            if key[1:]==paramName and value:
                                rhsValue = value
                                if condition.getLHSCellMetadata().isStr() != 1.0:
                                    rhsValue = float(rhsValue)
                elif condition.getRHSParamType()==PARAM_TYPE_CELL:
                    rhsCellMetadata = condition.getRHSCellMetadata()
                    if operator=='in':
                        refcell=condition.getRHSReferenceCellMetadata()
                        if refcell and rhsCellMetadata:
                            if condition.getRHSParentReferenceCellMetadata():
                                manyToOneRefCell = condition.getRHSParentReferenceCellMetadata()
                                manyToOneReferenceValue= rhsSheet.get(manyToOneRefCell)
                                rhsSheet=self.session.get(manyToOneReferenceValue,manyToOneRefCell.getReferredSheetMetadata())
                            refcell=condition.getRHSReferenceCellMetadata()
                            rhsValue=[sheet.get(rhsCellMetadata) for sheet in session.getList(rhsSheet,refcell)]
                    else:
                        rhsValue = rhsSheet.get(rhsCellMetadata)
                elif condition.getRHSParamType()==PARAM_TYPE_FORMULA:
                    if condition.getRHSFormulaList():
                        from orangescape.model.engine.execution.ExecutionTree import ExecutionTree
                        formula = condition.getRHSFormulaList()[0]
                        et = ExecutionTree(session)
                        rhsValue= et.executeFormula(formula, rhsSheet)
                if rhsValue:
                    listOfCondition.append((lhsCellMetadata.getSheetId(), operator, rhsValue))
        baseQuery = (conditionType, listOfCondition)
        for childCriteria in criteria.getCriteriaList():
            nestedBaseQuery = createAdvBaseQuery(session,childCriteria, rhsSheet, params)
            listOfCondition.append(nestedBaseQuery)
        return baseQuery

def geFinalEditorFormName(actDef):
    customFormName = actDef.getFinalEditorForm()
    if customFormName:
        return customFormName
    gridViewName = actDef.getFinalEditorGridView()
    return gridViewName

def getFinalInboxFormName(actDef):
    customFormName = actDef.getFinalInboxForm()
    if customFormName:
        return customFormName
    gridViewName = actDef.getFinalInboxGridView()
    return gridViewName

def getInboxGridViewIds(session,sheet):
    activityDefDict=dict()
    activityMetadata=session.getApplication().getSheetMetadata('ActivityDefinition')
    branchMetadata=session.getApplication().getSheetMetadata('Branch')
    branch_sheet_RefCell=branchMetadata.getPublicCellMetadata('SheetMetadata')
    activity_branch_RefCell=activityMetadata.getPublicCellMetadata('Branch')
    branchList=session.getList(sheet,branch_sheet_RefCell)
    for branch in branchList:
        if branch.getValue('ProcessType') and branch.getValue('ProcessType')=='Workflow':
            activityList=session.getList(branch,activity_branch_RefCell)
            for activity in activityList:
                if activity.getValue('NodeType') and (activity.getValue('NodeType')=='OR' or activity.getValue('NodeType')=='AND' or activity.getValue('NodeType')=='REAL_OR'):
                    getBranchActivities_InboxGridViewIds(session,activity,activityDefDict)
                elif activity.getValue('FinalInboxGridView'):
                        activityDefDict[activity.getValue('FinalInboxGridView')]=activity.getValue('SheetId')
    return activityDefDict

def getBranchActivities_InboxGridViewIds(session,activityDef,activityDefDict):
    activityMetadata=session.getApplication().getSheetMetadata('ActivityDefinition')
    branchMetadata=session.getApplication().getSheetMetadata('Branch')
    branch_activity_RefCell=branchMetadata.getPublicCellMetadata('ActivityDefinition')
    activity_branch_RefCell=activityMetadata.getPublicCellMetadata('Branch')
    branchList=session.getList(activityDef,branch_activity_RefCell)
    for branch in branchList:
        if branch.getValue('ProcessType') and branch.getValue('ProcessType')=='Workflow':
            activityList=session.getList(branch,activity_branch_RefCell)
            for activity in activityList:
                if activity.getValue('NodeType') and (activity.getValue('NodeType')=='OR' or activity.getValue('NodeType')=='AND' or activity.getValue('NodeType')=='REAL_OR'):
                    getBranchActivities_InboxGridViewIds(session,activity,activityDefDict)
                elif activity.getValue('FinalInboxGridView'):
                        activityDefDict[activity.getValue('FinalInboxGridView')]=activity.getValue('SheetId')

def createFormulaNode(criteria, session):
    from orangescape.model.server.Interface import BaseNode
    from orangescape.model.server.Constants import PARAM_TYPE_VALUE, PARAM_TYPE_CELL
    operFn_operatorDict = {'=':'eqOper', '<':'eqOper', '>':'gtOper', '>=':'gtEqOper', '!=':'notEqOper', '<=':'ltEqOper'}

    conditionlist=criteria.getConditionList()
    if conditionlist.__len__()==0:
        resultFormula=None
    else:
        resultFormula=BaseNode()
        resultFormula.argNo = 1.0
        resultFormula.nodeValue ='AND'
        resultFormula.nodeType = 'Function'

    isNotValidCondition=True
    for condition in conditionlist:
        if condition.getOperator():
            operator=operFn_operatorDict[condition.getOperator()]
        else:
            operator='eqOper'
        root=BaseNode()
        root.argNo = 1.0

        root.nodeValue =operator
        root.nodeType = 'Function'
        if condition.getLHSCellMetadata():
            lhsNode = BaseNode()
            lhsNode.argNo = 1.0
            lhsNode.nodeType = 'CellReference'
            lhsNode.cellMetadata = condition.getLHSCellMetadata()

            if condition.getRHSParamType() is None or condition.getRHSParamType()==PARAM_TYPE_VALUE:
                rhsNode=None
                if condition.getRHSValue():
                    if(condition.getLHSCellMetadata().isStr()==1):
                        rhsValue=condition.getRHSValue()
                    else:
                        rhsValue=float(condition.getRHSValue())

                    rhsNode = BaseNode()
                    rhsNode.argNo = 2.0
                    rhsNode.nodeType = 'Constant'
                    rhsNode.nodeValue = rhsValue
                elif condition.getRHSParamType()==PARAM_TYPE_CELL:
                    rhsNode = BaseNode()
                    rhsNode.argNo = 2.0
                    if condition.getRHSCellMetadata().isReferenceCell():
                        rhsNode.nodeType ='3dCellReference'
                    else:
                        rhsNode.nodeType = 'CellReference'
                    rhsNode.cellMetadata = condition.getRHSCellMetadata()

                if rhsNode and lhsNode:
                    root.nodeList.append(rhsNode)
                    root.nodeList.append(lhsNode)

                    resultFormula.nodeList.append(root)
#       Recursive calling
    if not criteria.getCriteria()==None:
        formula=createFormulaNode(criteria.getCriteria(), session)
        if formula:
            resultFormula.nodeList.append(formula)

    return resultFormula

def validateGroup(assignedTo, approlelist):
    #approle checking is removed from basecommandfactory also inside __resolve_activity_basequery__
    #it fails for start activity assigned to
    for roleSubscription in approlelist:
        if assignedTo == roleSubscription.getValue('AssignedName') :
            return True
    return False

def updateGlobalParameters(globalInst):
    from orangescape.model.server.Context import getApplication
    if globalInst:
        import appconfig
        appconfig_dict=appconfig.__dict__
        application =getApplication()
        globalSheet = application.getSheetMetadata('Global')
        celldict=globalSheet.getPublicCellMetadataDict()
        for cellName in celldict:
            if appconfig_dict.has_key(cellName):
                globalInstVal=globalInst.getValue(cellName)
                if globalInstVal and globalInstVal!= appconfig_dict[cellName]:
                    #logging.info("%s ======%s"%(cellName,globalInstVal))
                    appconfig_dict[cellName]=globalInstVal
    else:
        logging.warn("global instance is null when trying to update global parameter")