from fontTools.misc import sstruct
from fontTools.misc.textTools import safeEval
from .sbixGlyph import Glyph
import struct

sbixStrikeHeaderFormat = """
	>
	ppem:          H	# The PPEM for which this strike was designed (e.g., 9,
						# 12, 24)
	resolution:    H	# The screen resolution (in dpi) for which this strike
						# was designed (e.g., 72)
"""

sbixGlyphDataOffsetFormat = """
	>
	glyphDataOffset:   L	# Offset from the beginning of the strike data record
							# to data for the individual glyph
"""

sbixStrikeHeaderFormatSize = sstruct.calcsize(sbixStrikeHeaderFormat)
sbixGlyphDataOffsetFormatSize = sstruct.calcsize(sbixGlyphDataOffsetFormat)


class Strike(object):
	def __init__(self, rawdata=None, ppem=0, resolution=72):
		self.data = rawdata
		self.ppem = ppem
		self.resolution = resolution
		self.glyphs = {}

	def decompile(self, ttFont):
		if self.data is None:
			from fontTools import ttLib
			raise ttLib.TTLibError
		if len(self.data) < sbixStrikeHeaderFormatSize:
			from fontTools import ttLib
			raise(ttLib.TTLibError, "Strike header too short: Expected %x, got %x.") \
				% (sbixStrikeHeaderFormatSize, len(self.data))

		# read Strike header from raw data
		sstruct.unpack(sbixStrikeHeaderFormat, self.data[:sbixStrikeHeaderFormatSize], self)

		# calculate number of glyphs
		firstGlyphDataOffset, = struct.unpack(">L", \
			self.data[sbixStrikeHeaderFormatSize:sbixStrikeHeaderFormatSize + sbixGlyphDataOffsetFormatSize])
		self.numGlyphs = (firstGlyphDataOffset - sbixStrikeHeaderFormatSize) // sbixGlyphDataOffsetFormatSize - 1
		# ^ -1 because there's one more offset than glyphs

		# build offset list for single glyph data offsets
		self.glyphDataOffsets = []
		for i in range(self.numGlyphs + 1): # + 1 because there's one more offset than glyphs
			start = i * sbixGlyphDataOffsetFormatSize + sbixStrikeHeaderFormatSize
			current_offset, = struct.unpack(">L", self.data[start:start + sbixGlyphDataOffsetFormatSize])
			self.glyphDataOffsets.append(current_offset)

		# iterate through offset list and slice raw data into glyph data records
		for i in range(self.numGlyphs):
			current_glyph = Glyph(rawdata=self.data[self.glyphDataOffsets[i]:self.glyphDataOffsets[i+1]], gid=i)
			current_glyph.decompile(ttFont)
			self.glyphs[current_glyph.glyphName] = current_glyph
		del self.glyphDataOffsets
		del self.numGlyphs
		del self.data

	def compile(self, ttFont):
		self.glyphDataOffsets = b""
		self.bitmapData = b""

		glyphOrder = ttFont.getGlyphOrder()

		# first glyph starts right after the header
		currentGlyphDataOffset = sbixStrikeHeaderFormatSize + sbixGlyphDataOffsetFormatSize * (len(glyphOrder) + 1)
		for glyphName in glyphOrder:
			if glyphName in self.glyphs:
				# we have glyph data for this glyph
				current_glyph = self.glyphs[glyphName]
			else:
				# must add empty glyph data record for this glyph
				current_glyph = Glyph(glyphName=glyphName)
			current_glyph.compile(ttFont)
			current_glyph.glyphDataOffset = currentGlyphDataOffset
			self.bitmapData += current_glyph.rawdata
			currentGlyphDataOffset += len(current_glyph.rawdata)
			self.glyphDataOffsets += sstruct.pack(sbixGlyphDataOffsetFormat, current_glyph)

		# add last "offset", really the end address of the last glyph data record
		dummy = Glyph()
		dummy.glyphDataOffset = currentGlyphDataOffset
		self.glyphDataOffsets += sstruct.pack(sbixGlyphDataOffsetFormat, dummy)

		# pack header
		self.data = sstruct.pack(sbixStrikeHeaderFormat, self)
		# add offsets and image data after header
		self.data += self.glyphDataOffsets + self.bitmapData

	def toXML(self, xmlWriter, ttFont):
		xmlWriter.begintag("strike")
		xmlWriter.newline()
		xmlWriter.simpletag("ppem", value=self.ppem)
		xmlWriter.newline()
		xmlWriter.simpletag("resolution", value=self.resolution)
		xmlWriter.newline()
		glyphOrder = ttFont.getGlyphOrder()
		for i in range(len(glyphOrder)):
			if glyphOrder[i] in self.glyphs:
				self.glyphs[glyphOrder[i]].toXML(xmlWriter, ttFont)
				# TODO: what if there are more glyph data records than (glyf table) glyphs?
		xmlWriter.endtag("strike")
		xmlWriter.newline()

	def fromXML(self, name, attrs, content, ttFont):
		if name in ["ppem", "resolution"]:
			setattr(self, name, safeEval(attrs["value"]))
		elif name == "glyph":
			if "graphicType" in attrs:
				myFormat = safeEval("'''" + attrs["graphicType"] + "'''")
			else:
				myFormat = None
			if "glyphname" in attrs:
				myGlyphName = safeEval("'''" + attrs["glyphname"] + "'''")
			elif "name" in attrs:
				myGlyphName = safeEval("'''" + attrs["name"] + "'''")
			else:
				from fontTools import ttLib
				raise ttLib.TTLibError("Glyph must have a glyph name.")
			if "originOffsetX" in attrs:
				myOffsetX = safeEval(attrs["originOffsetX"])
			else:
				myOffsetX = 0
			if "originOffsetY" in attrs:
				myOffsetY = safeEval(attrs["originOffsetY"])
			else:
				myOffsetY = 0
			current_glyph = Glyph(
				glyphName=myGlyphName,
				graphicType=myFormat,
				originOffsetX=myOffsetX,
				originOffsetY=myOffsetY,
			)
			for element in content:
				if isinstance(element, tuple):
					name, attrs, content = element
					current_glyph.fromXML(name, attrs, content, ttFont)
					current_glyph.compile(ttFont)
			self.glyphs[current_glyph.glyphName] = current_glyph
		else:
			from fontTools import ttLib
			raise ttLib.TTLibError("can't handle '%s' element" % name)
