from hachoir.field import Bytes
from hachoir.core.tools import makePrintable, humanFilesize
from hachoir.stream import InputIOStream


class SubFile(Bytes):
    """
    File stored in another file
    """

    def __init__(self, parent, name, length, description=None,
                 parser=None, filename=None, mime_type=None, parser_class=None):
        if filename:
            if not isinstance(filename, str):
                filename = makePrintable(filename, "ISO-8859-1")
            if not description:
                description = 'File "%s" (%s)' % (
                    filename, humanFilesize(length))
        Bytes.__init__(self, parent, name, length, description)

        def createInputStream(cis, **args):
            tags = args.setdefault("tags", [])
            if parser_class:
                tags.append(("class", parser_class))
            if parser is not None:
                tags.append(("id", parser.PARSER_TAGS["id"]))
            if mime_type:
                tags.append(("mime", mime_type))
            if filename:
                tags.append(("filename", filename))
            return cis(**args)
        self.setSubIStream(createInputStream)


class CompressedStream:
    offset = 0

    def __init__(self, stream, decompressor):
        self.stream = stream
        self.decompressor = decompressor(stream)
        self._buffer = b''

    def read(self, size):
        d = self._buffer
        data = [d[:size]]
        size -= len(d)
        if size > 0:
            d = self.decompressor(size)
            data.append(d[:size])
            size -= len(d)
            while size > 0:
                n = 4096
                if self.stream.size:
                    n = min(self.stream.size - self.offset, n)
                    if not n:
                        break
                d = self.stream.read(self.offset, n)[1]
                self.offset += 8 * len(d)
                d = self.decompressor(size, d)
                data.append(d[:size])
                size -= len(d)
        self._buffer = d[size + len(d):]
        return b''.join(data)


def CompressedField(field, decompressor):
    def createInputStream(cis, source=None, **args):
        if field._parent:
            stream = cis(source=source)
            args.setdefault("tags", []).extend(stream.tags)
        else:
            stream = field.stream
        input = CompressedStream(stream, decompressor)
        if source is None:
            source = "Compressed source: '%s' (offset=%s)" % (
                stream.source, field.absolute_address)
        return InputIOStream(input, source=source, **args)
    field.setSubIStream(createInputStream)
    return field
