"""This code defines phone/segment/phoneme objects.
"""

import math
import random
import fpconst as FP

import HTK_write as HW

class alt(object):
	"""This represents several possible alternative pronunciations."""
	__slots__ = ['lbl']

	def __init__(self, *s):
		assert not None in s
		self.lbl = s
	
	def write(self, parens=False):
		tmp = []
		for x in self.lbl:
			if isinstance(x, str):
				x = HW.HTKformat(x)
			else:
				x = x.write(parens=True)
			tmp.append(x)
		q = ' | '.join(tmp)
		if parens and len(tmp) > 1:
			q = '( %s )' % q
		return q

	def labels(self):
		tmp = ()
		for x in self.lbl:
			if isinstance(x, str):
				tmp += (x,)
			else:
				tmp += x.labels()
		return tmp

	def likely(self):
		tmp = []
		for x in self.lbl:
			if isinstance(x, str):
				tmp.append((x,))
			else:
				tmp.append(x.likely())
		return random.choice(tmp)

	def map(self, mapping):
		tmp = []
		for x in self.lbl:
			if isinstance(x, str):
				tmp.append(mapping(x))
			else:
				tmp.append(x.map(mapping))
		return alt(*tmp)

	def __repr__(self):
		return "<alt %s>" % [str(q) for q in self.lbl]



class seq(object):
	"""This represents a sequence of phones."""
	__slots__ = ['lbl']

	def __init__(self, *s):
		assert not None in s
		self.lbl = s
	
	def write(self, parens=False):
		tmp = []
		for x in self.lbl:
			if isinstance(x, str):
				x = HW.HTKformat(x)
			else:
				x = x.write(parens=True)
			tmp.append(x)
		x = ' '.join(tmp)
		if parens and len(tmp) > 1:
			x = '( %s )' % x
		return x

	def labels(self):
		tmp = ()
		for x in self.lbl:
			if isinstance(x, str):
				tmp += (x,)
			else:
				tmp += x.labels()
		return tmp

	def likely(self):
		tmp = ()
		for x in self.lbl:
			if isinstance(x, str):
				tmp += (x,)
			else:
				tmp += x.likely()
		return tmp

	def map(self, mapping):
		tmp = []
		for x in self.lbl:
			if isinstance(x, str):
				tmp.append(mapping(x))
			else:
				tmp.append(x.map(mapping))
		return seq(*tmp)

	def __repr__(self):
		return "<seq %s>" % [str(q) for q in self.lbl]



class rep0(object):
	"""This represents zero or more phones."""
	__slots__ = ['lbl']

	def __init__(self, s):
		assert s is not None
		self.lbl = s

	def write(self, parens=False):
		if isinstance(self.lbl, str):
			x = HW.HTKformat(self.lbl)
		else:
			x = self.lbl.write()
		return '{ %s }' % x

	def labels(self):
		if isinstance(self.lbl, str):
			return (self.lbl,)
		return self.lbl.labels()

	def likely(self):
		n = int(math.floor(random.expovariate(1.5)))
		if isinstance(self.lbl, str):
			return n*(self.lbl,)
		return n*self.lbl.likely()
		
	def map(self, mapping):
		if isinstance(self.lbl, str):
			return rep0( mapping(self.lbl) )
		return rep0(self.lbl.map(mapping))


	def __repr__(self):
		return "<rep0 %s>" % self.lbl


class rep1(object):
	"""This represents one or more phones."""
	__slots__ = ['lbl']

	def __init__(self, s):
		assert s is not None
		self.lbl = s

	def write(self, parens=False):
		if isinstance(self.lbl, str):
			x = HW.HTKformat(self.lbl)
		else:
			x = self.lbl.write()
		return '< %s >' % x

	def labels(self):
		if isinstance(self.lbl, str):
			return (self.lbl,)
		return self.lbl.labels()

	def likely(self):
		n = int(math.ceil(random.expovariate(1.5)))
		if isinstance(self.lbl, str):
			return n*(self.lbl,)
		return n*(self.lbl.likely(),)
		
	def map(self, mapping):
		if isinstance(self.lbl, str):
			return rep1( mapping(self.lbl) )
		return rep1(self.lbl.map(mapping))

	def __repr__(self):
		return "<rep1 %s>" % self.lbl


class opt(object):
	__slots__ = ['lbl', 'p']

	def __init__(self, s, p=0.9):
		assert s is not None
		self.lbl = s
		assert 0 <= p <= 1.0
		self.p = p

	def write(self, parens=False):
		if isinstance(self.lbl, str):
			x = HW.HTKformat(self.lbl)
		else:
			x = self.lbl.write()
		if self.p > 0.999:
			return x
		return '[ %s ]' % x

	def labels(self):
		if isinstance(self.lbl, str):
			return (self.lbl,)
		return self.lbl.labels()

	def likely(self):
		if self.p < random.random():
			return ()
		if isinstance(self.lbl, str):
			return (self.lbl,)
		return self.lbl.likely()
		
	def map(self, mapping):
		if isinstance(self.lbl, str):
			return opt( mapping(self.lbl), p=self.p )
		return opt(self.lbl.map(mapping), p=self.p)


	def __repr__(self):
		return "<opt%.2f %s>" % (self.p, self.lbl)




class phone_c(object):
	__slots__ = ['lbl', 's', 'e', 'dur']

	def __init__(self, lbl, s=FP.NaN, e=FP.NaN, dur=None):
		assert isinstance(lbl, (str, rep0, rep1, alt, opt, seq)), "Weird type: %s" % repr(lbl)
		self.lbl = lbl
		self.s = s
		self.e = e
		self.dur = dur


	def grammar_fmt(self):
		if isinstance(self.lbl, str):
			return  HW.HTKformat(self.lbl)
		else:
			return self.lbl.write(parens=True)


	def startswith(self, s):
		assert isinstance(self.lbl, str)
		return self.lbl.startswith(s)

	QUANTUM = 1e-7

	def writeMLF(self, fd, ncol=1):
		if ncol==3:
			if FP.isNaN(self.s) or FP.isNaN(self.e):
				raise ValueError, "Ncol=3 demanded but NaN: %s %s %s" % (self.s, self.e, self.lbl)
			fd.write("%d %d %s\n" % (int(round(self.s/self.QUANTUM)),
						int(round(self.e/self.QUANTUM)), HW.HTKformat(self.label())))
		else:
			fd.write("%s\n" % HW.HTKformat(self.label()))


	def __repr__(self):
		if FP.isNaN(self.s) and FP.isNaN(self.e):
			s = "<Ph %s>" % str(self.lbl)
		else:
			s = "<Ph %s %.4f to %.4f>" % (self.lbl, self.s, self.e)
		return s


	def label(self):
		assert isinstance(self.lbl, str)
		return self.lbl


	def labels(self):
		if isinstance(self.lbl, str):
			return (self.lbl,)
		return self.lbl.labels()


	def likely(self):
		if isinstance(self.lbl, str):
			return (self.lbl,)
		return self.lbl.likely()


	def is_some_sil(self):
		l = self.likely()
		return 'sil' in l or 'shortsil' in l


	def is_all_sil(self):
		for ph in self.likely():
			if not (ph=='sil' or ph=='shortsil'):
				return False
		return True


def silence(dur=None, s=None, e=None):
	if s is not None and e is not None:
		dur = e-s
	assert dur > 0.0
	if dur < 0.13:
		ph = 'shortsil'
	else:
		ph = 'sil'
	if s is not None:
		e = s + dur
	elif e is not None:
		s = e-dur
	return phone_c(ph, s=s, e=e)
