# idigi_parse.py (v0.3.1)
# contains helper files, SAX parsers, etc.
#
# 9-18-2010 NWF
#
# TODO: -create a ws/parse class for all elements
#       -add DTD capabilities
# 
import xml.sax
from xml.sax import handler, make_parser, parseString
import string

# normalize a string for accurate comparisons
def normalize_whitespace(txtIn):
    return ' '.join(txtIn.split())

def parse_device_core_resp(xmldata):
    try:
        handler = DeviceCoreResp_Handler()
        parser = xml.sax.make_parser()
        parser.setContentHandler(handler)
        parser.setDTDHandler(handler)
        xml.sax.parseString(xmldata, handler)
        retList = handler.get_return_list()

    except Exception, ex:
        return "ERROR"
    else:
        print "parse_device_core_resp: returning a LIST!"
        return retList

# used by build_device_dict
def build_devices_from_xml(xmldata, findCwid=""):
    try:
        handler = DevCoreParser_ReturnAll()

        parser = xml.sax.make_parser()
        parser.setContentHandler(handler)
        parser.setDTDHandler(handler)
        xml.sax.parseString(xmldata, handler)

        retDict = handler.get_return_dict()

    except Exception, ex:
        print "build_devices_from_xml: exception= ", ex
        return "ERROR"
    else:
        return retDict

def parse_devcore_resp(xmldata, findCwid=""):
        try:
            if (findCwid == ""):
                handler = DevCoreParser_ReturnAll()

            parser = xml.sax.make_parser()
            parser.setContentHandler(handler)
            parser.setDTDHandler(handler)
            xml.sax.parseString(xmldata, handler)

            retDict = handler.get_return_dict()
            print "\n\n DICT= "
            print retDict

        except Exception, ex:
            print "parse_devcore_resp: exception= ", ex
            return "ERROR"
        else:
            return retList

def parse_query_state_resp(xmldata, findCwid=""):
    try:
        if (findCwid == ""):
            handler = Query_Zigbee_State_Parser()
        #else:
        #    handler = idigi_xml.DevCoreParser_FindDevice(findCwid)
        parser = xml.sax.make_parser()
        parser.setContentHandler(handler)
        parser.setDTDHandler(handler)
        xml.sax.parseString(xmldata, handler)

        retDict = handler.get_return_dict()
            
    except Exception, ex:
        print "parse_query_state_resp: exception= ", ex
        return "ERROR"
    else:
        return retDict


def check_for_errors(xmldata):
    try:
        handler = CheckForErrors()
        parser = xml.sax.make_parser()
        parser.setContentHandler(handler)
        xml.sax.parseString(xmldata, handler)
        retstr = handler.get_return_string()
        print "retstr=", retstr

    except Exception, ex:
        print "check_for_errors: ret Error"
        return "ERROR"
    else:
        print "check_for_errors: ret", retstr
        return retstr
# SAX Handlers

class CheckForErrors(handler.ContentHandler):

    def __init__(self):
        self.errFound = 0
        self.inDesc = 0
        self.buffer = []
        self.retList = []

    def startElement(self, name, attributes):
        if name == "error":
            self.errFound = 1
        if name == "desc":
            self.inDesc = 1
            self.buffer = []

    def characters(self, ch):
        if self.inDesc:
            self.buffer.append(ch)

    def endElement(self, name):
        if name == "desc":
            descr = string.join(self.buffer)            
            self.retList.append(descr.encode("ascii"))
            
    def get_return_string(self):
        if self.errFound == 1:
            ret = str(self.retList[0])
        else:
            ret = "OK"
        print "parser returning: ", ret
        return ret
            
# used to build initial device dict/list
class DevCoreParser_ReturnAll(handler.ContentHandler, \
                            handler.DTDHandler):

    def __init__(self):
        self.inDevMac = 0
        self.inCwId = 0
        self.inDevType = 0
        self.inConnStatus = 0
        self.inPanId = 0
        self.buffer = []
        self.retDict = {}
        self.tmpDict = {}
        self.retList = []
        self.tmpList = []
        self.iCurr = 0

    def startDocument(self):
        pass #print "--------  Document Start --------"
        
    def startElement(self, name, attributes):
        if name == "devMac":
            self.inDevMac = 1
            self.buffer = []
        if name == "devConnectwareId":
            self.inCwId = 1
            self.buffer = []
        if name == "dpDeviceType":
            self.inDevType = 1
            self.buffer = []
        if name == "dpConnectionStatus":
            self.buffer = []
            self.inConnStatus = 1
        if name == "dpPanId":
            self.buffer = []
            self.inPanId = 1

    def characters(self, ch):
        if self.inDevMac:
            self.buffer.append(ch)
        if self.inCwId:
            self.buffer.append(ch)
        if self.inDevType:
            self.buffer.append(ch)
        if self.inConnStatus:
            self.buffer.append(ch)
        if self.inPanId:
            self.buffer.append(ch)

    def endElement(self, name):
        if name == "DeviceCore":
            self.retDict[self.curKey] = self.tmpDict
            self.retList.insert(self.iCurr, self.tmpList)
            self.tmpList = []
            self.tmpDict = {}
            self.iCurr += 1
        if name == "devMac":
            self.inDevMac = 0
            tmpList = []
            macdata = string.join(self.buffer)
            self.tmpList.insert(1, macdata.encode("ascii"))
            self.tmpDict[name.encode("ascii")] = macdata.encode("ascii")
        if name == "devConnectwareId":
            self.inCwId = 0
            tmpList = []
            namedata = string.join(self.buffer)
            self.curKey = namedata.encode("ascii")
            self.tmpList.insert(0, namedata.encode("ascii"))
            self.tmpDict[name.encode("ascii")] = namedata.encode("ascii")
        if name == "dpDeviceType":
            self.inDevType = 0
            tmpList = []
            dtdata = string.join(self.buffer)            
            self.tmpList.insert(2, dtdata.encode("ascii"))
            self.tmpDict[name.encode("ascii")] = dtdata.encode("ascii")
        if name == "dpConnectionStatus":
            self.inConnStatus = 0
            tmpList = []
            csdata = string.join(self.buffer)            
            self.tmpList.insert(3, csdata.encode("ascii"))
            self.tmpDict[name.encode("ascii")] = csdata.encode("ascii")
        if name == "dpPanId":
            self.inPanId = 0
            tmpList = []
            pandata = string.join(self.buffer)
            self.tmpList.insert(4, pandata.encode("ascii"))
            self.tmpDict[name.encode("ascii")] = pandata.encode("ascii")

    def endDocument(self):
        pass #print "--------  Document End --------"

    #def get_return_list(self):
    #    print self.retList
    #    return self.retList

    def get_return_dict(self):
        return self.retDict

class QueryZBStateParser_FindDevice(handler.ContentHandler, \
                               handler.DTDHandler):

    def __init__(self, findCwid=""):
        self.inCwId = 0
        self.inConnStatus = 0
        self.inPanId = 0
        self.buffer = []
        self.findCwid = findCwid
        self.storeData = 0
        self.retList = []

    def startElement(self, name, attributes):
        if name == "devConnectwareId":
            self.inCwId = 1
            self.buffer = []
        if name == "dpConnectionStatus":
            if (self.storeData == 1):
                self.buffer = []
                self.inConnStatus = 1
        if name == "dpPanId":
            if (self.storeData == 1):
                self.buffer = []
                self.inPanId = 1

    def skippedEntity(self, name):
        print name

    def characters(self, ch):
        if self.inCwId:
            self.buffer.append(ch)
        if self.inConnStatus:
            self.buffer.append(ch)
        if self.inPanId:
            self.buffer.append(ch)

    def endElement(self, name):
        if name == "devConnectwareId":
            self.inCwId = 0
            namedata = string.join(self.buffer)
            if (namedata == normalize_whitespace(self.findCwid)):
                self.retList.insert(0, namedata.encode("ascii"))
                self.storeData = 1
            else:
                self.storeData = 0
        if name == "dpConnectionStatus":
            self.inConnStatus = 0
            csdata = string.join(self.buffer)            
            if (self.storeData == 1):
                self.retList.insert(1, csdata.encode("ascii"))
        if name == "dpPanId":
            self.inPanId = 0
            pandata = string.join(self.buffer)
            if (self.storeData == 1):
                self.retList.insert(2, pandata.encode("ascii"))

    def get_return_list(self):
        return self.retList

# XML SAX HANDLERS (for parsing functions)
# eventually handle <device_info>, <serial_stats>, <net_stats>
# etc.  For now, handle only <zigbee_state>
class Query_Zigbee_State_Parser(handler.ContentHandler, \
                                  handler.DTDHandler):

    def __init__(self, deviceId=""):
        self.inGwAddr = 0
        self.inDevice = 0
        self.inCaps = 0
        self.inExtPan = 0
        self.inChannel = 0
        self.inNetAddr = 0
        self.inAssoc = 0
        self.inFwVer = 0
        self.inHwVer = 0
        self.inDevType = 0
        self.inChild = 0
        self.inMaxPay = 0
        self.inRssi = 0
        self.buffer = []
        self.retDict = {}
        self.retList = []
        self.devId = deviceId
        self.iCurr = 0

    def startDocument(self):
        #print "--------  Document Start --------"
        pass 

    def resolveEntity(self, publicId, systemId):
        #print " ** resolveEntity  **"
        pass

    def skippedEntity(self, name):
        #print name
        pass

    def startElement(self, name, attributes):
        if name == "device":
            self.inDevice = 1
            self.cwid = attributes.get("id");
        #   print "CWID = ", self.cwid
        #if name == "desc":
        #    self.buffer = []
        #    self.inDesc = 1           
        if name == "gateway_addr":
            self.buffer = []
            self.inGwAddr = 1
        #if name == "caps":
        #    self.buffer = []
        #    self.inCaps = 1            
        #if name == "ext_pan_id":
        #    self.buffer = []
        #    self.inExtPan = 1
        #if name == "channel":
        #    self.buffer = []
        #    self.inChannel = 1
        #if name == "net_addr":
        #    self.buffer = []
        #    self.inNetAddr = 1
        if name == "association":
            self.buffer = []
            self.inAssoc = 1
        if name == "firmware_version":
            self.buffer = []
            self.inFwVer = 1
        if name == "hardware_version":
            self.buffer = []
            self.inHwVer = 1
        #if name == "device_type":
        #    self.buffer = []
        #    self.inDevType = 1
        #if name == "children":
        #    self.buffer = []
        #    self.inChild = 1
        #if name == "max_payload":
        #    self.buffer = []
        #    self.inMaxPay = 1    
        #if name == "rssi":
        #    self.buffer = []
        #    self.inRssi = 1
            
    def characters(self, ch):
        if self.inGwAddr:
            self.buffer.append(ch)
        #if self.inCaps:
        #    self.buffer.append(ch)
        #if self.inExtPan:
        #    self.buffer.append(ch)
        #if self.inChannel:
        #    self.buffer.append(ch)
        #if self.inNetAddr:
        #    self.buffer.append(ch)
        if self.inAssoc:
            self.buffer.append(ch)
        if self.inFwVer:
            self.buffer.append(ch)
        if self.inHwVer:
            self.buffer.append(ch)
       #if self.inDevType:
       #     self.buffer.append(ch)
       #if self.inChild:
       #     self.buffer.append(ch)
       #if self.inMaxPay:
       #     self.buffer.append(ch)
       # if self.inRssi:
       #     self.buffer.append(ch)

    # Note, that I'm using insert with an index here
    # since not all devices being parsed have all of
    # the same possible elements (if online, offline, etc)
    def endElement(self, name):
        if name == "device":
            self.inDevice = 0
            data = ""
            data = self.cwid.encode("ascii")
            self.retDict[name.encode("ascii")] = data
        if name == "gateway_addr":
            self.inGwAddr = 0
            gwdata = string.join(self.buffer)
            self.retDict[name.encode("ascii")] = gwdata.encode("ascii")
        #if name == "caps":
        #    self.inCaps = 0
        #    capdata = string.join(self.buffer)
        #    self.retList.insert(2, capdata.encode("ascii"))
        #if name == "ext_pan_id":
        #    self.inExtPan = 0
        #    epiddata = string.join(self.buffer)            
        #    self.retList.insert(3, epiddata.encode("ascii"))
        #if name == "channel":
        #    self.inChannel = 0
        #    chdata = string.join(self.buffer)
        #    self.retList.insert(4, chdata.encode("ascii"))
        #if name == "net_addr":
        #    self.inNetAddr = 0
        #    nadata = string.join(self.buffer)
        #    self.retList.insert(5, nadata.encode("ascii"))
        if name == "association":
            self.inAssoc = 0
            asscdata = string.join(self.buffer)
            self.retDict[name.encode("ascii")] = asscdata.encode("ascii")
        if name == "firmware_version":
            self.inFwVer = 0
            fwdata = string.join(self.buffer)
            self.retDict[name.encode("ascii")]= fwdata.encode("ascii")
        if name == "hardware_version":
            self.inHwVer = 0
            hwdata = string.join(self.buffer)
            self.retDict[name.encode("ascii")] = hwdata.encode("ascii")
        #if name == "device_type":
        #    self.inDevType = 0
        #    dtdata = string.join(self.buffer)
        #   self.retList.insert(9, dtdata.encode("ascii"))
        #if name == "children":
        #    self.inDevType = 0
        #    chdata = string.join(self.buffer)
        #    self.retList.insert(10, chdata.encode("ascii"))
        #if name == "max_payload":
        #    self.inMaxPay = 0
        #    maxdata = string.join(self.buffer)
        #    self.retList.insert(11, maxdata.encode("ascii"))
        #if name == "rssi":
        #    self.inRssi = 0
        #    rssidata = string.join(self.buffer)
        #    self.retList.insert(12, rssidata.encode("ascii"))
            
    def endDocument(self):
        pass #print "--------  Document End --------"
            
    def get_return_dict(self):
        return self.retDict
