"""
AMF metadata (inside Flash video, FLV file) parser.

Documentation:

 - flashticle: Python project to read Flash (formats SWF, FLV and AMF)
   http://undefined.org/python/#flashticle

Author: Victor Stinner
Creation date: 4 november 2006
"""

from hachoir.field import (FieldSet, ParserError,
                           UInt8, UInt16, UInt32, PascalString16, Float64)
from hachoir.core.tools import timestampUNIX


def parseUTF8(parent):
    yield PascalString16(parent, "value", charset="UTF-8")


def parseDouble(parent):
    yield Float64(parent, "value")


def parseBool(parent):
    yield UInt8(parent, "value")


def parseArray(parent):
    yield UInt32(parent, "count")
    for index in range(parent["count"].value):
        yield AMFObject(parent, "item[]")


def parseObjectAttributes(parent):
    while True:
        item = Attribute(parent, "attr[]")
        yield item
        if item["key"].value == "":
            break


def parseMixedArray(parent):
    yield UInt32(parent, "count")
    for index in range(parent["count"].value + 1):
        item = Attribute(parent, "item[]")
        yield item
        if not item['key'].value:
            break


def parseDate(parent):
    yield Float64(parent, "timestamp_microsec")
    yield UInt16(parent, "timestamp_sec")


def parseNothing(parent):
    # Python hack to make sure that the function is a generator
    if 0:
        yield


class AMFObject(FieldSet):
    CODE_DATE = 11
    tag_info = {
        # http://osflash.org/amf/astypes
        0: (parseDouble, "Double"),
        1: (parseBool, "Boolean"),
        2: (parseUTF8, "UTF-8 string"),
        3: (parseObjectAttributes, "Object attributes"),
        # MOVIECLIP = '\x04',
        # NULL = '\x05',
        # UNDEFINED = '\x06',
        # REFERENCE = '\x07',
        8: (parseMixedArray, "Mixed array"),
        9: (parseNothing, "End of object"),
        10: (parseArray, "Array"),
        CODE_DATE: (parseDate, "Date"),
        # LONGUTF8 = '\x0c',
        # UNSUPPORTED = '\x0d',
        #  Server-to-client only
        # RECORDSET = '\x0e',
        # XML = '\x0f',
        # TYPEDOBJECT = '\x10',
    }

    def __init__(self, *args, **kw):
        FieldSet.__init__(self, *args, **kw)
        code = self["type"].value
        try:
            self.parser, desc = self.tag_info[code]
            if code == self.CODE_DATE:
                self.createValue = self.createValueDate
        except KeyError:
            raise ParserError("AMF: Unable to parse type %s" % code)

    def createFields(self):
        yield UInt8(self, "type")
        yield from self.parser(self)

    def createValueDate(self):
        value = (self["timestamp_microsec"].value * 0.001) \
            - (self["timestamp_sec"].value * 60)
        return timestampUNIX(value)


class Attribute(AMFObject):

    def __init__(self, *args):
        AMFObject.__init__(self, *args)
        self._description = None

    def createFields(self):
        yield PascalString16(self, "key", charset="UTF-8")
        yield UInt8(self, "type")
        yield from self.parser(self)

    def createDescription(self):
        return 'Attribute "%s"' % self["key"].value
