# -*- coding: utf-8 -*-

import os
from gmisclib import die
from gmisclib import dictops

import process_phones as PP
import phones as PH
import generate_misc as GM
import get_phones as GP

MIN_PHONES_PER_UTTERANCE = 3


def prep_phones(pa, pmap, phonology, is_manual_transcription=False,
			insert_optional_phones=False, datasource='?'):
	"""This maps from the string of phonemes that the
	human or dictionary produces into a string of
	C, V, S and transition phonemes that the ASR systems wants.
	It inserts opportunities for liason where possible.
	@param is_manual_transcription: are pauses transcribed?  Otherwise, the
		system assumes that they may appear at word boundaries.
	@type is_manual_transcription: bool
	@param pa: The transcribed phoneme sequence
	@type pa: C{list(str)}
	@param pmap: A mapping from input phonemes to output phonemes.
	@type pmap: C{dict(str:str)}
	"""
	#: the output list of phonemes gets accumulated here.
	phones = [GP.phone_c('_sil_', s=0.0)]
	#: are we in the middle of a garbled section?
	garble = False
	#: counters are just used for trouble detection.
	counters = dictops.dict_of_accums()
	#: Have we just seen an end-of-word marker?
	iwp = False
	last = GP.phone_c('#')
	for p in pa:
		try:
			pm = pmap[p.lbl]
		except KeyError:
			pm = None
		assert not phones[-1].startswith('IPP')
		if p.lbl in ['#', '##', '_sil_']:
			counters.add('_sil_', 1)
			if phones[-1].lbl != '_sil_':
				phones.append(GP.phone_c('_sil_', s=p.s, e=p.e))
				last = GP.phone_c('#')
			iwp = False
		elif p.startswith('#w') or p.startswith('#p'):
			# Word or punctuation boundaries.
			garble = False
			if not phones[-1].lbl.startswith('IPP_'):
				iwp = True
		elif p.startswith('#['):
			# Beginning of garble/correction
			garble = True
		elif p.startswith('#]') or p.startswith(']#'):
			# end of garble/correction
			iwp = True
			garble = False
		elif p.startswith('#'):
			# Other non-phoneme markers.
			garble = False
		elif pm is not None and pm:
			# All normal phonemes.
			assert not phones[-1].lbl.startswith('IPP_')
			assert not (phones[-1].lbl=='_sil_' and pm=='_sil_')
			assert pm != '_sil_'
			if iwp:
				counters.add('#w', 1)
			if garble:
				pm = 'garble'
			if iwp:
				if isC(phonology, last.lbl) and isC(phonology, p.lbl):
					# Liason is not reliably marked even in the manual transcriptions.
					if is_manual_transcription:
						phones.append(GP.phone_c('IPP_wl', dur=STEP))
					else:
						phones.append(GP.phone_c('IPP_WL', dur=STEP))
				else:
					if  is_manual_transcription:
						phones.append(GP.phone_c('IPP_x', dur=STEP))
					else:
						phones.append(GP.phone_c('IPP_W', dur=STEP))
			else:
				# Not a word boundary.
				if is_manual_transcription:
					phones.append(GP.phone_c('IPP_x', dur=STEP))
				else:
					phones.append(GP.phone_c('IPP_X', dur=STEP))
			counters.add(phones[-1].lbl, 1)
			iwp = False
			phones.append(GP.phone_c(pm, s=p.s, e=p.e))
			last = p
			counters.add(pm, 1)
			counters.add('*', 1)
		elif pm is None:
			# Legal non-phonemes like tone markers.
			pass
		else:
			raise BadPhonesError("Unrecognized phone: %s" % p, datasource)
	if len(phones) < 3:
		raise BadPhonesError("Too few phones: %d"
					% counters.get('*',0),
					pa, datasource)
	if not is_manual_transcription and counters.get('#w', 0) < (counters.get('*',0)-7)//15:
		raise BadPhonesError("Too few word boundaries: %d in a string of %d phones"
					% (counters.get('#w',0), counters.get('*',0)),
					pa, datasource)
	if counters.get('garble',0) > (counters.get('*',0)+5)//10:
		raise BadPhonesError("Too many garbles: %d in a string of %d phones"
					% (counters.get('garble',0), counters.get('*',0)),
					pa, datasource)
	if phones[-1].lbl != '_sil_':
		if not phones[-1].lbl.startswith('IPP_'):
			if is_manual_transcription:
				phones.append(GP.phone_c('IPP_x', dur=STEP))
			else:
				phones.append(GP.phone_c('IPP_X', dur=STEP))
		phones.append(GP.phone_c('_sil_', dur=FINALDUR))

	if is_manual_transcription:
		fill_NaNs_in_phone_sequence(phones)
	return phones

def prepare_phones_aesop_main(info, pmap, phonology, use_manual_only, insert_optional_phones, ppm):
	d = os.path.join(info['dbdir'], info['d'], info['f'])
	candidates_files = GP.find_manual_segmentations(info)
	o = []
	if candidates_files:
		for c in candidates_files:
			phonelist, phonefile = GP.get_manual_transcription(info, c)
			die.info("Found human segmentation in %s for %d" % (phonefile, d))
			if len(phonelist) < MIN_PHONES_PER_UTTERANCE:
				die.warn("Too few phones (%d) in %s" % (len(phonelist), d))
				continue
			try:
				tmp = ppm.prep_phones(phonelist, pmap, phonology, is_manual_transcription=True,
					insert_optional_phones=insert_optional_phones,
					datasource=phonefile)
			except MS.BadPhonesError, x:
				die.warn("%s in %s" % (x, d))
				continue
			if len(tmp) >= MIN_PHONES_PER_UTTERANCE:
				o.append(tmp)
			else:
				die.warn("After preparation by %s, there were too few phones (%d): %s" % (ppm.__name__, len(tmp), d))
		priority = 1
	elif not use_manual_only:
		phonelist, priority, phonefile = GP.get_transcription(info)
		if len(phonelist) < MIN_PHONES_PER_UTTERANCE:
			die.warn("Too few phones (%d) in %s from %s" % (len(phonelist), phonefile, d))
		else:
			priority += 1
			try:
				tmp = ppm.prep_phones(phonelist, pmap, phonology, is_manual_transcription=False,
						insert_optional_phones=insert_optional_phones, datasource=phonefile)
			except MS.BadPhonesError, x:
				die.warn(str(x))
				die.warn("%s in %s from %s" % (x, phonefile, d))
			else:
				if len(tmp) >= MIN_PHONES_PER_UTTERANCE:
					o.append(tmp)
				else:
					die.warn("After preparation by %s, there were too few phones (%d): %s" % (ppm.__name__, len(tmp), d))
	else:
		priority = 10000

	return (o, priority)

