import urllib2
import os
import sys
import logging
from xml.dom import minidom
from orangescape.model.engine.execution.ProcessEngine import ProcessNode
from orangescape.model.engine.util.DateUtil import xldate_as_tuple,XLDateNegative
from orangescape.model.server.integration.XMLImportService import XMLImportService
from orangescape.model.server.util.ConversionUtil import convertFromEngineFormat
from RenderTemplate import renderTemplate
import datetime
from appconfig import env,ENTERPRISE,GAE, GAESQL
from traceback import format_exception, format_exc
if env == ENTERPRISE:
    import httplib
elif env in (GAE, GAESQL):
    from google.appengine.api import urlfetch
from UserDict import DictMixin


class odict(DictMixin):

    def __init__(self):
        self._keys = []
        self._data = {}


    def __setitem__(self, key, value):
        if key not in self._data:
            self._keys.append(key)
        self._data[key] = value


    def __getitem__(self, key):
        return self._data[key]


    def __delitem__(self, key):
        del self._data[key]
        self._keys.remove(key)


    def keys(self):
        return list(self._keys)


    def copy(self):
        copyDict = odict()
        copyDict._data = self._data.copy()
        copyDict._keys = self._keys[:]
        return copyDict

class WebServiceProcessNode(ProcessNode):
    def enter(self):
        url =self.args.get('URL')
        if not url:
            raise Exception("Webservice url not defined. Please check the webservice properties.")
        self.namespace = self.args.get('NameSpace')
        security = self.args.get('Security')
        if not self.namespace:
            raise Exception("Namespace not defined. Please check the webservice properties.")
        self.responseHeader=self.args.get('ResponseHeader')
        action =self.args.get('Action')
        username=None
        password=None
        if security and security!="None":
           username=self.args.get('Username')
           password=self.args.get('Password')
        if not action:
            raise Exception("Webservice Action not defined. Please check the webservice properties.")
        root=self.args.get('Root')
        if not root:
            raise Exception("Root not defined. Please check the webservice properties.")
        self.allowNull="Yes"
        if self.args.has_key('Allow Null'):
            self.allowNull=self.args.get('Allow Null')
        if env == ENTERPRISE:
            addr=url.replace('http://','').split('/',1)
            host= addr[0]
            urlPath= '/'+addr[1]
        del self.args['URL']
        del self.args['NameSpace']
        if action:
            del self.args['Action']
        del self.args['Root']
        if self.args.has_key('Allow Null'):
            del self.args['Allow Null']

        templatePath=os.path.dirname(__file__).split('model')
        redirectUrl = templatePath[0:len(templatePath)-1]
        self.path=os.path.join('/'.join(redirectUrl)+"templates")
        params = odict()
        outputParams={}
        inputPortList = self.activityDefinition.getInputportMetadataList()
        inputPortList.sort(lambda x, y: cmp(x.getSequenceNo(),y.getSequenceNo()))
        #logging.info(inputPortList)
        for inputport in inputPortList:
            self.__generateInputXML__(inputport,self.sheet,params)
        for hierarchy in self.activityDefinition.getHierarchyList():
            params[hierarchy.getNodeName()]=self.getParamDict(self.sheet,hierarchy)

        if env == ENTERPRISE:
            templateDict = {'method':action,
                            'namespace':self.namespace,
                            'root':root,
                            'nodes':params
                            }
            xml = renderTemplate("webservice.xml.mako", templateDict)
            webservice = httplib.HTTP(host)
            webservice.putrequest("POST",urlPath)
            webservice.putheader("Host",host)
            webservice.putheader("Content-type", "text/xml; charset=\"UTF-8\"")
            webservice.putheader("Content-length", "%d" %len(xml))
            webservice.putheader("SOAPAction",action)
            if security and security!="None":
                import base64
                base64string = base64.encodestring('%s:%s' % (username, password))[:-1]
                authheader =  "Basic %s" % base64string
                webservice.putheader("Authorization",authheader)
            webservice.endheaders()
            logging.info("the request xml is "+xml)
            webservice.send(xml)
            statuscode, statusmessage, header = webservice.getreply()
            logging.info("Webservice return code :"+str(statuscode)+" with message "+str(statusmessage)+" for "+ str(host)+str(urlPath) +" the header is "+str(header))
            file = webservice.getfile()
            if statuscode==404:
                raise Exception("Host not found "+urlPath)
            if statuscode==500:
                raise Exception("Internal server error thrown by url "+urlPath)
            if statuscode==302:
                url=header.getheader('location')
                logging.info("the redirection url is "+url)
                addr=url.replace('http://','').split('/',1)
                host= addr[0]
                urlPath= '/'+addr[1]
                file=self.redirect(host,urlPath,xml,action)
            response= file.read()
            file.close()
            try:
                logging.info("the response xml is "+response)
                xmldom=minidom.parseString(response)
            except Exception, e:
                pass
        else:
            templateDict = {'method':action,
                            'namespace':self.namespace,
                            'root':root,
                            'nodes':params
                            }
            if action:
                templateDict['action']=action
            xml = renderTemplate("webservice.xml.mako", templateDict)
            logging.info("the request xml is "+xml)
            header = dict()
            header['Content-type']="text/xml; charset=\"UTF-8\""
            header['Content-length']=str(len(xml))
            header['SOAPAction']=action
            if security and security!="None":
                import base64
                base64string = base64.encodestring('%s:%s' % (username, password))[:-1]
                authheader =  "Basic %s" % base64string
                header["Authorization"]=authheader
            response=urlfetch.fetch(url, payload=xml, method="POST", headers=header, allow_truncated=False, follow_redirects=True, deadline=10)
            try:
                xmldom=minidom.parseString(response.content)
                logging.info("the response xml is "+response.content)
            except Exception, exception:
                exc_type, exc_value, exc_tb = sys.exc_info()
                errMsg="%s:%s"%(type(exception).__name__,exception)+" "+"".join(format_exception(exc_type, exc_value, exc_tb)).replace('\n','</br>')
                logging.error(errMsg)
        responseDict=dict()
        responseDict['SheetId']=self.sheet.getSheetId()
        try:
            self.__process_response__(xmldom,responseDict)
        except Exception, exception:
            exc_type, exc_value, exc_tb = sys.exc_info()
            errMsg="%s:%s"%(type(exception).__name__,exception)+" "+"".join(format_exception(exc_type, exc_value, exc_tb)).replace('\n','</br>')
            logging.error(errMsg)

    def __process_response__(self,response,responseDict):
        outputPortList = self.activityDefinition.getOutputportMetadataList()
        try:
            soapPrefix=response.childNodes[0].namespaceURI
            childNodes=response.getElementsByTagNameNS(soapPrefix,"Body")[0]
            if not self.responseHeader:
                raise Exception("Response header is not specified in webservice command configuration. Please check the webservice properties")
            responseHeader=childNodes.getElementsByTagNameNS(self.namespace,self.responseHeader)[0]
            RESPONSE=None
            for outputPort in outputPortList:
                paramNodeList=responseHeader.getElementsByTagNameNS(self.namespace,outputPort.getParamName())
                paramNode=None
                if paramNodeList:
                    paramNode=paramNodeList[0]
                if not paramNode:
                    paramNode=responseHeader.getElementsByTagName(outputPort.getParamName())[0]
                if not paramNode:
                    raise Exception("Unable to get parameter for param "+outputPort.getParamName()+". Please check the webservice response.")
                if len(paramNode.childNodes)>0:
                    paramValue=paramNode.childNodes[0].nodeValue
                else:
                    paramValue=''
                responseDict[outputPort.getCellMetadata().getCellName()]=paramValue
            for hierarchy in self.activityDefinition.getOutputHierarchyList():
                hierarchyNodes=responseHeader.getElementsByTagNameNS(self.namespace,hierarchy.getNodeName())
                if not hierarchyNodes:
                    hierarchyNodes=responseHeader.getElementsByTagName(hierarchy.getNodeName())
                refCell=hierarchy.getReferenceCell()
                if refCell:
                    self.getOutputParamDict(responseDict,hierarchy,hierarchyNodes,refCell.getCellName()+"."+refCell.getSheetMetadata().getSheetMetadataName())
                else:
                    self.getOutputParamDict(responseDict,hierarchy,hierarchyNodes)
            logging.info(responseDict)
            templateDict = {'namespace':'urn:orangescape',
                            'root':self.sheet.getSheetMetadata().getSheetName(),
                            'nodes':responseDict
                            }
            xml = renderTemplate("soapResponse.xml.mako", templateDict)
            importService=XMLImportService(self.component,self.sheet.getSheetMetadata(),xml)
            importService.process()
        except Exception, exception:
            exc_type, exc_value, exc_tb = sys.exc_info()
            errMsg="%s:%s"%(type(exception).__name__,exception)+" "+"".join(format_exception(exc_type, exc_value, exc_tb)).replace('\n','</br>')
            logging.error(errMsg)

    def redirect(self,host,urlPath,xml,action):
        webservice = httplib.HTTP(host)
        webservice.putrequest("POST",urlPath)
        webservice.putheader("Content-type", "text/xml; charset=\"UTF-8\"")
        webservice.putheader("Content-length", "%d" %len(xml))
        webservice.putheader("SOAPAction",action)
        webservice.endheaders()
        webservice.send(xml)
        statuscode, statusmessage, header = webservice.getreply()
        return webservice.getfile()

    def getParamDict(self,sheet,hierarchy):
        children=self.session.getList(sheet,hierarchy.getReferenceCell())
        paramList=[]
        for child in children:
            valueDict=odict()
            inputPortList = hierarchy.getInputportMetadataList()
            inputPortList.sort(lambda x, y: cmp(x.getSequenceNo(),y.getSequenceNo()))
            for inputport in inputPortList:

                self.__generateInputXML__(inputport,child,valueDict)
            for childHierarchy in hierarchy.getHierarchyList():
                childParamList=self.getParamDict(child,childHierarchy)
                valueDict[childHierarchy.getNodeName()]=childParamList

            paramList.append(valueDict)
        return paramList

    def getOutputParamDict(self,responseDict,hierarchy,hierarchyNodes,refCellStr=None):
        childList=[]
        for childNode in hierarchyNodes:
            childValueDict=dict()
            for outputPort in hierarchy.getOutputportMetadataList():
                paramNodeList=childNode.getElementsByTagNameNS(self.namespace,outputPort.getParamName())
                if not paramNodeList:
                   paramNodeList=childNode.getElementsByTagName(outputPort.getParamName())
                paramNode=None
                if paramNodeList:
                   paramNode= paramNodeList[0]
                if not paramNode:
                    raise Exception("Unable to get parameter for param "+outputPort.getParamName()+". Please check the webservice response.")
                if len(paramNode.childNodes)>0:
                    paramValue=paramNode.childNodes[0].nodeValue
                else:
                    paramValue=''
                if not refCellStr:
                    responseDict[outputPort.getCellMetadata().getCellName()]=paramValue
                else:
                    childValueDict[outputPort.getCellMetadata().getCellName()]=paramValue

            for mHierarchy in hierarchy.getHierarchyList():
                hierarchyNode=childNode.getElementsByTagNameNS(self.namespace,mHierarchy.getNodeName())
                if not hierarchyNode:
                   hierarchyNode=childNode.getElementsByTagName(mHierarchy.getNodeName())
                refCell=mHierarchy.getReferenceCell()
                if refCell:
                    self.getOutputParamDict(responseDict,mHierarchy,hierarchyNode,refCell.getCellName()+"."+refCell.getSheetMetadata().getSheetMetadataName())
                else:
                    self.getOutputParamDict(responseDict,mHierarchy,hierarchyNode)
            childList.append(childValueDict)
            if refCellStr:
                responseDict[refCellStr]=childList

    def __generateInputXML__(self,inputport,sheet,valueDict):
        cellMetadata=inputport.getCellMetadata()
        if cellMetadata:
            if cellMetadata.getFinalCategory()=='Category005':
                cellValue=sheet.getValue(inputport.getCellMetadata().getCellName())
                if cellValue>0:
                    tuple=xldate_as_tuple(cellValue,0)
                    dateObj= datetime.datetime(*(tuple[0:6]))
                    dateStr = self.dateToISOString(dateObj)
                    valueDict[inputport.getParamName()]=dateStr
                else:
                    if self.allowNull=="Yes":
                        valueDict[inputport.getParamName()]=''
                    else:
                        valueDict[inputport.getParamName()]='1900-01-01T00:00:00'
            elif cellMetadata.getFinalCategory()=='Category011':
                boolValue='FALSE'
                cellValue=sheet.getValue(inputport.getCellMetadata().getCellName())
                if cellValue:
                    if cellValue==1.0:
                        boolValue='TRUE'
                    else:
                        boolValue='FALSE'
                valueDict[inputport.getParamName()]=boolValue
            else:
                cellValue=sheet.getValue(inputport.getCellMetadata().getCellName())
                if cellValue and cellMetadata.getFinalCategory()=='Category002':
                    cellValue=convertFromEngineFormat(cellValue, cellMetadata.getFinalCategory(),cellMetadata.getFormat())
                if cellValue:
                    valueDict[inputport.getParamName()]=cellValue
                else:
                    if self.allowNull=="Yes":
                        valueDict[inputport.getParamName()]=''
                    else:
                        valueDict[inputport.getParamName()]='0'

    def dateToISOString(self,fromDateObject):
        return fromDateObject.strftime("%Y-%m-%dT%H:%M:%S")
