/*
 * Galatea Dialog Manager:
 * (c)2004 Takuya NISHIMOTO (nishi@hil.t.u-tokyo.ac.jp)
 *
 * $Id: RecogInterpreter.java,v 1.7 2008/02/14 02:00:07 nishi Exp $
 */
package galatea.recogman;

import galatea.dialog.RuntimeError;
import galatea.logger.Logger;
import galatea.relaxer.event.EventEv;
import galatea.relaxer.event.EventGRAMINFO;
import galatea.relaxer.event.EventINPUT;
import galatea.relaxer.event.EventINPUTPARAM;
import galatea.relaxer.event.EventPHYPO;
import galatea.relaxer.event.EventRECOGFAIL;
import galatea.relaxer.event.EventRECOGOUT;
import galatea.relaxer.event.EventRECOGOUTRef;
import galatea.relaxer.event.EventSHYPO;
import galatea.relaxer.event.EventWHYPO;
import galatea.relaxer.event.IEventEvChoice;
import galatea.relaxer.event.IEventRECOGOUTChoice;
import galatea.scripting.ECMAScript;
import galatea.util.HashArray;
import galatea.util.Util;

import java.io.StringReader;
import java.nio.charset.Charset;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RecogInterpreter
{
	//private final String ENCODING = "EUC-JP";
	private Logger dbg = new Logger("RecogInterpreter", 0);
	private String script_ = new String();
	private String text_ = new String();
	
	private boolean accepted_ = false;
	private String dialogName_ = "";
	private HashArray slotAliases_ = new HashArray();
	
	private RecogInterpreterListener recogListener_;
	
	private ECMAScript ecmascript_ = null;
	
	// INPUTPARAM
	private int frames_ = 0;
	private int msec_ = 0;
	
	// label:wd
	private Pattern pattern1_ = Pattern.compile("([^:]*):(.*)");
	// wd@slot=val
	private Pattern pattern2_ = Pattern.compile("(.*)@(.*)=(.*)");
	// wd@slot (assume wd=val)
	private Pattern pattern3_ = Pattern.compile("(.*)@(.*)");
	// arg1 arg2
	private Pattern pattern4_ = Pattern.compile("(\\S+) (.*)");
	
	public RecogInterpreter() 
	{
		try {
			ecmascript_ = new ECMAScript();
		} catch (RuntimeError e) {
			dbg.print(e.toString());
		}
	}
	
	
	public void setListener(RecogInterpreterListener listener)
	{
		recogListener_ = listener;
	}
	
	
	public void reset()
	{
		_resetShypo();
		frames_ = 0;
		msec_ = 0;
		
		if (_eventHandler.length() > 0) {
			String sc = _eventHandler + "('reset')";
			dbg.print(sc);
			try {
				ecmascript_.evaluate(sc);
			} catch (RuntimeError e) {
				dbg.print(e.toString());
			}
		}
	}
	
	
	private void _resetShypo()
	{
		text_ = "";
		script_ = "";
	}
	
	
	public void setDialogName(String dialogName)
	{
		dialogName_ = dialogName;
	}
	
	
	public void resetSlotAlias()
	{
		slotAliases_ = new HashArray();
	}
	
	
	public void setSlotAlias(String arg)
	{
		Matcher m;
		if ((m = pattern4_.matcher(arg)).matches()) {
			slotAliases_.put(m.group(1), m.group(2));
		}
	}
	
	
	// 
	// to @SIM set SlotAlias = boolean:yes place:石川
	// to @SIM set SlotAlias = boolean confirm
	//
	private String _makeAssignCommand(String dialog, String slot, String value)
	{
		String d = dialog; 
		String s = slot; 
		String v = value;
		
		String key = slot + ":" + value;
		if (slotAliases_.has(key)) {
			String arg = (String)slotAliases_.get(key);
			Matcher m;
			if ((m = pattern1_.matcher(arg)).matches()) {
				s = m.group(1);
				v = m.group(2);
			}
		} else if (slotAliases_.has(slot)) {
			String arg = (String)slotAliases_.get(slot);
			s = arg;
		}
		
		String script = "";
		script += d + "." + s + "='" + v + "';";
		script += d + "." + s + "$.justfilled=true;";
		return script;
	}
	
	
	public String getText()
	{
		return text_;
	}
	
	
	public String getScript()
	{
		return script_;
	}
	
	
	/**
	 *  evaluate: onevent('assign','field1=さようなら')
	 */
	private void _parsePhypo(EventPHYPO phypo)
	{
		String text = "";
		for (int i = 0, n = phypo.sizeWHYPO(); i < n; i++) {
			EventWHYPO whypo = phypo.getWHYPO(i);
			String word = whypo.getWORD();
			_parseToScript(word);
			if (_eventHandler.length() > 0 && _parsedSlot.length() > 0) {
				String sc = _eventHandler
				+ "('assign','" + _parsedSlot + "=" + _parsedValue + "')";
				dbg.print(sc);
				try {
					ecmascript_.evaluate(sc);
				} catch (RuntimeError e) {
					dbg.print(e.toString());
				}
			}
			text += _parsedString;
			// dbg.print("text: "+text);
		}
		
		if ( _eventHandler.length() > 0 && text.length() > 0) {
			String sc = _eventHandler + "('phypo','" + text + "')";
			dbg.print("phypo " + phypo.getSCORE() + " " + phypo.getFRAME());
			dbg.print(sc);
			try {
				ecmascript_.evaluate(sc);
			} catch (RuntimeError e) {
				dbg.print(e.toString());
			}
		}
		
	}
	
	
	private void _parseShypo(EventSHYPO shypo)
	{
		_resetShypo();
		String text = "";
		String items = "";
		for (int i = 0, n = shypo.sizeWHYPO(); i < n; i++) {
			EventWHYPO whypo = shypo.getWHYPO(i);
			String word = whypo.getWORD();
			items += _parseToScript(word);
			text += _parsedString;
		}
		script_ = "$utterance='" + text + "';";
		script_ += items;
		text_ = text;
	}
	
	
	/*
	 * 
	 */ 
	private String _parsedString = "";
	private String _parsedSlot = "";
	private String _parsedValue = "";
	/*
	 * おはよう@field1=おはよう
	 */
	private String _parseToScript(String word)
	{
		_parsedString = "";
		_parsedSlot = "";
		_parsedValue = "";
		
		if (word == null) return "";
		
		String label="", slot="", val="", item="";
		String wd = "";
		Matcher m1, m2, m3;
		if ((m1 = pattern1_.matcher(word)).matches()) {
			// label:wd
			// dbg.print("RECOGOUT: WHYPO match1 "+word);
			label = m1.group(1);
			wd    = m1.group(2);
		} else if ((m2 = pattern2_.matcher(word)).matches()) {
			// wd@slot=val
			dbg.print("WHYPO match2 "+word);
			label = m2.group(1);
			wd    = m2.group(1);
			slot  = m2.group(2);
			val   = m2.group(3);
			item  = _makeAssignCommand(dialogName_, slot, val);
		} else if ((m3 = pattern3_.matcher(word)).matches()) {
			// wd@slot (assume wd=val)
			dbg.print("WHYPO match3 "+word);
			label = m3.group(1);
			wd    = m3.group(1);
			slot  = m3.group(2);
			val   = m3.group(1);
			item  = _makeAssignCommand(dialogName_, slot, val);
		} else {
			// dbg.print("nomatch "+word);
			label = word;
			wd    = word;
		}
		_parsedString = wd;
		_parsedSlot = slot;
		_parsedValue = val;
		return item;
	}
	
	
	
	public void receiveInputParam(EventINPUTPARAM ei)
	{
		frames_ = ei.getFRAMES();
		msec_ = ei.getMSEC();
	}
	
	
	public void receiveRecogout(EventRECOGOUT ro) 
	{
		IEventRECOGOUTChoice rc = ro.getContent();
		if (rc instanceof EventPHYPO) {
			_parsePhypo((EventPHYPO)rc);
			
		} else if (rc instanceof EventRECOGOUTRef) {
			EventRECOGOUTRef rr = (EventRECOGOUTRef)rc;
			for (int i = 0, n = rr.sizeSHYPO(); i < n; i++) {
				EventSHYPO shypo = rr.getSHYPO(i);
				if (shypo.getPASS() == 1) {
					recogListener_.recogPass1Finished();
					
				} else if (shypo.getRANK() == 1) {
					_parseShypo(shypo);
					recogListener_.recogPass2Finished();
				}
			}
		}
	}
	
	
	public void receiveRecogMessage(String body)
	{
		String evstr = "<ev src=\"SRM\" type=\"INPUT\">" + body + "</ev>";
		// System.err.println("[SIM] receiveRecogMessage " + evstr);
		StringReader reader = new StringReader(evstr);
		try {
			EventEv ev = new EventEv(reader);
			IEventEvChoice evc = ev.getContent();
			if (evc instanceof EventRECOGOUT) {
				receiveRecogout((EventRECOGOUT)evc);
			} else if (evc instanceof EventINPUT) {
				EventINPUT input = (EventINPUT)evc;
				String status = input.getSTATUS();
				if (status.equals("LISTEN")) {
					recogListener_.recogListenStarted();
					reset();
				} else if (status.equals("STARTREC")) {
					recogListener_.recogRecordStarted();
				} else if (status.equals("ENDREC")) {
					recogListener_.recogRecordFinished();
				}
			} else if (evc instanceof EventRECOGFAIL) {
				recogListener_.recogFailed();
			} else if (evc instanceof EventINPUTPARAM) {
				receiveInputParam((EventINPUTPARAM)ev.getContent());
			} else if (evc instanceof EventGRAMINFO) {
				//
			}
		} catch (Exception e) {
			recogListener_.recogException(e, body);
		}
	}
	
	//  <native>to @SIM set Script = interval.js</native>
	//  <native>to @SIM set EventHandler = onevent</native>
	public void setScript(String file)
	{
		try {
			Charset cs = Util.getSystemDefaultCharset();//Charset.forName(ENCODING);
			String sc = Util.loadFromFile(file, cs);
			ecmascript_.evaluate(sc);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	private String _eventHandler = "";
	
	public void setEventHandler(String func)
	{
		_eventHandler = func;
	}
	
}
