from cStringIO import StringIO
from json import write
from SerializeUtil import getDisplayCellName
import logging
from orangescape.model.server.PerfMonitor import startPerfMonitor, stopPerfMonitor
from orangescape.model.server.util.ConversionUtil import convertFromEngineFormat

def jsonWrite(obj):
    return write(obj)

def getInboxJson(session, sheetMetadata , objectList, displayName=None, serializeChild=False, totalCount=None):
    pm = startPerfMonitor(sheetMetadata.getSheetName(),'serialize','serializeInbox')
    inboxJson = InboxSerialization(session).getJson(sheetMetadata, objectList, displayName, serializeChild, totalCount)
    stopPerfMonitor(pm)
    return inboxJson

def getInboxCsv(session, sheetMetadata, objectList, cellNameList=[]):
    csvStr = CsvExport(session).exportToCsv(sheetMetadata, objectList, cellNameList)
    return csvStr

class Serializer:
    def __init__(self, session):
        self.session = session
        self.fileStr = StringIO()
        self.processedDict = dict()
        self.getMultiDict = dict()
        self.manyToOneDict = dict()

    def ioAppend(self, val):
        self.fileStr.write(val)

class InboxSerialization(Serializer):
    def getJson(self, sheetMetadata, objList, displayName, serializeChild, totalCount = None):
        user = self.session.getUser()
        userId = user.getSheetId()
        #open the main brace
        self.ioAppend("{")
        self.ioAppend(jsonWrite("root"))
        self.ioAppend(":")
        self.ioAppend(jsonWrite(userId))
        if serializeChild:
            self.serialize(user, totalCount)
            userStrBuffer = self.processedDict.get(userId)
            userStrBuffer.append(",")
            userStrBuffer.append(self.getStaticOneToManyList(sheetMetadata, objList, displayName, serializeChild))

            userLoginId = user.getValue('UserLoginModel')
            if userLoginId:
                userLogin = self.session.get(userLoginId, self.session.getApplication().getSheetMetadata('UserLoginModel'))
                self.serialize(userLogin)
            for obj in objList:
                if obj.getSheetId() != userId:
                    self.serialize(obj)

            #append all the many to one reference
            manyToOneObjDict = self.session.getMulti(self.getMultiDict)
            for key, strBuffer in self.processedDict.iteritems():
                manyToOneIdDictList = self.manyToOneDict.get(key)
                if manyToOneIdDictList is not None:
                    for manyToOneIdDict in manyToOneIdDictList:
                        for manyToOneId, cellNameList in manyToOneIdDict.iteritems():
                            manyToOneObj = manyToOneObjDict.get(manyToOneId)
                            if manyToOneObj is not None:
                                cellName = cellNameList[0]
                                displayCellName = cellNameList[1]
                                strBuffer.append(",")
                                strBuffer.append(jsonWrite(cellName))
                                strBuffer.append(":{")
                                strBuffer.append(jsonWrite(manyToOneObj.getSheetId()))
                                strBuffer.append(":")
                                strBuffer.append(jsonWrite(manyToOneObj.getValue(displayCellName)))
                                strBuffer.append("}")
                #close the main individual brace
                strBuffer.append("}")
                self.ioAppend(''.join(strBuffer))
        else:
            self.ioAppend(",")
            self.ioAppend(jsonWrite(userId))
            self.ioAppend(":{")
            self.ioAppend(self.getStaticOneToManyList(sheetMetadata, objList, displayName, serializeChild))
            self.ioAppend("}")

        #close the main brace
        self.ioAppend("}")
        return self.fileStr.getvalue()

    def serialize(self, obj, totalCount=None):
        strBuffer = list()
        objKey = obj.getSheetId()
        #open the main individual brace
        self.processedDict[objKey] = ""
        strBuffer.append(",")
        strBuffer.append(jsonWrite(objKey))
        strBuffer.append(":{")
        sheetMetadata = obj.getSheetMetadata()
        #process public variables and reference variables
        self.serializePublicCellMetadata(strBuffer, obj, sheetMetadata)
        if totalCount:
            strBuffer.append(",")
            strBuffer.append(jsonWrite('TotalCount'))
            strBuffer.append(":")
            strBuffer.append(jsonWrite(totalCount))


        #process one to many references
        #self.serializeOneToManyReference(strBuffer,obj,sheetMetadata)

        self.processedDict[objKey] = strBuffer
#        strBuffer.append("}")
#        self.ioAppend(''.join(strBuffer))

    def serializePublicCellMetadata(self, strBuffer, obj, sheetMetadata):
        cellMetadataList = sheetMetadata.getCellMetadataList()

        for cell in cellMetadataList:
            cellName = cell.getCellName()
            cellValue = obj.get(cell)
            if cell.isReferenceCell():
                #process many to one references
                self.serializeManyToOneReference(strBuffer, obj, cell, cellValue)
            elif cellValue is not None and cellName != 'SheetId' and cellName != 'SheetMetadataId':
                strBuffer.append(jsonWrite(cellName))
                strBuffer.append(":")
                strBuffer.append(jsonWrite(cellValue))
                strBuffer.append(",")
        strBuffer.append(jsonWrite('SheetId'))
        strBuffer.append(":")
        strBuffer.append(jsonWrite(obj.getSheetId()))
        strBuffer.append(",")
        strBuffer.append(jsonWrite('SheetMetadataId'))
        strBuffer.append(":")
        strBuffer.append(jsonWrite(obj.getSheetMetadata().getSheetId()))

    def serializeManyToOneReference(self, strBuffer, obj, refCell, cellValue):
        if cellValue is not None:
            refSheetMetadata = refCell.getReferredSheetMetadata()
            cellName = refCell.getCellName()
            displayCellName = getDisplayCellName(refCell)
            if cellValue is not None:
                refSheetMetadataId = refSheetMetadata.getSheetId()
                self.getMultiDict[cellValue] = refSheetMetadataId

                sheetId = obj.getSheetId()
                if self.manyToOneDict.__contains__(sheetId):
                    self.manyToOneDict[sheetId].append({cellValue:[cellName, displayCellName]})
                else:
                    idDict = list()
                    idDict.append({cellValue:[cellName, displayCellName]})
                    self.manyToOneDict[sheetId] = idDict

#        strBuffer.append("{")
#        if refObjId is not None and str(refObjId).strip() != '':
#            refSheetMetadata = refCell.getReferredSheetMetadata()
#            refObj = self.session.get(refObjId,refSheetMetadata)
#            displayCellName = getDisplayCellName(refCell)
#            strBuffer.append(jsonWrite(refObjId))
#            strBuffer.append(":")
#            strBuffer.append(jsonWrite(refObj.getValue(displayCellName)))
#        strBuffer.append("}")

    def getStaticOneToManyList(self, sheetMetadata, objList, displayName, serializeChild):
        strBuffer = list()
        keyName = "ModifiedBy." + sheetMetadata.getSheetName()
        strBuffer.append(jsonWrite(keyName))
        strBuffer.append(":{")
        childLen = len(objList)
        for obj in objList:
            childLen = childLen - 1
            cellName = 'SheetName'
            if displayName:
                cellName = displayName
            if serializeChild:
                strBuffer.append(jsonWrite(obj.getSheetId()))
                strBuffer.append(":")
                strBuffer.append(jsonWrite(obj.getValue(cellName)))
            else:
                #for search dialog we are using aggregate query for performance improvement
                strBuffer.append(jsonWrite(obj[0]))
                strBuffer.append(":")
                strBuffer.append(jsonWrite(obj[1]))
            if childLen > 0:
                strBuffer.append(",")
        strBuffer.append("}")
        return ''.join(strBuffer)

    def serializeOneToManyReference(self, strBuffer, obj, sheetMetadata):
        oneToManyRefList = sheetMetadata.getOneToManyList()
        for refCell in oneToManyRefList:
            if refCell.isNamedCell():
                childInstances = self.session.getList(obj, refCell)
                childLen = len(childInstances)
                refCellName = refCell.getCellName()
                refSheetMetadataName = refCell.getSheetMetadata().getSheetMetadataName()
                strBuffer.append(",")
                keyName = refCellName + "." + refSheetMetadataName
                strBuffer.append(jsonWrite(keyName))
                strBuffer.append(":{")
                for childInstance in childInstances:
                    childId = childInstance.getSheetId()
                    childLen = childLen - 1
                    strBuffer.append(jsonWrite(childId))
                    strBuffer.append(":")
                    strBuffer.append(jsonWrite(childInstance.getSheetName()))
                    if childLen > 0:
                        strBuffer.append(",")
                strBuffer.append("}")

class CsvExport(Serializer):
    def exportToCsv(self, sheetMetadata, objectList, cellNameList):
        cellMetadataList = list()
        publicCellMetadata = sheetMetadata.getPublicCellMetadataDict()

        if len(cellNameList)>0:
            for cellName in cellNameList:
                if publicCellMetadata.__contains__(cellName):
                    cellMetadataList.append(sheetMetadata.getPublicCellMetadata(cellName))
        else:
            cellMetadataList = sheetMetadata.getCellMetadataList()
        referenceCellMetadataList = list()
        newCellMetadataList = list()
        for cell in cellMetadataList:
            cellName=cell.getCellName()
            if cell.isReferenceCell():
                referenceCellMetadataList.append(cell)
            else:
                newCellMetadataList.append(cell)

        columnNameList = [cell.getCellName() for cell in newCellMetadataList]
        [columnNameList.append(refCell.getCellName()) for refCell in referenceCellMetadataList]
        self.ioAppend(', '.join(columnNameList))
        self.ioAppend('\n')

        for obj in objectList:
            strBuffer = list()
            if isinstance(obj,list):
                self.ioAppend(', '.join(['%s' % el for el in obj]))
                self.ioAppend('\n')
            else:
                self.serializePublicCellMetadata(strBuffer , obj, newCellMetadataList)
                self.serializeManyToOneReference(strBuffer, obj, referenceCellMetadataList)
                self.processedDict[obj.getSheetId()] = strBuffer


        manyToOneObjDict = self.session.getMulti(self.getMultiDict)
        for key, strBuffer in self.processedDict.iteritems():
            for manyToOneValue in self.manyToOneDict.get(key):
                if manyToOneValue == '':
                    strBuffer.append('')
                else:
                    for manyToOneId, displayCellName in manyToOneValue.iteritems():
                        manyToOneObj = manyToOneObjDict.get(manyToOneId)
                        if manyToOneObj is not None:
                            #need to handle format for refcell display name
                            strBuffer.append(manyToOneObj.getValue(displayCellName))

            self.ioAppend(', '.join(['%s' % el for el in strBuffer]))
            self.ioAppend('\n')

        return self.fileStr.getvalue()

    def serializePublicCellMetadata(self, strBuffer, obj, cellMetadataList):
        for cell in cellMetadataList:
            cellValue = obj.get(cell)
            category = cell.getValue('FinalCategory')
            formatStr = cell.getFinalFormat()
            strBuffer.append(convertFromEngineFormat(cellValue, category, formatStr))

    def serializeManyToOneReference(self, strBuffer, obj, referenceCellMetadataList):
        sheetId = obj.getSheetId()
        self.manyToOneDict[sheetId] = list()
        for refCell in referenceCellMetadataList:
            cellValue = obj.get(refCell)
            if cellValue is not None:
                refSheetMetadata = refCell.getReferredSheetMetadata()
                displayCellName = getDisplayCellName(refCell)
                refSheetMetadataId = refSheetMetadata.getSheetId()
                self.getMultiDict[cellValue] = refSheetMetadataId
                self.manyToOneDict[sheetId].append({cellValue:displayCellName})
            else:
                self.manyToOneDict[sheetId].append('')
