from fontTools.misc import sstruct
from fontTools.misc.textTools import byteord, safeEval
from . import DefaultTable
import pdb
import struct


METAHeaderFormat = """
		>	# big endian
		tableVersionMajor:			H
		tableVersionMinor:			H
		metaEntriesVersionMajor:	H
		metaEntriesVersionMinor:	H
		unicodeVersion:				L
		metaFlags:					H
		nMetaRecs:					H
"""
# This record is followed by nMetaRecs of METAGlyphRecordFormat.
# This in turn is followd by as many METAStringRecordFormat entries
# as specified by the METAGlyphRecordFormat entries
# this is followed by the strings specifried in the  METAStringRecordFormat
METAGlyphRecordFormat = """
		>	# big endian
		glyphID:			H
		nMetaEntry:			H
"""
# This record is followd by a variable data length field:
# 	USHORT or ULONG	hdrOffset
# Offset from start of META table to the beginning
# of this glyphs array of ns Metadata string entries.
# Size determined by metaFlags field
# METAGlyphRecordFormat entries must be sorted by glyph ID

METAStringRecordFormat = """
		>	# big endian
		labelID:			H
		stringLen:			H
"""
# This record is followd by a variable data length field:
# 	USHORT or ULONG	stringOffset
# METAStringRecordFormat entries must be sorted in order of labelID
# There may be more than one entry with the same labelID
# There may be more than one strign with the same content.

# Strings shall be Unicode UTF-8 encoded, and null-terminated.

METALabelDict = {
    0: "MojikumiX4051",  # An integer in the range 1-20
    1: "UNIUnifiedBaseChars",
    2: "BaseFontName",
    3: "Language",
    4: "CreationDate",
    5: "FoundryName",
    6: "FoundryCopyright",
    7: "OwnerURI",
    8: "WritingScript",
    10: "StrokeCount",
    11: "IndexingRadical",
}


def getLabelString(labelID):
    try:
        label = METALabelDict[labelID]
    except KeyError:
        label = "Unknown label"
    return str(label)


class table_M_E_T_A_(DefaultTable.DefaultTable):
    dependencies = []

    def decompile(self, data, ttFont):
        dummy, newData = sstruct.unpack2(METAHeaderFormat, data, self)
        self.glyphRecords = []
        for i in range(self.nMetaRecs):
            glyphRecord, newData = sstruct.unpack2(
                METAGlyphRecordFormat, newData, GlyphRecord()
            )
            if self.metaFlags == 0:
                [glyphRecord.offset] = struct.unpack(">H", newData[:2])
                newData = newData[2:]
            elif self.metaFlags == 1:
                [glyphRecord.offset] = struct.unpack(">H", newData[:4])
                newData = newData[4:]
            else:
                assert 0, (
                    "The metaFlags field in the META table header has a value other than 0 or 1 :"
                    + str(self.metaFlags)
                )
            glyphRecord.stringRecs = []
            newData = data[glyphRecord.offset :]
            for j in range(glyphRecord.nMetaEntry):
                stringRec, newData = sstruct.unpack2(
                    METAStringRecordFormat, newData, StringRecord()
                )
                if self.metaFlags == 0:
                    [stringRec.offset] = struct.unpack(">H", newData[:2])
                    newData = newData[2:]
                else:
                    [stringRec.offset] = struct.unpack(">H", newData[:4])
                    newData = newData[4:]
                stringRec.string = data[
                    stringRec.offset : stringRec.offset + stringRec.stringLen
                ]
                glyphRecord.stringRecs.append(stringRec)
            self.glyphRecords.append(glyphRecord)

    def compile(self, ttFont):
        offsetOK = 0
        self.nMetaRecs = len(self.glyphRecords)
        count = 0
        while offsetOK != 1:
            count = count + 1
            if count > 4:
                pdb.set_trace()
            metaData = sstruct.pack(METAHeaderFormat, self)
            stringRecsOffset = len(metaData) + self.nMetaRecs * (
                6 + 2 * (self.metaFlags & 1)
            )
            stringRecSize = 6 + 2 * (self.metaFlags & 1)
            for glyphRec in self.glyphRecords:
                glyphRec.offset = stringRecsOffset
                if (glyphRec.offset > 65535) and ((self.metaFlags & 1) == 0):
                    self.metaFlags = self.metaFlags + 1
                    offsetOK = -1
                    break
                metaData = metaData + glyphRec.compile(self)
                stringRecsOffset = stringRecsOffset + (
                    glyphRec.nMetaEntry * stringRecSize
                )
                # this will be the String Record offset for the next GlyphRecord.
            if offsetOK == -1:
                offsetOK = 0
                continue

            # metaData now contains the header and all of the GlyphRecords. Its length should bw
            # the offset to the first StringRecord.
            stringOffset = stringRecsOffset
            for glyphRec in self.glyphRecords:
                assert glyphRec.offset == len(
                    metaData
                ), "Glyph record offset did not compile correctly! for rec:" + str(
                    glyphRec
                )
                for stringRec in glyphRec.stringRecs:
                    stringRec.offset = stringOffset
                    if (stringRec.offset > 65535) and ((self.metaFlags & 1) == 0):
                        self.metaFlags = self.metaFlags + 1
                        offsetOK = -1
                        break
                    metaData = metaData + stringRec.compile(self)
                    stringOffset = stringOffset + stringRec.stringLen
            if offsetOK == -1:
                offsetOK = 0
                continue

            if ((self.metaFlags & 1) == 1) and (stringOffset < 65536):
                self.metaFlags = self.metaFlags - 1
                continue
            else:
                offsetOK = 1

            # metaData now contains the header and all of the GlyphRecords and all of the String Records.
            # Its length should be the offset to the first string datum.
            for glyphRec in self.glyphRecords:
                for stringRec in glyphRec.stringRecs:
                    assert stringRec.offset == len(
                        metaData
                    ), "String offset did not compile correctly! for string:" + str(
                        stringRec.string
                    )
                    metaData = metaData + stringRec.string

        return metaData

    def toXML(self, writer, ttFont):
        writer.comment(
            "Lengths and number of entries in this table will be recalculated by the compiler"
        )
        writer.newline()
        formatstring, names, fixes = sstruct.getformat(METAHeaderFormat)
        for name in names:
            value = getattr(self, name)
            writer.simpletag(name, value=value)
            writer.newline()
        for glyphRec in self.glyphRecords:
            glyphRec.toXML(writer, ttFont)

    def fromXML(self, name, attrs, content, ttFont):
        if name == "GlyphRecord":
            if not hasattr(self, "glyphRecords"):
                self.glyphRecords = []
            glyphRec = GlyphRecord()
            self.glyphRecords.append(glyphRec)
            for element in content:
                if isinstance(element, str):
                    continue
                name, attrs, content = element
                glyphRec.fromXML(name, attrs, content, ttFont)
            glyphRec.offset = -1
            glyphRec.nMetaEntry = len(glyphRec.stringRecs)
        else:
            setattr(self, name, safeEval(attrs["value"]))


class GlyphRecord(object):
    def __init__(self):
        self.glyphID = -1
        self.nMetaEntry = -1
        self.offset = -1
        self.stringRecs = []

    def toXML(self, writer, ttFont):
        writer.begintag("GlyphRecord")
        writer.newline()
        writer.simpletag("glyphID", value=self.glyphID)
        writer.newline()
        writer.simpletag("nMetaEntry", value=self.nMetaEntry)
        writer.newline()
        for stringRec in self.stringRecs:
            stringRec.toXML(writer, ttFont)
        writer.endtag("GlyphRecord")
        writer.newline()

    def fromXML(self, name, attrs, content, ttFont):
        if name == "StringRecord":
            stringRec = StringRecord()
            self.stringRecs.append(stringRec)
            for element in content:
                if isinstance(element, str):
                    continue
                stringRec.fromXML(name, attrs, content, ttFont)
            stringRec.stringLen = len(stringRec.string)
        else:
            setattr(self, name, safeEval(attrs["value"]))

    def compile(self, parentTable):
        data = sstruct.pack(METAGlyphRecordFormat, self)
        if parentTable.metaFlags == 0:
            datum = struct.pack(">H", self.offset)
        elif parentTable.metaFlags == 1:
            datum = struct.pack(">L", self.offset)
        data = data + datum
        return data

    def __repr__(self):
        return (
            "GlyphRecord[ glyphID: "
            + str(self.glyphID)
            + ", nMetaEntry: "
            + str(self.nMetaEntry)
            + ", offset: "
            + str(self.offset)
            + " ]"
        )


# XXX The following two functions are really broken around UTF-8 vs Unicode


def mapXMLToUTF8(string):
    uString = str()
    strLen = len(string)
    i = 0
    while i < strLen:
        prefixLen = 0
        if string[i : i + 3] == "&#x":
            prefixLen = 3
        elif string[i : i + 7] == "&amp;#x":
            prefixLen = 7
        if prefixLen:
            i = i + prefixLen
            j = i
            while string[i] != ";":
                i = i + 1
            valStr = string[j:i]

            uString = uString + chr(eval("0x" + valStr))
        else:
            uString = uString + chr(byteord(string[i]))
        i = i + 1

    return uString.encode("utf_8")


def mapUTF8toXML(string):
    uString = string.decode("utf_8")
    string = ""
    for uChar in uString:
        i = ord(uChar)
        if (i < 0x80) and (i > 0x1F):
            string = string + uChar
        else:
            string = string + "&#x" + hex(i)[2:] + ";"
    return string


class StringRecord(object):
    def toXML(self, writer, ttFont):
        writer.begintag("StringRecord")
        writer.newline()
        writer.simpletag("labelID", value=self.labelID)
        writer.comment(getLabelString(self.labelID))
        writer.newline()
        writer.newline()
        writer.simpletag("string", value=mapUTF8toXML(self.string))
        writer.newline()
        writer.endtag("StringRecord")
        writer.newline()

    def fromXML(self, name, attrs, content, ttFont):
        for element in content:
            if isinstance(element, str):
                continue
            name, attrs, content = element
            value = attrs["value"]
            if name == "string":
                self.string = mapXMLToUTF8(value)
            else:
                setattr(self, name, safeEval(value))

    def compile(self, parentTable):
        data = sstruct.pack(METAStringRecordFormat, self)
        if parentTable.metaFlags == 0:
            datum = struct.pack(">H", self.offset)
        elif parentTable.metaFlags == 1:
            datum = struct.pack(">L", self.offset)
        data = data + datum
        return data

    def __repr__(self):
        return (
            "StringRecord [ labelID: "
            + str(self.labelID)
            + " aka "
            + getLabelString(self.labelID)
            + ", offset: "
            + str(self.offset)
            + ", length: "
            + str(self.stringLen)
            + ", string: "
            + self.string
            + " ]"
        )
