import appconfig as config
from cStringIO import StringIO
from json import write
import logging
from SerializeUtil import excludeDict, excludeStylePropList, getDisplayCellName, getDependancyList, getFilteredList
from orangescape.model.server.PerfMonitor import startPerfMonitor, stopPerfMonitor

def getJsonString(session,object,gridViewName):
    pm = startPerfMonitor(object.getSheetMetadata().getSheetName(),'serialize','serializeForm',sheetid=object.getSheetId())
    specifedSheetMetadataDict = session.getApplication().getFormMapping().get(object.getSheetMetadata().getSheetName())
    specifiedGridViewDict = None
    jsonStr = ""
    if specifedSheetMetadataDict:
        specifiedGridViewDict = specifedSheetMetadataDict.get(gridViewName)
        if specifiedGridViewDict:
            jsonStr = JsonWriter(session,specifiedGridViewDict).getJson(object)
        else:
            raise Exception('Form information not found in formmapping for the sheet %s for the Form %s'%(object.getSheetMetadata().getSheetName(), gridViewName))
    else:
        logging.warn('Form information not found in formmapping for the sheet %s'%(object.getSheetMetadata().getSheetName()))
    stopPerfMonitor(pm)
    return jsonStr

def serializeToFile(session,object,gridViewId):
    #serialize application related stuff
    jsonStr = serializeSheet(session,object,gridViewId)
    myfile = open("test/Application.py", "w")
    myfile.write("Application="+jsonStr)
    myfile.close

    #serialize sheetmetadata related stuff
    sheetMetadata = session.getApplication().getSheetMetadata('SheetMetadata')
    sheetList = session.getList(object,sheetMetadata.getPublicCellMetadata('Application'))
    for sheet in sheetList:
        jsonStr = serializeSheet(session,sheet,gridViewId)
        sheetId = sheet.getSheetId()
        myfile = open("test/"+sheetId+".py", "w")
        myfile.write(sheetId+" = "+jsonStr)
        myfile.close

def serializeSheet(session,object,gridViewId):
    specifedSheetMetadataDict = session.getApplication().getFormMapping().get(object.getSheetMetadata().getSheetName())
    specifiedGridViewDict = None
    jsonStr = ""
    if specifedSheetMetadataDict:
        specifiedGridViewDict = specifedSheetMetadataDict.get(gridViewId)
        jsonStr = JsonWriter(session,specifiedGridViewDict,True).getJson(object)
    return jsonStr

def serializeStyle(session,style,styleSheetMetadata):
    cssStr = StringIO()
    cssStr.write(".")
    cssStr.write(style.getSheetName().replace(" ","_"))
    cssStr.write('{\n')
    customStyleSheetMetadata = session.getApplication().getSheetMetadata('CustomStyle')
    refCell = customStyleSheetMetadata.getPublicCellMetadata('Style')
    customStyleList = session.getList(style,refCell)
    for customStyle in customStyleList:
        propName = customStyle.getValue('propertyName')
        propValue = customStyle.getValue('propertyValue')
        if propName:
            cssStr.write("\t")
            cssStr.write(propName)
            cssStr.write(": ")
            cssStr.write(str(propValue))
            cssStr.write(";\n")
    for cellName in styleSheetMetadata.getPublicCellMetadataDict():
        cell = styleSheetMetadata.getPublicCellMetadata(cellName)
        cellValue = style.get(cell)
        cellName=cellName.replace('_','-')
        if cellValue and not excludeStylePropList.__contains__(cellName):
            cssStr.write("\t")
            cssStr.write(cellName)
            cssStr.write(": ")
            cssStr.write(str(cellValue))
            cssStr.write(";\n")
    cssStr.write("}\n\n")
    return cssStr.getvalue()

class JsonWriter:
    def __init__(self,session,specifiedGridViewDict,asFile=False):
        self.session = session
        self.processedDict = dict()
        self.fileStr = StringIO()
        self.specifiedGridViewDict = specifiedGridViewDict
        self.asFile = asFile
        self.getMultiDict = dict()
        self.manyToOneDict = dict()

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

    def getSpecifiedDict(self,specifedDict,key):
        if self.specifiedGridViewDict.get(key):
            return self.specifiedGridViewDict.get(key)
        value = specifedDict.get(key)
        if value is not None and  isinstance(value, dict):
            return value
        return None

    def getJson(self,obj):
        objKey = obj.getSheetId()
        #open the main brace
        self.ioAppend("{")
        self.ioAppend(self.jsonWrite("root"))
        self.ioAppend(":")
        self.ioAppend(self.jsonWrite(objKey))
        #serialize root object
        specifiedDict = self.specifiedGridViewDict.get("root")
        if specifiedDict is not None:
            self.serialize(obj,specifiedDict)

        #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:
                for manyToOneIdDict in manyToOneIdDictList:
                    for manyToOneId,cellNameList in manyToOneIdDict.iteritems():
                        manyToOneObj = manyToOneObjDict.get(manyToOneId)
                        refCell = cellNameList[0]
                        cellName = refCell.getCellName()
                        displayCellName = getDisplayCellName(refCell)
                        #to serialize network of object reference
                        if len(cellNameList) > 1:
                            parentRefCellValue = cellNameList[1]
                            parentRefObj = manyToOneObjDict.get(parentRefCellValue)
                            childObjList = getFilteredList(parentRefObj, refCell, self.session)
                            #childObjList = self.session.getList(parentRefObj, searchRefCell)
                            strBuffer.append(",")
                            strBuffer.append(self.jsonWrite(cellName))
                            strBuffer.append(":{")
                            strBuffer.append(self.jsonWrite("selected"))
                            strBuffer.append(":")
                            strBuffer.append(self.jsonWrite(manyToOneId))
                            for childObj in childObjList:
                                strBuffer.append(",")
                                strBuffer.append(self.jsonWrite(childObj.getSheetId()))
                                strBuffer.append(":")
                                strBuffer.append(self.jsonWrite(childObj.getValue(displayCellName)))
                            strBuffer.append("}")
                        elif manyToOneObj is not None:
                            strBuffer.append(",")
                            strBuffer.append(self.jsonWrite(cellName))
                            strBuffer.append(":{")
                            strBuffer.append(self.jsonWrite(manyToOneObj.getSheetId()))
                            strBuffer.append(":")
                            strBuffer.append(self.jsonWrite(manyToOneObj.getValue(displayCellName)))
                            strBuffer.append("}")

            #close the main individual brace
            strBuffer.append("}")

            self.ioAppend(''.join(strBuffer))

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

        return self.fileStr.getvalue()

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

        #process one to many references
        oneToManySpecifiedDict = specifiedDict.get("OneToMany")
        if oneToManySpecifiedDict:
            self.serializeOneToManyReference(strBuffer,obj,sheetMetadata,oneToManySpecifiedDict)

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

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

        #for metadata serialization alone
        excludeList = list()
        commonExcludeList = list()
        if self.asFile:
            if excludeDict.get(sheetMetadata.getSheetName()):
                excludeList = excludeDict.get(sheetMetadata.getSheetName())
            commonExcludeList = excludeDict.get('CommonExclude')

        for privateCell in privateCellMetadataList:
            cellValue = obj.get(privateCell)
            strBuffer.append(self.jsonWrite(privateCell.getCellDisplayName()))
            strBuffer.append(":")
            strBuffer.append(self.jsonWrite(cellValue))
            strBuffer.append(",")

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

    def serializeManyToOneReference(self, strBuffer, obj, refCell, sheetMetadata, manyToOneSpecifiedDict):
        cellValue = obj.get(refCell)
        sheetId = obj.getSheetId()
        cellName = refCell.getCellName()
        depantdancyList = getDependancyList(refCell)
        #to serialize network of object reference
        if depantdancyList:
            parentRefCell = depantdancyList[0]
            searchRefCell = depantdancyList[1]
            parentRefCellValue = obj.get(parentRefCell)
            if parentRefCellValue:
                parentRefSheetMetadata = parentRefCell.getReferredSheetMetadata()
                parentRefSheetMetadataId = parentRefSheetMetadata.getSheetId()
                #add to multiDict to get all the many to one reference in single db request
                self.getMultiDict[parentRefCellValue] = parentRefSheetMetadataId

                if self.manyToOneDict.__contains__(sheetId):
                    self.manyToOneDict[sheetId].append({cellValue:[refCell, parentRefCellValue]})
                else:
                    idDict = list()
                    idDict.append({cellValue:[refCell, parentRefCellValue]})
                    self.manyToOneDict[sheetId] = idDict
        if cellValue is not None:
            refSheetMetadata = refCell.getReferredSheetMetadata()
            refSheetMetadataId = refSheetMetadata.getSheetId()

            if not depantdancyList:
                #add to multiDict to get all the many to one reference in single db request
                self.getMultiDict[cellValue] = refSheetMetadataId

                #to serialise the respective reference inside the same processedDict
                if self.manyToOneDict.__contains__(sheetId):
                    self.manyToOneDict[sheetId].append({cellValue:[refCell]})
                else:
                    idDict = list()
                    idDict.append({cellValue:[refCell]})
                    self.manyToOneDict[sheetId] = idDict

            #recursive call to serialize the reference object
            if manyToOneSpecifiedDict is not None:
                refSpecifiedDict = self.getSpecifiedDict(manyToOneSpecifiedDict, refCell.getSheetId())
                if refSpecifiedDict is not None:
                    #recursive call to serialize the reference object
                    if not self.processedDict.__contains__(cellValue):
                        refObj = self.session.getIfExists(cellValue,refSheetMetadata)
                        if refObj is not None:
                            #for metadata serialization alone dont serialize cell from node if it is 3dcellreference or publiccell
                            if not(refSheetMetadata.getSheetMetadataName() == 'CellMetadata' and sheetMetadata.getSheetMetadataName() == "Node" and (obj.getValue('NodeType') == "3dCellReference" or refObj.getValue('SheetMetadata'))):
                                self.serialize(refObj,refSpecifiedDict)
                        else:
                            logging.error("Reference '%s' not found in Instance '%s' for the Cell '%s'"%(cellValue, sheetId, cellName))

    def serializeOneToManyReference(self,strBuffer,obj,sheetMetadata,oneToManySpecifiedDict):
        for key in oneToManySpecifiedDict:
            nameList = key.split(".")
            refCellId = nameList[0]
            refSheetMetadataName = nameList[1]
            refSheetMetadata = self.session.getApplication().getSheetMetadata(refSheetMetadataName)
            refCell = refSheetMetadata.getCellMetadataById(refCellId)
            childInstances= self.session.getList(obj,refCell)
            childLen = len(childInstances)
            #for metadata serialization alone
            if not self.asFile or childLen > 0:
                strBuffer.append(",")
                strBuffer.append(self.jsonWrite(refCell.getCellName()+"."+refSheetMetadataName))
                strBuffer.append(":{")
                for childInstance in childInstances:
                    childId = childInstance.getSheetId()
                    #recursive call to serialize the reference object
                    childSpecifiedDict = self.getSpecifiedDict(oneToManySpecifiedDict,key)
                    if childSpecifiedDict is not None:
                        #for metamodel Node serialization,don't serialize 3dcellreference node via CellMetadata.Node
                        if not(sheetMetadata.getSheetMetadataName() == 'CellMetadata' and refSheetMetadataName == "Node" and childInstance.getValue('NodeType') == "3dCellReference"):
                            if not self.processedDict.__contains__(childId):
                                self.serialize(childInstance,childSpecifiedDict)
                    childLen = childLen - 1
                    strBuffer.append(self.jsonWrite(childId))
                    strBuffer.append(":")
                    strBuffer.append(self.jsonWrite(childInstance.getSheetName()))
                    if childLen > 0:
                        strBuffer.append(",")
                strBuffer.append("}")

    def jsonWrite(self, obj):
        if self.asFile != True:
            return write(obj)
        else:
            if isinstance(obj,str) or isinstance(obj,unicode):
                result = list()
                result.append('"')
                obj = obj.replace('\\', r'\\')
#                obj = obj.replace('"','\\"')
                obj = obj.replace('"', r'\"')
                obj = obj.replace('\b', r'\b')
                obj = obj.replace('\f', r'\f')
                obj = obj.replace('\n', r'\n')
                obj = obj.replace('\r', r'\r')
                obj = obj.replace('\t', r'\t')
                result.append(obj)
                result.append('"')
                return "".join(result)
            elif isinstance(obj,int) or isinstance(obj,long):
                return str(obj)
            elif isinstance(obj,float):
                return "%f" % obj
            elif obj is None:
                return "None"
