#!/usr/bin/python -u
# -*- coding: utf-8 -*-


from defs import *
from stack import NamespaceStack
from string import StringBlock

from struct import unpack


class AXMLParser():


    def __init__(self, reader):

        self._reader = reader

        magic = reader.readInt()
        if magic != CHUNK_AXML_FILE:
            raise Exception('Bad Magic Number (0x%08lx)!' % magic)

        # chunkSize
        reader.skipInt()

        self._strings = StringBlock(reader)
        self._namespaces = NamespaceStack()
        self._operational = True

        self.resetEventInfo()


    def resetEventInfo(self):

        self._event = -1
        self._line_number = -1
        self._name = -1
        self._namespace_uri = -1
        self._attributes = None
        self._id_attrib = -1
        self._class_attrib = -1
        self._style_attrib = -1

        self._decreaseDepth = False



    def next(self):

        self.doNext()
        return self._event


    def doNext(self):





        event = self._event


        while True:

            if self._decreaseDepth:
                self._decreaseDepth = False
                self._namespaces.decDepth()

            # Fake END_DOCUMENT event.
            if event == END_TAG and self._namespaces.getDepth() == 1 and self._namespaces.count() == 0:
                self._event = END_DOCUMENT
                break

            if event == START_DOCUMENT:
                # Fake event, see CHUNK_XML_START_TAG handler.
                chunk = CHUNK_XML_START_TAG

            else:
                chunk = self._reader.readInt()

            if chunk == CHUNK_RESOURCEIDS:

                size = self._reader.readInt()
                if size < 8 or size % 4 != 0:
                    raise Exception('Invalid resource ids size (%d).' % size)

                self._resource_ids = self._reader.readIntArray(size / 4 - 2)

                continue

            if chunk < CHUNK_XML_FIRST or chunk > CHUNK_XML_LAST:
                raise Exception('Invalid chunk type 0x%08lx.' % chunk)

            # Fake START_DOCUMENT event.
            if chunk == CHUNK_XML_START_TAG and event == -1:
                self._event = START_DOCUMENT
                break

            # Common header.
            self._reader.skipInt() # chunkSize
            self._line_number = self._reader.readInt()
            self._reader.skipInt() # 0xffffffff

            if chunk == CHUNK_XML_START_NAMESPACE or chunk == CHUNK_XML_END_NAMESPACE:

                if chunk == CHUNK_XML_START_NAMESPACE:

                    prefix = self._reader.readInt()
                    uri = self._reader.readInt()
                    self._namespaces.push(prefix, uri)

                else:

                    self._reader.skipInt() # prefix
                    self._reader.skipInt() # uri
                    self._namespaces.pop()

                continue

            elif chunk == CHUNK_XML_START_TAG:

                self._namespace_uri = self._reader.readInt()
                self._name = self._reader.readInt()
                self._reader.skipInt() # flags ?

                attribs_count = self._reader.readInt()
                self._id_attrib = (attribs_count >> 16) - 1
                attribs_count &= 0xffff

                self._class_attrib = self._reader.readInt()
                self._style_attrib = (self._class_attrib >> 16) - 1
                self._class_attrib = (self._class_attrib & 0xffff) - 1

                self._attributes = self._reader.readIntArray(attribs_count * ATTRIBUTE_LENGHT)

                for i in range(ATTRIBUTE_IX_VALUE_TYPE, len(self._attributes), ATTRIBUTE_LENGHT):
                    self._attributes[i] >>= 24

                self._namespaces.incDepth()
                self._event = START_TAG

                break

            elif chunk == CHUNK_XML_END_TAG:

                self._namespaceUri = self._reader.readInt()
                self._name = self._reader.readInt()

                self._event = END_TAG
                self._decreaseDepth = True

                break

            elif chunk == CHUNK_XML_TEXT:

                self._name = self._reader.readInt()
                self._reader.skipInt() # ???
                self._reader.skipInt() # ???

                self._event=TEXT

                break				

            else:
                raise Exception('Unknown chunck (0x%08lx)' % chunk)


    ### ESPACES ###

    def getNamespacePrefix(self, index):

        name = self._namespaces.getPrefix(index)

        if name == -1:
            return ''

        else:
            return self._strings.getRaw(name)


    def getNamespaceUri(self, index):

        name = self._namespaces.getUri(index)

        if name == -1:
            return ''

        else:
            return self._strings.getRaw(name)


    ### NAMES ###

    def getTagPrefix(self):
        """Provide the prefix linked to START_TAG or END_TAG."""

        name = self._namespaces.findPrefix(self._namespace_uri)

        if name == -1:
            return ''

        else:
            return self._strings.getRaw(name) + ':'


    def getTagName(self):
        """Provide the name linked to START_TAG or END_TAG."""

        if self._name == -1 or (self._event != START_TAG and self._event != END_TAG):
            raise Exception('Invalid tag name.')

        return self._strings.getRaw(self._name)


    def getText(self):
        """Provide the content linked to TEXT."""

        if self._name == -1 or self._event != START_TEXT:
            raise Exception('Invalid text content.')

        return self._strings.getRaw(self._name)


    ### ATRIBUTES ###

    def countAttributes(self):
        """Count the properties of the current tag."""

        if self._event != START_TAG:
            raise Exception('Invalid event.')

        return len(self._attributes) / ATTRIBUTE_LENGHT


    def getAttribPrefix(self, index):
        """Get the prefix of a given attribute."""

        index *= ATTRIBUTE_LENGHT

        if index >= len(self._attributes):
            raise Exception('Bad attribute index.')

        uri = self._attributes[index + ATTRIBUTE_IX_NAMESPACE_URI]
        name = self._namespaces.findPrefix(uri)

        if name == -1:
            return ''

        else:
            return self._strings.getRaw(name) + ':'


    def getAttribName(self, index):
        """Get the name of a given attribute."""

        index *= ATTRIBUTE_LENGHT

        if index >= len(self._attributes):
            raise Exception('Bad attribute index.')

        name = self._attributes[index + ATTRIBUTE_IX_NAME]

        if name == -1:
            return '???'

        else:
            return self._strings.getRaw(name)


    def getAttribValue(self, index):
        """Get the value of a given attribute."""

        index *= ATTRIBUTE_LENGHT

        if index >= len(self._attributes):
            raise Exception('Bad attribute index.')

        vtype = self._attributes[index + ATTRIBUTE_IX_VALUE_TYPE]
        vdata = self._attributes[index + ATTRIBUTE_IX_VALUE_DATA]

        if vtype == TYPE_NULL:
            return '???'

        elif vtype == TYPE_REFERENCE:
            return '@%s%08X' % (self.getPackage(vdata), vdata)

        elif vtype == TYPE_ATTRIBUTE:
            return '?%s%08X' % (self.getPackage(vdata), vdata)

        if vtype == TYPE_STRING:
            vdata = self._attributes[index + ATTRIBUTE_IX_VALUE_STRING]
            return self._strings.getRaw(vdata)

        elif vtype == TYPE_FLOAT:
            return '%f' % unpack('=f', pack('=L', vdata))[0] 

        elif vtype == TYPE_DIMENSION:
            return '%f%s' % (self.complexToFloat(vdata), DIMENSION_UNITS[vdata & COMPLEX_UNIT_MASK])

        elif vtype == TYPE_FRACTION:
            return '%f%s' % (self.complexToFloat(vdata), FRACTION_UNITS[vdata & COMPLEX_UNIT_MASK])

        elif vtype == TYPE_INT_HEX:
            return '0x%08x' % vdata

        elif vtype == TYPE_INT_BOOLEAN:
            if vdata == 0:
                return 'false'
            else:
                return 'true'

        elif vtype >= TYPE_FIRST_COLOR_INT and vtype <= TYPE_LAST_COLOR_INT:
            return '#%08x' % vdata

        elif vtype >= TYPE_FIRST_INT and vtype <= TYPE_LAST_INT:
            return str(vdata)

        return "<0x%x, 0x%02x>" % (vdata, vtype)


    def complexToFloat(self, xcomplex):
        return (float)(xcomplex & 0xffffff00) * RADIX_MULTS[(xcomplex >> 4) & 3];

    def getPackage(self, id):
        if id >> 24 == 1:
            return "android:"
        else:
            return ""