# License: GPLv3 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
