from fontTools.misc import sstruct
from fontTools.misc.textTools import bytechr, byteord, strjoin
from . import DefaultTable
import array
from collections.abc import Mapping

hdmxHeaderFormat = """
	>   # big endian!
	version:	H
	numRecords:	H
	recordSize:	l
"""

class _GlyphnamedList(Mapping):

	def __init__(self, reverseGlyphOrder, data):
		self._array = data
		self._map = dict(reverseGlyphOrder)

	def __getitem__(self, k):
		return self._array[self._map[k]]

	def __len__(self):
		return len(self._map)

	def __iter__(self):
		return iter(self._map)

	def keys(self):
		return self._map.keys()

class table__h_d_m_x(DefaultTable.DefaultTable):

	def decompile(self, data, ttFont):
		numGlyphs = ttFont['maxp'].numGlyphs
		glyphOrder = ttFont.getGlyphOrder()
		dummy, data = sstruct.unpack2(hdmxHeaderFormat, data, self)
		self.hdmx = {}
		for i in range(self.numRecords):
			ppem = byteord(data[0])
			maxSize = byteord(data[1])
			widths = _GlyphnamedList(ttFont.getReverseGlyphMap(), array.array("B", data[2:2+numGlyphs]))
			self.hdmx[ppem] = widths
			data = data[self.recordSize:]
		assert len(data) == 0, "too much hdmx data"

	def compile(self, ttFont):
		self.version = 0
		numGlyphs = ttFont['maxp'].numGlyphs
		glyphOrder = ttFont.getGlyphOrder()
		self.recordSize = 4 * ((2 + numGlyphs + 3) // 4)
		pad = (self.recordSize - 2 - numGlyphs) * b"\0"
		self.numRecords = len(self.hdmx)
		data = sstruct.pack(hdmxHeaderFormat, self)
		items = sorted(self.hdmx.items())
		for ppem, widths in items:
			data = data + bytechr(ppem) + bytechr(max(widths.values()))
			for glyphID in range(len(glyphOrder)):
				width = widths[glyphOrder[glyphID]]
				data = data + bytechr(width)
			data = data + pad
		return data

	def toXML(self, writer, ttFont):
		writer.begintag("hdmxData")
		writer.newline()
		ppems = sorted(self.hdmx.keys())
		records = []
		format = ""
		for ppem in ppems:
			widths = self.hdmx[ppem]
			records.append(widths)
			format = format + "%4d"
		glyphNames = ttFont.getGlyphOrder()[:]
		glyphNames.sort()
		maxNameLen = max(map(len, glyphNames))
		format = "%" + repr(maxNameLen) + 's:' + format + ' ;'
		writer.write(format % (("ppem",) + tuple(ppems)))
		writer.newline()
		writer.newline()
		for glyphName in glyphNames:
			row = []
			for ppem in ppems:
				widths = self.hdmx[ppem]
				row.append(widths[glyphName])
			if ";" in glyphName:
				glyphName = "\\x3b".join(glyphName.split(";"))
			writer.write(format % ((glyphName,) + tuple(row)))
			writer.newline()
		writer.endtag("hdmxData")
		writer.newline()

	def fromXML(self, name, attrs, content, ttFont):
		if name != "hdmxData":
			return
		content = strjoin(content)
		lines = content.split(";")
		topRow = lines[0].split()
		assert topRow[0] == "ppem:", "illegal hdmx format"
		ppems = list(map(int, topRow[1:]))
		self.hdmx = hdmx = {}
		for ppem in ppems:
			hdmx[ppem] = {}
		lines = (line.split() for line in lines[1:])
		for line in lines:
			if not line:
				continue
			assert line[0][-1] == ":", "illegal hdmx format"
			glyphName = line[0][:-1]
			if "\\" in glyphName:
				from fontTools.misc.textTools import safeEval
				glyphName = safeEval('"""' + glyphName + '"""')
			line = list(map(int, line[1:]))
			assert len(line) == len(ppems), "illegal hdmx format"
			for i in range(len(ppems)):
				hdmx[ppems[i]][glyphName] = line[i]
