# This file is part of RinohType, the Python document preparation system.
#
# Copyright (c) Brecht Machiels.
#
# Use of this source code is subject to the terms of the GNU Affero General
# Public License v3. See the LICENSE file or http://www.gnu.org/licenses/.
 
 
from .parse import OpenTypeTable, MultiFormatTable, Record, context_array
from .parse import fixed, array, uint16, tag, glyph_id, offset, indirect, Packed
 
 
class ListRecord(Record):
    entries = [('Tag', tag),
               ('Offset', offset)]
 
    def parse_value(self, file, file_offset, entry_type):
        self['Value'] = entry_type(file, file_offset + self['Offset'])
 
 
class ListTable(OpenTypeTable):
    entry_type = None
    entries = [('Count', uint16),
               ('Record', context_array(ListRecord, 'Count'))]
 
    def __init__(self, file, file_offset):
        super().__init__(file, file_offset)
        self.by_tag = {}
        for record in self['Record']:
            record.parse_value(file, file_offset, self.entry_type)
            tag_list = self.by_tag.setdefault(record['Tag'], [])
            tag_list.append(record['Value'])
 
 
class LangSysTable(OpenTypeTable):
    entries = [('LookupOrder', offset),
               ('ReqFeatureIndex', uint16),
               ('FeatureCount', uint16),
               ('FeatureIndex', context_array(uint16, 'FeatureCount'))]
 
 
class ScriptTable(ListTable):
    entry_type = LangSysTable
    entries = [('DefaultLangSys', indirect(LangSysTable))] + ListTable.entries
 
 
class ScriptListTable(ListTable):
    entry_type = ScriptTable
 
 
class FeatureTable(OpenTypeTable):
    entries = [('FeatureParams', offset),
               ('LookupCount', uint16),
               ('LookupListIndex', context_array(uint16, 'LookupCount'))]
 
    def __init__(self, file, offset):
        super().__init__(file, offset)
        if self['FeatureParams']:
            # TODO: parse Feature Parameters
            pass
        else:
            del self['FeatureParams']
 
 
class FeatureListTable(ListTable):
    entry_type = FeatureTable
 
 
class LookupFlag(Packed):
    reader = uint16
    fields = [('RightToLeft', 0x0001, bool),
              ('IgnoreBaseGlyphs', 0x0002, bool),
              ('IgnoreLigatures', 0x0004, bool),
              ('IgnoreMarks', 0x0008, bool),
              ('UseMarkFilteringSet', 0x010, bool),
              ('MarkAttachmentType', 0xFF00, int)]
 
 
class RangeRecord(OpenTypeTable):
    entries = [('Start', glyph_id),
               ('End', glyph_id),
               ('StartCoverageIndex', uint16)]
 
 
class Coverage(MultiFormatTable):
    entries = [('CoverageFormat', uint16)]
    formats = {1: [('GlyphCount', uint16),
                   ('GlyphArray', context_array(glyph_id, 'GlyphCount'))],
               2: [('RangeCount', uint16),
                   ('RangeRecord', context_array(RangeRecord, 'RangeCount'))]}
 
    def index(self, glyph_id):
        if self['CoverageFormat'] == 1:
            return self['GlyphArray'].index(glyph_id)
        else:
            for record in self['RangeRecord']:
                if record['Start'] <= glyph_id <= record['End']:
                    return (record['StartCoverageIndex']
                            + glyph_id - record['Start'])
            raise ValueError
 
 
class ClassRangeRecord(OpenTypeTable):
    entries = [('Start', glyph_id),
               ('End', glyph_id),
               ('Class', uint16)]
 
 
class ClassDefinition(MultiFormatTable):
    entries = [('ClassFormat', uint16)]
    formats = {1: [('StartGlyph', glyph_id),
                   ('GlyphCount', uint16),
                   ('ClassValueArray', context_array(uint16, 'GlyphCount'))],
               2: [('ClassRangeCount', uint16),
                   ('ClassRangeRecord', context_array(ClassRangeRecord,
                                                      'ClassRangeCount'))]}
 
    def class_number(self, glyph_id):
        if self['ClassFormat'] == 1:
            index = glyph_id - self['StartGlyph']
            if 0 <= index < self['GlyphCount']:
                return self['ClassValueArray'][index]
        else:
            for record in self['ClassRangeRecord']:
                if record['Start'] <= glyph_id <= record['End']:
                    return record['Class']
        return 0
 
 
class LookupTable(OpenTypeTable):
    entries = [('LookupType', uint16),
               ('LookupFlag', LookupFlag),
               ('SubTableCount', uint16)]
 
    def __init__(self, file, file_offset, subtable_types):
        super().__init__(file, file_offset)
        offsets = array(uint16, self['SubTableCount'])(file)
        if self['LookupFlag']['UseMarkFilteringSet']:
            self['MarkFilteringSet'] = uint16(file)
        subtable_type = subtable_types[self['LookupType']]
        self['SubTable'] = [subtable_type(file, file_offset + subtable_offset)
                            for subtable_offset in offsets]
 
    def lookup(self, *args, **kwargs):
        for subtable in self['SubTable']:
            try:
                return subtable.lookup(*args, **kwargs)
            except KeyError:
                pass
        raise KeyError
 
 
class DelayedList(list):
    def __init__(self, reader, file, file_offset, item_offsets):
        super().__init__([None] * len(item_offsets))
        self._reader = reader
        self._file = file
        self._offsets = [file_offset + item_offset
                         for item_offset in item_offsets]
 
    def __getitem__(self, index):
        if super().__getitem__(index) is None:
            self[index] = self._reader(self._file, self._offsets[index])
        return super().__getitem__(index)
 
 
class LookupListTable(OpenTypeTable):
    entries = [('LookupCount', uint16)]
 
    def __init__(self, file, file_offset, types):
        super().__init__(file, file_offset)
        lookup_offsets = array(offset, self['LookupCount'])(file)
        lookup_reader = lambda file, file_offset: LookupTable(file, file_offset,
                                                              types)
        self['Lookup'] = DelayedList(lookup_reader, file, file_offset,
                                     lookup_offsets)
 
 
class LayoutTable(OpenTypeTable):
    entries = [('Version', fixed),
               ('ScriptList', indirect(ScriptListTable)),
               ('FeatureList', indirect(FeatureListTable))]
 
    def __init__(self, file, file_offset):
        super().__init__(file, file_offset)
        lookup_list_offset = offset(file)
        self['LookupList'] = LookupListTable(file,
                                             file_offset + lookup_list_offset,
                                             self.lookup_types)
 
 
class Device(OpenTypeTable):
    entries = [('StartSize', uint16),
               ('EndSize', uint16),
               ('DeltaFormat', uint16),
               ('DeltaValue', uint16)]