from .error import HumbleError


class Data:
	"""Manage parsed data"""

	def __init__(self):

		# Public

		self.head = None
		"""
		Type when found in source:
		  {string name : value}

		Possible items:
		  "flags"         : [(int bitIndex, int bitValue)]
		  "lowestRecPPEM" : int

		The "flags" item is always included, but its list
		of bit values is empty when no flags are found.
		A bit value is either 0 or 1.
		"""

		self.maxp = None
		"""
		Type when found in source:
		  {string name : int value}

		Possible names:
		  maxStackElements
		  maxFunctionDefs
		  maxStorage
		  maxZones
		  maxTwilightPoints
		"""

		self.gasp = None
		"""
		Type when found in source:
		  [(int size, bool doGridfit, bool doGray, bool symSmoothing, bool symGridfit)]
		"""

		self.cvt = None
		"""
		Type when found in source:
		  [int]
		"""

		self.fpgm = None
		"""
		Type when found in source:
		  Program
		"""

		self.prep = None
		"""
		Type when found in source:
		  Program
		"""

		self.glyphs = {}
		"""
		Type:
		  {string name : Program program}
		"""

		# Private

		self.__flagLookup = {}
		self.__cvtLookup = {}
		self.__storageLookup = {}
		self.__functionLookup = {}
		self.__functionRecipeLookup = {}
		self.__voidFunctionIndices = []


	def _initHead(self):
		if self.head is not None:
			raise HumbleError("Duplicate head block")
		self.head = {}
		self.head["flags"] = []

	def _initMaxp(self):
		if self.maxp is not None:
			raise HumbleError("Duplicate maxp block")
		self.maxp = {}

	def _initGasp(self):
		if self.gasp is not None:
			raise HumbleError("Duplicate gasp block")
		self.gasp = []

	def _initCvt(self):
		if self.cvt is not None:
			raise HumbleError("Duplicate cvt block")
		self.cvt = []

	def _addFlagName(self, name, value):
		if name in self.__flagLookup:
			raise HumbleError("Duplicate flag name: {}".format(name))
		self.__flagLookup[name] = value

	def _addHeadItem(self, name, value):
		if name.startswith("flags."):
			if value != 0 and value != 1:
				raise HumbleError("Flag value must be single bit: {}".format(name))

			flags = self.head["flags"]
			if   name == "flags.instructionsMayDependOnPointSize": flags.append((2, value))
			elif name == "flags.forcePpemToIntegerValues":         flags.append((3, value))
			elif name == "flags.instructionsMayAlterAdvanceWidth": flags.append((4, value))
			elif name == "flags.fontOptimizedForClearType":        flags.append((13, value))
			else:
				raise HumbleError("Unknown head flag: {}".format(name))
		else:
			if name in self.head:
				raise HumbleError("Duplicate head record: {}".format(name))
			if name != "lowestRecPPEM":
				raise HumbleError("Unknown head record: {}".format(name))
			self.head[name] = value

	def _addMaxpItem(self, name, value):
		if name in self.maxp:
			raise HumbleError("Duplicate maxp record: {}".format(name))
		if name not in (
			"maxStackElements",
			"maxFunctionDefs",
			"maxStorage",
			"maxZones",
			"maxTwilightPoints"):
			raise HumbleError("Unknown maxp record: {}".format(name))
		self.maxp[name] = value

	def _addGaspItem(self, size, doGridFit, doGray, symSmoothing, symGridfit):
		if self.gasp:
			prevSize = self.gasp[-1][0]
			if size <= prevSize:
				raise HumbleError("The gasp records must be sorted in increasing order")
		self.gasp.append((size, doGridFit, doGray, symSmoothing, symGridfit))

	def _addCvtItem(self, name, value):
		if name in self.__cvtLookup:
			raise HumbleError("Duplicate cvt identifier: {}".format(name))
		index = len(self.cvt)
		self.cvt.append(value)
		self.__cvtLookup[name] = index

	def _addStorageName(self, name, index):
		if name in self.__storageLookup:
			raise HumbleError("Duplicate storage identifier: {}".format(name))
		self.__storageLookup[name] = index

	def _addFunction(self, index, name, recipe, isVoid):
		if name in self.__functionLookup:
			raise HumbleError("Duplicate function identifier: {}".format(name))
		if index in self.__functionLookup.values():
			raise HumbleError("Duplicate function index: {} {}".format(index, name))
		self.__functionLookup[name] = index
		self.__functionRecipeLookup[index] = recipe
		if isVoid:
			self.__voidFunctionIndices.append(index)

	def _addFpgm(self, program):
		if self.fpgm is not None:
			raise HumbleError("Duplicate fpgm block")
		self.fpgm = program

	def _addPrep(self, program):
		if self.prep is not None:
			raise HumbleError("Duplicate prep block")
		self.prep = program

	def _addGlyph(self, name, program):
		if name in self.glyphs:
			raise HumbleError("Duplicate block for glyph: {}".format(name))
		self.glyphs[name] = program

	def _getFlagValue(self, name):
		if name in self.__flagLookup:
			return self.__flagLookup[name]
		raise HumbleError("Unknown flag name: {}".format(name))

	def _getCvtIndex(self, name):
		try:
			return int(name)
		except ValueError:
			if name in self.__cvtLookup:
				return self.__cvtLookup[name]
			raise HumbleError("Unknown cvt identifier: {}".format(name))

	def _getStorageIndex(self, name):
		try:
			return int(name)
		except ValueError:
			if name in self.__storageLookup:
				return self.__storageLookup[name]
			raise HumbleError("Unknown storage identifier: {}".format(name))

	def _getFunctionIndex(self, name):
		try:
			return int(name)
		except ValueError:
			if name in self.__functionLookup:
				return self.__functionLookup[name]
			raise HumbleError("Unknown function identifier: {}".format(name))

	def _getFunctionRecipe(self, index):
		if index in self.__functionRecipeLookup:
			return self.__functionRecipeLookup[index]
		return ()

	def _isVoidFunction(self, index):
		return index in self.__voidFunctionIndices
