from orangescape.model.engine.execution.ExecutionTree import ExecutionTree
from orangescape.model.server.Interface import BaseNode
import logging
from orangescape.model.server.util.ServerUtil import createBaseQueryCriteria, createAdvBaseQuery
from orangescape.model.server.Constants import PARAM_TYPE_CELL,PARAM_TYPE_FORMULA,PARAM_TYPE_VALUE,PARAM_TYPE_USERINPUT,PARAM_TYPE_MASTERLOOKUP
from orangescape.model.server.util.ReportSerializer import ReportKeysSerializer, ReportSyncSerializer
import datetime
import appconfig as config

class SearchExecution:
    session=None
    queryNode=None

    def __init__(self, session):
        self.session=session
        self.totalCount = 0

    def execteSearch(self,lhsSheetMetadata,rhsSheetMetadata,rhsSheet,baseQueryval, searchDef, pageNumber, pageCount, serializeChildRecord, params):
        orderByCellId,isDesc = self.getOrderByCell(lhsSheetMetadata, searchDef, params)
        notAllowNone = searchDef.isNotAllowNone()

        limit = 100
        if pageCount is not None:
            limit = int(pageCount)
        offset = 0
        if pageNumber is not None:
            offset = (int(pageNumber)-1) * limit
        criteria=None
        baseQueryforAdv =None
        if searchDef.getCriteriaList().__len__()>0 :
            criteria=searchDef.getCriteriaList().pop()
            if criteria.getConditionList().__len__()>0:
                baseQueryforAdv = createAdvBaseQuery(self.session,criteria, rhsSheet, params, baseQueryval)

        searchResult=list()
        if  baseQueryforAdv == None and baseQueryval :
            baseQueryforAdv = ('and',baseQueryval)

        #logging.info(baseQuery)
        if serializeChildRecord:
            searchResult=self.session.advQuery(lhsSheetMetadata, limit, baseQueryforAdv, self.queryNode, orderByCellId, isDesc, offset)
            containsIn = False
            if config.env == config.GAE:
                for keyTuple in baseQueryforAdv:
                    if keyTuple.__contains__("in") or keyTuple.__contains__("!="):
                        containsIn = True
                        break;
            self.totalCount = limit
            if not containsIn:
                self.totalCount = self.session.count(lhsSheetMetadata, baseQueryforAdv)
        else:
            cellNameList = list()
            cellNameList.append("SheetId")
            displayCellName = "SheetName"
            if searchDef.getDisplayCell():
                displayCellName = searchDef.getDisplayCell().getCellName()
            cellNameList.append(displayCellName)
            #inMemorySorting is added,because of google appengine's index issue
            inMemorySorting = False
            if config.env == config.GAE:
                inMemorySorting = True
            searchResult=self.session.report_query(lhsSheetMetadata, limit, baseQueryforAdv, cellNameList, orderByCellId, isDesc, offset, inMemorySorting)
        return searchResult


    def getTotalCount(self):
        return self.totalCount

    def getOrderByCellViaParams(self, lhsSheetMetadata, params, orderByCellId=None, isDesc=False):
        #dynamic order by via params
        orderByCellList = list()
        if params.get("asc"):
            orderByCellList = eval(params.get('asc'))
            isDesc = False
        elif params.get("desc"):
            orderByCellList = eval(params.get('desc'))
            isDesc = True
        if len(orderByCellList) > 0:
            orderByCell = lhsSheetMetadata.getPublicCellMetadata(orderByCellList.pop())
            orderByCellId = orderByCell.getSheetId()

        return orderByCellId, isDesc

    def getOrderByCell(self, lhsSheetMetadata, searchDef, params):
        orderByCell = searchDef.getDisplayCell()
        orderByCellId = None
        if orderByCell:
            orderByCellId = orderByCell.getSheetId()
        isDesc = searchDef.isDescending()
        return self.getOrderByCellViaParams(lhsSheetMetadata, params, orderByCellId, isDesc)


    def createBaseQueryNode(self,searchDef,criteria,baseQuery, params=None):
        conditionlist=criteria.getConditionList()
        for condition in conditionlist:
            if condition.getOperator():
                operator = ''+condition.getOperator()
            else:
                operator='='

            if condition.getLHSCellMetadata():
                if condition.getRHSParamType() is None or condition.getRHSParamType()==PARAM_TYPE_VALUE:
                    if condition.getRHSValue():
                        if(condition.getLHSCellMetadata().isStr()==1):
                            rhsValue=condition.getRHSValue()
                        else:
                            rhsValue=float(condition.getRHSValue())
                        baseQuery=createBaseQueryCriteria(baseQuery,condition.getLHSCellMetadata(), operator, rhsValue)
                elif condition.getRHSParamType()==PARAM_TYPE_CELL:
                    if operator=='in':
                        if condition.getRHSReferenceCellMetadata() and condition.getRHSCellMetadata():
                            if condition.getRHSParentReferenceCellMetadata():
                                baseQuery=createBaseQueryCriteria(baseQuery,condition.getLHSCellMetadata(), operator, condition.getRHSReferenceCellMetadata(),condition.getRHSCellMetadata(),condition.getRHSParentReferenceCellMetadata())
                            else:
                                baseQuery=createBaseQueryCriteria(baseQuery,condition.getLHSCellMetadata(), operator, condition.getRHSReferenceCellMetadata(),condition.getRHSCellMetadata())
                    else:
                        if condition.getRHSCellMetadata():
                            baseQuery=createBaseQueryCriteria(baseQuery,condition.getLHSCellMetadata(), operator, condition.getRHSCellMetadata())

                elif condition.getRHSParamType()==PARAM_TYPE_FORMULA:
                    if condition.getRHSFormulaList():
                        baseQuery=createBaseQueryCriteria(baseQuery,condition.getLHSCellMetadata(), operator, condition.getRHSFormulaList()[0])
                elif condition.getRHSParamType()==PARAM_TYPE_USERINPUT or condition.getRHSParamType()==PARAM_TYPE_MASTERLOOKUP:
                    if params and condition.getRHSValue():
                        rhsValue=condition.getRHSValue()
                        for key,value in params.items():
                            if key[1:]==rhsValue and value:
                                if condition.getLHSCellMetadata().isStr() != 1.0:
                                    value = float(value)
                                baseQuery=createBaseQueryCriteria(baseQuery,condition.getLHSCellMetadata(), operator, value)
                elif operator=='in':
                        if condition.getRHSReferenceCellMetadata() and condition.getRHSCellMetadata():
                            baseQuery=createBaseQueryCriteria(baseQuery,condition.getLHSCellMetadata(), operator, condition.getRHSReferenceCellMetadata(),condition.getRHSCellMetadata())

    #       Recursive calling
        if not criteria.getCriteria()==None:
            baseQuery=self.createBaseQueryNode(searchDef,criteria.getCriteria(),baseQuery,params)
        return  baseQuery

    def resolvedependencyforBaseQuery(self,baseQueryval,lhsSheetMetadata,rhsSheetMetadata,rhsSheet,notAllowNone):
        baseQuery={}
        for key, value in baseQueryval.iteritems():
                rhsValue=None
                if value:
                    if value.__len__()==1:
                        if isinstance(value[0], str) or isinstance(value[0], unicode) or isinstance(value[0], float) or isinstance(value[0], list):
                            rhsValue=value[0]
                        elif isinstance(value[0], object):
                            if value[0].getSheetMetadataName() == 'Formula':
                                et = ExecutionTree(self.session)
                                rhsValue= et.executeFormula(value[0], rhsSheet)
                            else:
                                rhsValue=rhsSheet.get(value[0])
                        else:
                            rhsValue=value[0]
                        if notAllowNone!=1.0 or rhsValue:
                            baseQuery[key]=rhsValue
                    else:
                        refcell=value[0]
                        if value.__len__()==3:
                            rhsSheet=self.session.get(rhsSheet.get(value[2]),value[2].getReferredSheetMetadata())
                        rhsListValue=[sheet.get(value[1]) for sheet in self.session.getList(rhsSheet,refcell)]
                        if notAllowNone!=1.0 or rhsListValue.__len__()>0:
                            baseQuery[key]=rhsListValue
        return baseQuery

    def createFormulaNode(self, criteria,resultFormula, transientSheet, session):
        operFn_operatorDict = {'=':'eqOper', '<':'eqOper', '>':'gtOper', '>=':'gtEqOper', '!=':'notEqOper', '<=':'ltEqOper'}
        resultFormula=BaseNode()
        resultFormula.argNo = 1.0

        resultFormula.nodeValue ='AND'
        resultFormula.nodeType = 'Function'

        conditionlist=criteria.getConditionList()
        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:
                    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()

                root.nodeList.append(rhsNode)
                root.nodeList.append(lhsNode)

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

        return resultFormula

    def createCriteriaNode(self,criteria,resultFormula, transientSheet,session):
        resultFormula=QueryNode()
        resultFormula.argNo = 1.0
#        if criteria.isAndOper()==1:
#            resultFormula.nodeValue ='AND'
#        else:
#            resultFormula.nodeValue='OR'
        resultFormula.nodeValue ='AND'
        resultFormula.nodeType = 'Function'

        conditionlist=criteria.getConditionList()
        isNotValidCondition=True
        for condition in conditionlist:
            if(condition.getLHSFormulaList() and  condition.getRHSFormulaList()):
                isNotValidCondition=False
                root = QueryNode()
                root.argNo = 1.0
                root.nodeValue ='eqOper'
                if condition.getOperator():
                    root.nodeValue = condition.getOperator()
                root.nodeType = 'Function'

                lhsFormula=condition.getLHSFormulaList()[0]

                lhsNode=lhsFormula.getNodeList()[0]
                lhsNode.argNo=1.0

                rhsNode = QueryNode()
                rhsNode.argNo = 2.0
                rhsNode.nodeType='Constant'
                #Resolving the formula
                """Transient session need to be handled later"""
    #            et = ExecutionTree(self.transientSession)
    #            rhsNode.nodeValue = et.executeFormula(rhsFormula, transientSheet)

                """!!!!This will execute only for constant value.!!!!"""
                et = ExecutionTree(session)
                rhsValue= et.executeFormula(condition.getRHSFormulaList()[0], transientSheet)
#                rhsNode.nodeValue = et.executeFormula(condition.getRHSFormulaList()[0], transientSheet)

                if isinstance(rhsValue,str) or isinstance(rhsValue,unicode):
                    lhsNode.str=True
                    rhsNode.str=True
                    rhsNode.nodeValue=rhsValue
                else:
                    rhsNode.nodeFloatValue =rhsValue

                root.nodeList.append(rhsNode)
                root.nodeList.append(lhsNode)
                resultFormula.nodeList.append(root)
#       Recursive calling
        if not criteria.getCriteria()==None:
            formula=self.createCriteriaNode(criteria.getCriteria(), None, transientSheet, session)
            if formula:
                resultFormula.nodeList.append(formula)
        if  isNotValidCondition:
                    return None
        return resultFormula

    def query_keys(self, searchSheetMetadata, rhsSheet, searchDef, baseQuery, modifiedAt=None, params=None):
        if modifiedAt:
            baseQuery.append((searchSheetMetadata.getPublicCellMetadata('ModifiedAt').getSheetId(), '>', float(modifiedAt)))
        if searchDef.getCriteriaList().__len__()>0 :
            criteria=searchDef.getCriteriaList().pop()
            if criteria.getConditionList().__len__()>0:
                baseQuery = createAdvBaseQuery(self.session, criteria, rhsSheet, params, baseQuery)
        keyList = self.session.keys(searchSheetMetadata, baseQuery)
        return ReportKeysSerializer(self.session).serializeKeys(searchSheetMetadata, keyList)

    def query_entities(self, searchSheetMetadata, rhsSheet, searchDef, baseQuery, columns, modifiedAt=None, params=None):
        if modifiedAt:
            modifiedAt = float(modifiedAt)
            baseQuery.append((searchSheetMetadata.getPublicCellMetadata('ModifiedAt').getSheetId(), '>', modifiedAt))
        if searchDef.getCriteriaList().__len__()>0 :
            criteria=searchDef.getCriteriaList().pop()
            if criteria.getConditionList().__len__()>0:
                baseQuery = createAdvBaseQuery(self.session, criteria, rhsSheet, params, baseQuery)

        entities = self.session.advQuery( searchSheetMetadata, 0, baseQuery)
        return ReportSyncSerializer(self.session).serializeWithKeys(searchSheetMetadata, entities, columns)

    def aggregate_entities(self, searchSheetMetadata, rhsSheet, searchDef, baseQuery, measures, dimensions, operations, params=None):
        #order by is not taken from searchDef, because our worktop run as search aggreagate which need index for GFS, need to revisit
        orderByCellId, isDesc = self.getOrderByCellViaParams(searchSheetMetadata, params)

        baseQuery = None
        if searchDef.getCriteriaList().__len__()>0 :
            criteria=searchDef.getCriteriaList().pop()
            if criteria.getConditionList().__len__()>0:
                baseQuery = createAdvBaseQuery(self.session,criteria, rhsSheet, params)
        logging.info("***************** inside aggregate entities ***********************%s"%len(baseQuery))
        logging.info(baseQuery)
        searchResult=list()
        if baseQuery:
            searchResult = self.session.aggregate_query(searchSheetMetadata, 1000000000, baseQuery, measures, dimensions, operations, orderByCellId, isDesc)
        return searchResult
