"""MAR (Mozilla ARchive) parser

Author: Robert Xiao
Creation date: July 10, 2007

"""

from hachoir.core.endian import BIG_ENDIAN
from hachoir.field import (RootSeekableFieldSet, FieldSet,
                           String, CString, UInt32, RawBytes)
from hachoir.core.text_handler import displayHandler, filesizeHandler
from hachoir.core.tools import humanUnixAttributes
from hachoir.parser import HachoirParser


class IndexEntry(FieldSet):

    def createFields(self):
        yield UInt32(self, "offset", "Offset in bytes relative to start of archive")
        yield filesizeHandler(UInt32(self, "length", "Length in bytes"))
        yield displayHandler(UInt32(self, "flags"), humanUnixAttributes)
        yield CString(self, "name", "Filename (byte array)")

    def createDescription(self):
        return 'File %s, Size %s, Mode %s' % (
            self["name"].display, self["length"].display, self["flags"].display)


class MozillaArchive(HachoirParser, RootSeekableFieldSet):
    MAGIC = b"MAR1"
    PARSER_TAGS = {
        "id": "mozilla_ar",
        "category": "archive",
        "file_ext": ("mar",),
        "min_size": (8 + 4 + 13) * 8,  # Header, Index Header, 1 Index Entry
        "magic": ((MAGIC, 0),),
        "description": "Mozilla Archive",
    }
    endian = BIG_ENDIAN

    def __init__(self, stream, **args):
        RootSeekableFieldSet.__init__(
            self, None, "root", stream, None, stream.askSize(self))
        HachoirParser.__init__(self, stream, **args)

    def validate(self):
        if self.stream.readBytes(0, 4) != self.MAGIC:
            return "Invalid magic"
        return True

    def createFields(self):
        yield String(self, "magic", 4, "File signature (MAR1)", charset="ASCII")
        yield UInt32(self, "index_offset", "Offset to index relative to file start")
        self.seekByte(self["index_offset"].value, False)
        yield UInt32(self, "index_size", "size of index in bytes")
        current_index_size = 0  # bytes
        while current_index_size < self["index_size"].value:
            # plus 4 compensates for index_size
            self.seekByte(self["index_offset"].value +
                          current_index_size + 4, False)
            entry = IndexEntry(self, "index_entry[]")
            yield entry
            current_index_size += entry.size // 8
            self.seekByte(entry["offset"].value, False)
            yield RawBytes(self, "file[]", entry["length"].value)
