import logging

def invoke(component,sheetmetadata,args,application,referencecell):
    runtimeService = RuntimeService(component)
    runtimeService_method = getattr(runtimeService,args.get('method'))
    return runtimeService_method(sheetmetadata,args,application,referencecell)

class RuntimeService():
    def __init__(self, component):
        self.component = component
        self.session = component.getSession()

    def getOrderList(self, actualDict):
        for sheetId in actualDict:
            params = actualDict[sheetId]
            if not params.__contains__("__OrderBy"):
                return actualDict.items()
        return sorted(actualDict.items(), key=lambda x : x[1].pop("__OrderBy"))

    def batchUpdate(self, sheetMetadata, args, instance, referencecell):
        batchDict = eval(args.get("batchJson"))
        #updating modifiedIn in create, createUpdate, update and invoke
        modifiedIn = args.get("ModifiedIn")

        createdIdMapping = dict()
        createDict = batchDict.get("create")
        if createDict:
            createList = self.getOrderList(createDict)
            for sheetId, params in createList:
                if createdIdMapping.get(sheetId):
                    if params.__contains__("SheetMetadataId"): params.pop("SheetMetadataId")
                    sheet = createdIdMapping.get(sheetId)
                    sheetMetadata = sheet.getSheetMetadata()
                else:
                    sheetMetadata = self.getSheetMetadata(self.session, params)
                    sheet = self.session.get(sheetId,sheetMetadata)
                for refCellName in params:
                    itemList = params.get(refCellName)
                    refCellMetadata = None
                    if refCellName.__contains__("."):
                        refCellMetadata = self.getChildReferenceCell(sheetMetadata, refCellName)
                        if refCellMetadata is not None:
                            for dummyId in itemList:
                                newSheet = self.component.create(sheet, refCellMetadata, {"ModifiedIn":modifiedIn})
                                createdIdMapping[dummyId] = newSheet
                        else:
                            logging.error("one to many refcellName '%s' is incorrect while performing batch create operation from the sheet '%s[%s]' "%(refCellName, sheetMetadata.getSheetName(), sheetId))
                    else:
                        refCellMetadata = sheetMetadata.getPublicCellMetadata(refCellName)
                        if refCellMetadata is not None:
                            for dummyId in itemList:
                                newSheet = self.component.createUpdate(sheet, refCellMetadata, {"ModifiedIn":modifiedIn})
                                createdIdMapping[dummyId] = newSheet
                        else:
                            logging.error("many to one refcellName '%s' is incorrect while performing batch create operation from the sheet '%s[%s]' "%(refCellName, sheetMetadata.getSheetName(), sheetId))

        updateDict = batchDict.get("update")
        if updateDict:
            updateList = self.getOrderList(updateDict)
            for sheetId, params in updateList:
                if createdIdMapping.get(sheetId):
                    #sheetmetadataid,SheetId should be removed before updating
                    if params.__contains__("SheetId"): params.pop("SheetId")
                    if params.__contains__("SheetMetadataId"): params.pop("SheetMetadataId")
                    sheet = createdIdMapping.get(sheetId)
                else:
                    sheetMetadata = self.getSheetMetadata(self.session, params)
                    sheet = self.session.get(sheetId,sheetMetadata)
                if len(params)> 0 :
                    #if params containes dictionary(incase of referencecell)
                    self.replaceDictToValue(params)
                    params["ModifiedIn"] = modifiedIn
                    self.component.update(sheet, params)

        invokeDict = batchDict.get("invoke")
        if invokeDict:
            rootSheetMetadataId = invokeDict.pop("rootSheetMetadataId")
            form = None
            if invokeDict.__contains__("form"):
                form = invokeDict.pop("form")
            invokeList = self.getOrderList(invokeDict)
            for sheetId, params in invokeList:
                params = invokeDict.get(sheetId)
                if createdIdMapping.get(sheetId):
                    #sheetmetadataid will be available part of param
                    if params.__contains__("SheetMetadataId"): params.pop("SheetMetadataId")
                    sheet = createdIdMapping.get(sheetId)
                    sheetMetadata = sheet.getSheetMetadata()
                else:
                    sheetMetadata = self.getSheetMetadata(self.session, params)
                    sheet = self.session.get(sheetId,sheetMetadata)
                for actionName in params:
                    values = params[actionName]
                    if actionName.find('.')>0:
                        methodSplit = actionName.split("~")
                        values["method"] = methodSplit.pop()
                        values["invoke"] = methodSplit.pop()
                    else:
                        values["rootSheetMetadataId"] = rootSheetMetadataId
                        values["form"] = form
                        values["invoke"] = actionName
                    #update modifiedIn for all the batchupate invoke
                    self.component.update(sheet, {'ModifiedIn':modifiedIn})
                    self.component.invoke(sheetMetadata, values, sheet)

        deleteDict = batchDict.get("remove")
        if deleteDict:
            for sheetId in deleteDict:
                params = deleteDict.get(sheetId)
                sheetMetadata = self.getSheetMetadata(self.session, params)
                sheet = self.session.get(sheetId,sheetMetadata)
                for refCellName in params:
                    sheetIdList = params.get(refCellName)
                    if refCellName.__contains__("."):
                        refCellMetadata = self.getChildReferenceCell(sheetMetadata, refCellName)
                        if refCellMetadata is not None:
                            sheetList = [self.session.get(sheetId, refCellMetadata.getSheetMetadata()) for sheetId in sheetIdList]
                            self.component.deleteAll(sheetList)
                        else:
                            logging.error("one to many refcellName '%s' is incorrect while performing batch delete operation from the sheet '%s[%s]' "%(refCellName, sheetMetadata.getSheetName(), sheetId))
                    else:
                        refCellMetadata = sheetMetadata.getPublicCellMetadata(refCellName)
                        if refCellMetadata is not None:
                            sheetList = [self.session.get(sheetId, refCellMetadata.getReferredSheetMetadata()) for sheetId in sheetIdList]
                            self.component.deleteAll(sheetList)
                        else:
                            logging.error("many to one refcellName '%s' is incorrect while performing batch delete operation from the sheet '%s[%s]' "%(refCellName, sheetMetadata.getSheetName(), sheetId))

    def updateGlobal(self,sheetMetadata, args, instance, referencecell):
        from orangescape.model.server.util.ServerUtil import updateGlobalParameters
        from orangescape.model.server.Context import getGlobalInstance
        updateGlobalParameters(getGlobalInstance(self.session, True))

    def getSheetMetadata(self, session, params):
        sheetMetadata = session.getApplication().getSheetMetadataById(params.pop("SheetMetadataId"))
        return sheetMetadata

    def getChildReferenceCell(self,sheetMetadata, refCellName):
        splitData = refCellName.split(".")
        for cellMetadata in sheetMetadata.getOneToManyList():
            if splitData[0] == cellMetadata.getCellName() and splitData[1] == cellMetadata.getSheetMetadata().getSheetName():
                return cellMetadata

    def replaceDictToValue(self, params):
        #as now it is handled only in update
        for cellName in params:
            cellValue = params.get(cellName)
            if isinstance(cellValue ,dict):
                params[cellName] = cellValue.keys().pop(0)