from cStringIO import StringIO
from json import write,read
import logging

def DeepSerialize(session,object,isDeep=True):
    return JsonWriter(session).getJson(object,isDeep)

def export(session,object):
    return JsonWriter(session).export(object)

def specifiedSerialize(session,object,gridView):
    return SpecifiedJsonWriter(session).getJson(object,gridView)

def jsonWrite(obj):
    return write(obj)

def jsonRead(strVal):
    return read(strVal)

class JsonWriter:
    def __init__(self,session):
        self.session = session
        self.processedDict = dict()
        self.fileStr = StringIO()

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

    def serialize(self,obj,isDeep=True):
        strBuffer = list()
        objKey = obj.getSheetId()
        self.processedDict[objKey]=obj.getSheetMetadataName()
        strBuffer.append(",")
        strBuffer.append(jsonWrite(objKey))
        strBuffer.append(":{")
        sheetMetadata = obj.getSheetMetadata()
        #process public variables and reference variables
        self.serializePublicCellMetadata(strBuffer,obj,sheetMetadata,isDeep)

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

        strBuffer.append("}")
        self.ioAppend(''.join(strBuffer))

    def serializePublicCellMetadata(self,strBuffer,obj,sheetMetadata,isDeep):
        cellMetadataDict = sheetMetadata.getPublicCellMetadataDict()
        n = len(cellMetadataDict)
        for cellName in cellMetadataDict:
            n = n - 1
            cell = sheetMetadata.getPublicCellMetadata(cellName)
            cellValue = obj.get(cell)
            strBuffer.append(jsonWrite(cellName))
            strBuffer.append(":")
            #process many to one references
            if cell.isReferenceCell() and cell.getReferredSheetMetadata().getSheetName() not in ('Company', 'ProcessInstance', 'User'):
                refSheetMetadata = cell.getReferredSheetMetadata()
                self.serializeManyToOneReference(strBuffer,cellValue,refSheetMetadata,isDeep)
            elif cellName == 'SheetId':
                strBuffer.append(jsonWrite(obj.getSheetId()))
            else:
                strBuffer.append(jsonWrite(cellValue))
            if n > 0:
                strBuffer.append(",")

    def serializeManyToOneReference(self,strBuffer,refObjId,refSheetMetadata,isDeep):
        strBuffer.append("{")
        if refObjId is not None and refObjId!='True':
            try:
                refObj = self.session.get(refObjId,refSheetMetadata)
            except Exception,e:
                logging.error("Instance not found in session for id'"+refObjId+"'"+"for metadata'"+refSheetMetadata.getSheetName()+"'")
                raise e

            #recursive call to serialize the reference object
            if isDeep and not self.processedDict.__contains__(refObjId):
                self.serialize(refObj)
            strBuffer.append(jsonWrite(refObjId))
            strBuffer.append(":")
            strBuffer.append(jsonWrite(refObj.getSheetName()))
        strBuffer.append("}")

    def serializeOneToManyReference(self,strBuffer,obj,sheetMetadata,isDeep):
        oneToManyRefList = sheetMetadata.getOneToManyList()
        for refCell in oneToManyRefList:
            childInstances= self.session.getList(obj,refCell)
            childLen = len(childInstances)
            if refCell.isNamedCell():
                refCellName = refCell.getCellName()
            else:
                refCellName = refCell.getCellDisplayName()
            refSheetMetadataName = refCell.getRowMetadata().getSheetMetadata().getSheetMetadataName()
            strBuffer.append(",")
            strBuffer.append(jsonWrite(refCellName+"."+refSheetMetadataName))
            strBuffer.append(":{")
            for childInstance in childInstances:
                childId = childInstance.getSheetId()
                #recursive call to serialize the reference object
                if isDeep and not self.processedDict.__contains__(childId):
                    self.serialize(childInstance)
                childLen = childLen - 1
                strBuffer.append(jsonWrite(childId))
                strBuffer.append(":")
                strBuffer.append(jsonWrite(childInstance.getSheetName()))
                if childLen > 0:
                    strBuffer.append(",")
            strBuffer.append("}")

    def getJson(self,obj,isDeep=True):
        objKey = obj.getSheetId()
        self.ioAppend("{")
        self.ioAppend(jsonWrite("root"))
        self.ioAppend(":")
        self.ioAppend(jsonWrite(objKey))
        #serialize root object
        self.serialize(obj,isDeep)
        self.ioAppend("}")
        return self.fileStr.getvalue()

    def export(self,obj):
        strBuffer = list()
        objKey = obj.getSheetId()
        strBuffer.append("{")
        strBuffer.append(jsonWrite("root"))
        strBuffer.append(":")
        strBuffer.append(jsonWrite(objKey))
        strBuffer.append(",")
        strBuffer.append(jsonWrite(objKey))
        strBuffer.append(":{")
        sheetMetadata = obj.getSheetMetadata()
        rowMetadataList = sheetMetadata.getRowMetadataList()
        rowLength = len(rowMetadataList)
        for row in rowMetadataList:
            rowLength = rowLength - 1
            cellMetadataList = row.getCellMetadataList()
            n = len(cellMetadataList)
            for cell in cellMetadataList:
                n = n - 1
                cellKey = cell.getSheetId()
                cellValue = obj.get(cell)
                #many to one reference parsing
                if cell.isReferenceCell():
                    refSheetMetadata = cell.getReferredSheetMetadata()
                    strBuffer.append(jsonWrite(cell.getCellName()))
                    strBuffer.append(":")
                    self.serializeManyToOneReference(strBuffer,cellValue,refSheetMetadata,False)
                elif cell.getCellName() == 'SheetId':
                    strBuffer.append(jsonWrite(cellKey))
                    strBuffer.append(":")
                    strBuffer.append(jsonWrite(objKey))
                else:
                    strBuffer.append(jsonWrite(cellKey))
                    strBuffer.append(":")
                    strBuffer.append(jsonWrite(cellValue))
                if n > 0 or rowLength > 0:
                    strBuffer.append(",")

        self.serializeOneToManyReference(strBuffer,obj,sheetMetadata,False)

        strBuffer.append("}}")
        return "".join(strBuffer)

class SpecifiedJsonWriter:
    def __init__(self,session):
        self.session = session
        self.processedDict = dict()

    def specifiedSerialize(self,obj,gridView):
        objKey = obj.getSheetId()
        objName = obj.getSheetName()
        rootDict = dict()
        if self.processedDict.__contains__(objKey):
            #appends data into root Dict for next occurence
            rootDict = self.processedDict[objKey]
        else:
            #add root instance to the processed list
            self.processedDict[objKey]=rootDict

        for gridRow in gridView.getGridRowList():
            referenceCell = gridRow.getReferenceCellMetadata()
            if referenceCell:
                refCellId = referenceCell.getSheetId()
                refCellName = referenceCell.getCellName()
                refSheetMetadataName = referenceCell.getSheetMetadata().getSheetMetadataName()
                oneToManyDict = dict()
                manyToOneDict = dict()
                if obj.getSheetMetadata().getPublicCellMetadataDict().__contains__(refCellName):
                    parentId = obj.get(referenceCell)
                    parentDict = dict()
                    if parentId:
                        if self.processedDict.__contains__(parentId):
                            #appends data into parent dict to add the new attributes present in next row
                            parentDict = self.processedDict[parentId]
                            #append one to many reference into the same dict l001, l002(next call) inside prod001
                            if parentDict.__contains__(refCellName+"."+refSheetMetadataName):
                                oneToManyDict = parentDict.get(refCellName+"."+refSheetMetadataName)

                        parentInstance = self.session.get(parentId,referenceCell.getReferredSheetMetadata())
                        self.serializeSpecifiedPublicCellMetadata(parentDict,parentInstance,gridRow)
                        #add one to many reference to the parent Dict
                        oneToManyDict[objKey]=objName
                        parentDict[refCellName+"."+refSheetMetadataName]=oneToManyDict
                        #add many to one reference to the root Dict
                        manyToOneDict[parentId]=parentInstance.getSheetName()
                        rootDict[refCellName]=manyToOneDict
                        #add parent instance to the processed List
                        self.processedDict[parentId] = parentDict
                else:
                    childInstances= self.session.getList(obj,referenceCell)
                    manyToOneDict[objKey]=objName
#                    if rootDict.__contains__(refCellName+"."+refSheetMetadataName): not need, any way it will be created newly
#                        oneToManyDict = rootDict.get(refCellName+"."+refSheetMetadataName)
                    for childInstance in childInstances:
                        childId = childInstance.getSheetId()
                        childDict = dict()
                        if self.processedDict.__contains__(childId):
                            #appends data into child dict to add the new attributes present in next row
                            childDict = self.processedDict[childId]

                        self.serializeSpecifiedPublicCellMetadata(childDict,childInstance,gridRow)
                        #add many to one reference to the child Dict
                        childDict[refCellName]=manyToOneDict
                        oneToManyDict[childId] = childInstance.getSheetName()
                        #add child instance to the processed list
                        self.processedDict[childId] = childDict
                    #add one to many reference to the root Dict
                    rootDict[refCellName+"."+refSheetMetadataName]=oneToManyDict
            else:
                self.serializeSpecifiedPublicCellMetadata(rootDict,obj,gridRow)

    def serializeSpecifiedPublicCellMetadata(self,localDict,obj,gridRow):
        for gridCell in gridRow.getGridCellList():
            if gridCell.getCellMetadata():
                cellMetadata = gridCell.getCellMetadata()
                cellName = cellMetadata.getCellName()
                cellValue = obj.get(cellMetadata)
                localDict[cellName] = cellValue
            elif gridCell.getSection():
                section = gridCell.getSection()
                startCell = section.getFrom()
                endCell = section.getTo()
                startRowNumber = startCell.getRowMetadata().getRowNumber()
                endRowNumber = endCell.getRowMetadata().getRowNumber()
                startColumnNumber = startCell.getColumnMetadata().getColumnNumber()
                endColumnNumber = endCell.getColumnMetadata().getColumnNumber()
                for cellMetadata in obj.getSheetMetadata().getCellMetadataList():
                    rowNumber = cellMetadata.getRowNumber()
                    colNumber = cellMetadata.getColumnNumber()
                    if rowNumber >= startRowNumber and rowNumber <= endRowNumber and colNumber >=startColumnNumber and colNumber <= endColumnNumber:
                        localDict[cellMetadata.getCellName()] = obj.get(cellMetadata)
            elif len(gridCell.getGridViewList())>0:
                self.specifiedSerialize(obj,gridCell.getGridViewList()[0])

    def getJson(self,obj,gridView):
        objKey = obj.getSheetId()
        self.processedDict["root"] = objKey
        #serialize root object
        self.specifiedSerialize(obj,gridView)
        return jsonWrite(self.processedDict)
