__license__   = 'GPL v3'
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
''''''

import struct

from calibre.ebooks.lrf import LRFParseError


class Tag:

    tags = {
                0x00 : (6, '*ObjectStart'),
                0x01 : (0, '*ObjectEnd'),
                0x02 : (4, '*ObjectInfoLink'),
                0x03 : (4, '*Link'),
                0x04 : (4, '*StreamSize'),
                0x05 : (0, '*StreamStart'),
                0x06 : (0, '*StreamEnd'),
                0x07 : (4, None),
                0x08 : (4, None),
                0x09 : (4, None),
                0x0A : (4, None),
                0x0B : ('type_one', '*ContainedObjectsList'),
                0x0D : (2, None),
                0x0E : (2, None),
                0x11 : (2, None),
                0x12 : (2, None),
                0x13 : (2, None),
                0x14 : (2, None),
                0x15 : (2, None),
                0x16 : ('string', None),
                0x17 : (4, None),
                0x18 : (4, None),
                0x19 : (2, None),
                0x1A : (2, None),
                0x1B : (2, None),
                0x1C : (2, None),
                0x1D : (2, None),
                0x1E : (2, None),
                0x21 : (2, None),
                0x22 : (2, None),
                0x23 : (2, None),
                0x24 : (2, None),
                0x25 : (2, None),
                0x26 : (2, None),
                0x27 : (2, None),
                0x28 : (2, None),
                0x29 : (6, None),
                0x2A : (2, None),
                0x2B : (2, None),
                0x2C : (2, None),
                0x2D : (4, None),
                0x2E : (2, None),
                0x31 : (2, None),
                0x32 : (2, None),
                0x33 : (2, None),
                0x34 : (4, None),
                0x35 : (2, None),
                0x36 : (2, None),
                0x37 : (4, None),
                0x38 : (2, None),
                0x39 : (2, None),
                0x3A : (2, None),
                0x3C : (2, None),
                0x3D : (2, None),
                0x3E : (2, None),
                0x41 : (2, None),
                0x42 : (2, None),
                0x44 : (4, None),
                0x45 : (4, None),
                0x46 : (2, None),
                0x47 : (2, None),
                0x48 : (2, None),
                0x49 : (8, None),
                0x4A : (8, None),
                0x4B : (4, None),
                0x4C : (4, None),
                0x4D : (0, None),
                0x4E : (12, None),
                0x51 : (2, None),
                0x52 : (2, None),
                0x53 : (4, None),
                0x54 : (2, '*StreamFlags'),
                0x55 : ('string', None),
                0x56 : (2, None),
                0x57 : (2, None),
                0x58 : (2, None),
                0x59 : ('string', None),
                0x5A : ('string', None),
                0x5B : (4, None),
                0x5C : ('type_one', None),
                0x5D : ('string', None),
                0x5E : (2, None),
                0x61 : (2, None),
                0x62 : (0, None),
                0x63 : (0, None),
                0x64 : (0, None),
                0x65 : (0, None),
                0x66 : (0, None),
                0x67 : (0, None),
                0x68 : (0, None),
                0x69 : (0, None),
                0x6A : (0, None),
                0x6B : (0, None),
                0x6C : (8, None),
                0x6D : (2, None),
                0x6E : (0, None),
                0x71 : (0, None),
                0x72 : (0, None),
                0x73 : (10, None),
                0x75 : (2, None),
                0x76 : (2, None),
                0x77 : (2, None),
                0x78 : ('tag_78', None),
                0x79 : (2, None),
                0x7A : (2, None),
                0x7B : (4, None),
                0x7C : (4, '*ParentPageTree'),
                0x81 : (0, None),
                0x82 : (0, None),
                0xA1 : (4, None),
                0xA2 : (0, None),
                0xA5 : ('unknown', None),
                0xA6 : (0, None),
                0xA7 : (4, None),
                0xA8 : (0, None),
                0xA9 : (0, None),
                0xAA : (0, None),
                0xAB : (0, None),
                0xAC : (0, None),
                0xAD : (0, None),
                0xAE : (0, None),
                0xB1 : (0, None),
                0xB2 : (0, None),
                0xB3 : (0, None),
                0xB4 : (0, None),
                0xB5 : (0, None),
                0xB6 : (0, None),
                0xB7 : (0, None),
                0xB8 : (0, None),
                0xB9 : (0, None),
                0xBA : (0, None),
                0xBB : (0, None),
                0xBC : (0, None),
                0xBD : (0, None),
                0xBE : (0, None),
                0xC1 : (0, None),
                0xC2 : (0, None),
                0xC3 : (2, None),
                0xC4 : (0, None),
                0xC5 : (2, None),
                0xC6 : (2, None),
                0xC7 : (0, None),
                0xC8 : (2, None),
                0xC9 : (0, None),
                0xCA : (2, None),
                0xCB : ('unknown', None),
                0xCC : (2, None),
                0xD1 : (12, None),
                0xD2 : (0, None),
                0xD4 : (2, None),
                0xD6 : (0, None),
                0xD7 : (14, None),
                0xD8 : (4, None),
                0xD9 : (8, None),
                0xDA : (2, None),
                0xDB : (2, None),
                0xDC : (2, None),
                0xDD : (2, None),
                0xF1 : (2, None),
                0xF2 : (4, None),
                0xF3 : (4, None),
                0xF4 : (2, None),
                0xF5 : (4, None),
                0xF6 : (4, None),
                0xF7 : (4, None),
                0xF8 : (4, None),
                0xF9 : (6, None),
            }
    name_map = {}
    for key in tags.keys():
        temp = tags[key][1]
        if temp is not None:
            name_map[key] = temp

    def __init__(self, stream):
        self.offset = stream.tell()
        tag_id = struct.unpack('<BB', stream.read(2))
        if tag_id[1] != 0xF5:
            raise LRFParseError(f'Bad tag ID {tag_id[1]:02X} at {self.offset}')
        if tag_id[0] not in self.__class__.tags:
            raise LRFParseError(f'Unknown tag ID: F5{tag_id[0]:02X}')

        self.id = 0xF500 + tag_id[0]

        size, self.name = self.__class__.tags[tag_id[0]]
        if isinstance(size, str):
            parser = getattr(self, size + '_parser')
            self.contents = parser(stream)
        else:
            self.contents = stream.read(size)

    def __str__(self):
        s = f'Tag {self.id:04X} '
        if self.name:
            s += self.name
        s += f' at {self.offset:08X}, contents: {self.contents!r}'
        return s

    @property
    def byte(self):
        if len(self.contents) != 1:
            raise LRFParseError(f'Bad parameter for tag ID: {self.id:04X}')
        return struct.unpack('<B', self.contents)[0]

    @property
    def word(self):
        if len(self.contents) != 2:
            raise LRFParseError(f'Bad parameter for tag ID: {self.id:04X}')
        return struct.unpack('<H', self.contents)[0]

    @property
    def sword(self):
        if len(self.contents) != 2:
            raise LRFParseError(f'Bad parameter for tag ID: {self.id:04X}')
        return struct.unpack('<h', self.contents)[0]

    @property
    def dword(self):
        if len(self.contents) != 4:
            raise LRFParseError(f'Bad parameter for tag ID: {self.id:04X}')
        return struct.unpack('<I', self.contents)[0]

    def dummy_parser(self, stream):
        raise LRFParseError(f'Unknown tag at {stream.tell():08X}')

    @classmethod
    def string_parser(self, stream):
        size = struct.unpack('<H', stream.read(2))[0]
        return str(stream.read(size), 'utf_16')

    def type_one_parser(self, stream):
        cnt = struct.unpack('<H', stream.read(2))[0]
        res = []
        while cnt > 0:
            res.append(struct.unpack('<I', stream.read(4))[0])
            cnt -= 1
        return res

    def tag_78_parser(self, stream):
        pos = stream.tell()
        res = []
        res.append(struct.unpack('<I', stream.read(4))[0])
        tag = Tag(stream)
        if tag.id != 0xF516:
            raise LRFParseError(f'Bad tag 78 at {pos:08X}')
        res.append(tag.contents)
        res.append(struct.unpack('<H', stream.read(2))[0])
        return res
