#!/usr/bin/env python

from __future__ import generators

import xwaves_lab
import die

import oxi_entity
import read_words
import types
import os.path


def _pcleanup(marklist):
	EPS = 0.001
	WINDOW = 0.020
	for i in range(len(marklist)):
		t, lab = marklist[i]
		assert type(lab) == type('')
		if lab == '%':
			j = i-1
			while j>=0 and marklist[j][0]>t-WINDOW:
				if marklist[j][1].startswith('*'):
					tc = 0.5*(marklist[j][0] + t)
					marklist[j] = (tc-EPS, '%')
					marklist[i] = (tc+EPS, '*')
				j -= 1
	print '#marklist:', marklist
	if len(marklist)>0 and not marklist[0][1].startswith('*'):
		assert marklist[0][0] > 0.0, 'First tag at negative time.'
		marklist.insert(0, (0.0, '*'))
	while len(marklist)>0 and marklist[-1][1] != '%':
		marklist.pop(-1)





class phrase(oxi_entity.entity):
	def __init__(self, utt_info, t0=None, tf=None, contents=[]):
		oxi_entity.entity.__init__(self, utt_info, t0, tf)
		self.contents = contents

	def str_contents(self):
		return ','.join( [ '%s@%.3f' % (t[1], t[0]) for t in self.contents ] )

	def info(self):
		d = oxi_entity.entity.info(self)
		d['accents'] = self.str_contents()
		return d



class word(oxi_entity.entity):
	def __init__(self, utt_info, t0=None, tf=None, word=None):
		oxi_entity.entity.__init__(self, utt_info, t0, tf)
		self.word = word

	def info(self):
		d = oxi_entity.entity.info(self)
		d['word'] = self.word
		return d



class iphrases(oxi_entity.entity_list):
	def __init__(self, uttrec, suffix, loose=0, loose2=0, root=None,
			Valid=None, use_bgp=1):
		"""Constructs an iphrase object which has self.phrases
		(a list of the intonational phrases in the file),
		and self.nm (the base name of the data file).
		Self.phrases is a list of phrase objects, each of which
		marks an intonational phrase, and each phrase contains
		the accents marked in the data file."""

		oxi_entity.entity_list.__init__(self)

		if use_bgp and not Valid.bgp(uttrec):
			raise Valid.InvalidUtterance, 'bgp'
		if not Valid.generic(uttrec, suffix, 0):
			raise Valid.InvalidUtterance, suffix

		if root is None:
			nm = os.path.join(uttrec['d'], uttrec['f'])
		else:
			nm = os.path.join(root, uttrec['d'], uttrec['f'])

		markh, markd = xwaves_lab.read('%s.%s' % (nm, suffix), loose)
		markd = read_words.empty_to_star(markd)
		if use_bgp:
			bgph, bgpd = xwaves_lab.read('%s.bgp' % nm, loose)
			d = bgpd + markd
			d.sort()
		else:
			d = markd
		_pcleanup(d)
		state = 'out'
		t0 = None
		contents = None
		for (t, lab) in d:
			die.note('t', t)
			die.note('label', lab)
			die.note('state', state)
			die.note('t0', t0)
			die.note('contents', contents)
			if state in ['out', 'punt'] and lab.startswith('*'):
				state = 'in'
				t0 = t
				contents = []
			elif state == 'in' and lab=='%':
				state = 'out'
				tf = t
				self.append( phrase(uttrec, t0, tf, contents) )
			elif state == 'in' and lab.startswith('*') and len(contents)==0:
				t0 = t
			elif state == 'out' and lab in ['%', '#']:
				pass
			elif state in ['in', 'punt'] and lab == '#':
				state = 'punt'
			elif state == 'punt' and lab=='%':
				state = 'out'
				tf = None
			elif state == 'out':
				die.warn("Unrecognized tag in 'out' state.")
				if not loose2:
					raise oxi_entity.SillyLabelSequence, 'Label %s out of a phrase at %s:%.3f' % (lab, nm, t)
			elif state == 'punt':
				state = 'in'
				t0 = t
				contents = []
			else:
				contents.append( (t-t0, lab) )
		if state == 'in':
			if not loose2:
				raise oxi_entity.SillyLabelSequence, 'File is missing a "%%" at end'
			else:
				die.warn('File does not end at the end of a phrase.')






class words(oxi_entity.entity_list):
	TOOLONG = 2.0	# No normal word is more than this long.
	def __init__(self, uttrec, loose=0, root=None, Valid=None):
		"""Constructs a words object which has
		(a list of the words in the file),
		and self.nm (the base name of the data file).
		Self.words is a list of word objects, each of which
		marks the boundaries of a word.
		"""
		if not Valid.bgp(uttrec):
			raise Valid.InvalidUtterance, 'bgp'
		if not Valid.wds(uttrec):
			raise Valid.InvalidUtterance, 'wds'

		oxi_entity.entity_list.__init__(self)

		if root is None:
			nm = os.path.join(uttrec['d'], uttrec['f'])
		else:
			nm = os.path.join(root, uttrec['d'], uttrec['f'])

		state = 'out'
		t0 = None
		contents = None
		for (t, lab) in read_words.read_words_with_beginnings(nm, loose):
			die.note('t', t)
			die.note('label', lab)
			die.note('state', state)
			die.note('t0', t0)
			die.note('contents', contents)
			assert type(t) == types.FloatType
			if state == 'out' or lab.startswith('*'):
				state = 'in'
				t0 = t
			elif lab.startswith('%'):
				state = 'out'
				t0 = None
			elif state == 'in':
				assert t0 is not None
				if t<t0+self.TOOLONG:
					self.append( word(uttrec, t0, t, lab) )
				else:
					die.warn('Too long word: %.3fs long.' % (t-t0))
				t0 = t
			else:
				raise RuntimeError, "Can't happen."
