from __future__ import print_function, division, absolute_import
from fontTools import ttLib
from fontTools.ttLib.tables import otTables as ot

# VariationStore

def buildVarRegionAxis(axisSupport):
	self = ot.VarRegionAxis()
	self.StartCoord, self.PeakCoord, self.EndCoord = [float(v) for v in axisSupport]
	return self

def buildVarRegion(support, axisTags):
	assert all(tag in axisTags for tag in support.keys()), ("Unknown axis tag found.", support, axisTags)
	self = ot.VarRegion()
	self.VarRegionAxis = []
	for tag in axisTags:
		self.VarRegionAxis.append(buildVarRegionAxis(support.get(tag, (0,0,0))))
	self.VarRegionAxisCount = len(self.VarRegionAxis)
	return self

def buildVarRegionList(supports, axisTags):
	self = ot.VarRegionList()
	self.RegionAxisCount = len(axisTags)
	self.Region = []
	for support in supports:
		self.Region.append(buildVarRegion(support, axisTags))
	self.RegionCount = len(self.Region)
	return self


def _reorderItem(lst, narrows):
	out = []
	count = len(lst)
	for i in range(count):
		if i not in narrows:
			out.append(lst[i])
	for i in range(count):
		if i in narrows:
			out.append(lst[i])
	return out

def calculateNumShorts(self, optimize=True):
	count = self.VarRegionCount
	items = self.Item
	narrows = set(range(count))
	for item in items:
		wides = [i for i in narrows if not (-128 <= item[i] <= 127)]
		narrows.difference_update(wides)
		if not narrows:
			break
	if optimize:
		# Reorder columns such that all SHORT columns come before UINT8
		self.VarRegionIndex = _reorderItem(self.VarRegionIndex, narrows)
		for i in range(self.ItemCount):
			items[i] = _reorderItem(items[i], narrows)
		self.NumShorts = count - len(narrows)
	else:
		wides = set(range(count)) - narrows
		self.NumShorts = 1+max(wides) if wides else 0
	return self

def buildVarData(varRegionIndices, items, optimize=True):
	self = ot.VarData()
	self.VarRegionIndex = list(varRegionIndices)
	regionCount = self.VarRegionCount = len(self.VarRegionIndex)
	records = self.Item = []
	if items:
		for item in items:
			assert len(item) == regionCount
			records.append(list(item))
	self.ItemCount = len(self.Item)
	calculateNumShorts(self, optimize=optimize)
	return self


def buildVarStore(varRegionList, varDataList):
	self = ot.VarStore()
	self.Format = 1
	self.VarRegionList = varRegionList
	self.VarData = list(varDataList)
	self.VarDataCount = len(self.VarData)
	return self


def _getLocationKey(loc):
	return tuple(sorted(loc.items(), key=lambda kv: kv[0]))

class OnlineVarStoreBuilder(object):

	def __init__(self, axisTags):
		self._axisTags = axisTags
		self._regionMap = {}
		self._regionList = buildVarRegionList([], axisTags)
		self._store = buildVarStore(self._regionList, [])

	def setModel(self, model):
		self._model = model

		regionMap = self._regionMap
		regionList = self._regionList

		regions = model.supports[1:]
		regionIndices = []
		for region in regions:
			key = _getLocationKey(region)
			idx = regionMap.get(key)
			if idx is None:
				varRegion = buildVarRegion(region, self._axisTags)
				idx = regionMap[key] = len(regionList.Region)
				regionList.Region.append(varRegion)
			regionIndices.append(idx)

		data = self._data = buildVarData(regionIndices, [], optimize=False)
		self._outer = len(self._store.VarData)
		self._store.VarData.append(data)

	def finish(self, optimize=True):
		self._regionList.RegionCount = len(self._regionList.Region)
		self._store.VarDataCount = len(self._store.VarData)
		for data in self._store.VarData:
			data.ItemCount = len(data.Item)
			calculateNumShorts(data, optimize)
		return self._store

	def storeMasters(self, master_values):
		deltas = [int(round(d)) for d in self._model.getDeltas(master_values)]
		base = deltas.pop(0)
		inner = len(self._data.Item)
		self._data.Item.append(deltas)
		# TODO Check for full data array?
		return base, (self._outer << 16) + inner



# Variation helpers

def buildVarIdxMap(varIdxes):
	# TODO Change VarIdxMap mapping to hold separate outer,inner indices
	self = ot.VarIdxMap()
	self.mapping = list(varIdxes)
	return self

def buildVarDevTable(varIdx):
	self = ot.Device()
	self.DeltaFormat = 0x8000
	self.StartSize = varIdx >> 16
	self.EndSize = varIdx & 0xFFFF
	return self
