/**
 * $Id: JulianSubModule.java,v 1.8 2008/02/14 02:00:07 nishi Exp $
 */
package galatea.io.plugins;

import galatea.dialog.InterpretedEvent;
import galatea.dialog.window.DialogManagerWindow;
import galatea.io.ISenderToDevice;
import galatea.logger.Logger;
import galatea.recogman.JulianServerRunner;
import galatea.recogman.JuliusClient;
import galatea.recogman.RecogInterpreter;
import galatea.recogman.RecogInterpreterListener;
import galatea.submodule.AbstractSubModule;
import galatea.util.Util;

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

public class JulianSubModule 
	extends AbstractSubModule
	implements RecogInterpreterListener {
	
	private Logger dbg = new Logger("SRM");
	private RecogInterpreter parser_ = new RecogInterpreter();
	
	private String  xmlContent_ = "";
	private boolean debugMode_ = true;
	
	private Pattern patSrmTell_ = Pattern.compile("^From @SRM tell (.*)$");
	private boolean insideMultiLine_ = false;

//	private boolean useDIM_ = false;
	
	private JulianServerRunner server_;
	private JuliusClient client_;
	
	public JulianSubModule() {
		parser_.setListener(this);
		server_ = new JulianServerRunner();
		Runtime.getRuntime().addShutdownHook(new Thread() {
		    public void run() { 
				dbg.print("Shutting down");
		    	server_.setRunning(false);
		    }
		});
	}
	
	public void setTargetRunLevel(int level) {
		if (level == 2) {
			server_.run();
			client_ = new JuliusClient("localhost", 10500);
			try {
				Thread.sleep(3000); // 1000
				client_.open();
				dbg.print("JulianSubModule setup done");
			} catch (IOException e) {
				e.printStackTrace();
				return;
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			receiver_.receiveRunLevel(2, this);
		}
	}

	
	public void doTimerTask() {
		// server
		if (!server_.isRunning())
			return;
		while (true) {
			String er = server_.getErrors();
			if (er.length() == 0) 
				break;
			dbg.print("SE: " + er);
			server_.clearErrors();
		}
		while (true) {
			String str = server_.getOutputs();
			if (str.length() == 0) 
				break;
			dbg.print("SO: " + Util.removeNewLines(str));
			server_.clearOutputs();
		}
		// client
		try {
			client_.receive();
		} catch (Exception e) {
			dbg.print("CX: " + e.toString());
			server_.setRunning(false);
			return;
		}
		String msg;
		while ((msg = client_.getMessage()).length() > 0) {
			dbg.print("CO: " + msg);
			_fromSRMtell(msg);
		}
	}
	
	private void _debugPrint(String str) {
		if (debugMode_) {
			dbg.print(str);
		}
	}
	
	public void setDialogName(String s) {
		parser_.setDialogName(s);
	}
	
	public void setSlotAlias(String s) {
		if (s.equals("RESET")) {
			parser_.resetSlotAlias();
		} else {
			parser_.setSlotAlias(s);
		}
	}
	
	private void _fromSRMtell(String s) {
		//_debugPrint("[" + xmlContent_ + "] [" + s + "]");
		
		if (s.startsWith("grammar ")) {
			logger_.inputEventReceived(s);
			return;
		}
		if (s.startsWith("ModuleStart")) {
			logger_.inputEventReceived(s);
			return;
		}
		if (s.startsWith("ModuleExit")) {
			logger_.inputEventReceived(s);
			return;
		}
		if (s.startsWith("<STARTPROC/>")) {
			logger_.inputEventReceived(s);
			receiver_.receiveRunLevel(2, this);
			return;
		}
		if (s.startsWith("<GMM ")) {
			logger_.inputEventReceived(s);
			return;
		}
		if (s.startsWith("<INPUT ")) {
			logger_.inputEventReceived(s);
			return;
		}
		if (s.startsWith("<INPUTPARAM ")) {
			logger_.inputEventReceived(s);
			return;
		}
		if (s.startsWith("<REJECTED ")) {
			logger_.inputEventReceived(s);
			return;
		}
		if (s.startsWith("<GRAMMAR ")) {
			logger_.inputEventReceived(s);
			return;
		}
		
		/* 
		 * <INPUT STATUS="LISTEN" TIME="1164166313"/>
		 * <INPUT STATUS="STARTREC" TIME="1164166322"/>
		 * <INPUT STATUS="ENDREC" TIME="1164166323"/>
		 * <INPUTPARAM FRAMES="146" MSEC="1460"/>
		 * <RECOGOUT>  
		 * <SHYPO RANK="1" SCORE="-3783.279297" GRAM="2">    
		 * <WHYPO WORD="silB:" CLASSID="0" PHONE="silB" CM="1.000"/>    
		 * <WHYPO WORD="新大阪@source=新大阪" CLASSID="7" PHONE="sh i N o: s a k a" CM="0.999"/>    
		 * <WHYPO WORD="silE:" CLASSID="1" PHONE="silE" CM="1.000"/>  
		 * </SHYPO>
		 * </RECOGOUT>
		 */
		
		if (xmlContent_.length() > 0 || s.startsWith("<")) {
			xmlContent_ += s;
			// validation ?? 
			if (/* xmlContent_.endsWith("/>") || */ 
					xmlContent_.endsWith("</RECOGOUT>") 
					|| xmlContent_.endsWith("</GRAMINFO>") 
			) {
				try {
					parser_.receiveRecogMessage(xmlContent_);
				} catch (Exception e) {
					_debugPrint(e.toString());
				}
				xmlContent_ = "";
			}
		}
	}
	
	public void recogListenStarted() {
//		_sendUserSpeakState(0);
	}
	
	public void recogRecordStarted() {
//		_sendUserSpeakState(1);
	}
	
	public void recogRecordFinished() {
//		_sendUserSpeakState(0);
	}
	
	public void recogPass1Finished() {
	}
	
	public void recogPass2Finished() {
		String script = parser_.getScript();
		String text = parser_.getText();
		String xmlevent = "<ev src=\"SRM\" type=\"INPUT\"><interpreted>"
		+"<text>" + text + "</text>"
		+"<script>" + script + "</script></interpreted></ev>";
		InterpretedEvent ev = InterpretedEvent.newInstance(xmlevent);
		receiver_.receiveInterpreted(ev);
		logger_.inputEventReceived(xmlevent);
	}
	
	public void recogFailed() {
	}
	
	public void recogException(Exception e, String str) {
		_debugPrint("recogException(" + str +") " + e.toString());
	}
	
	public boolean filterOutputNative(String str) {
		String s;
		if ((s = Util.getFirstGroup("to @SIM set DialogName = (.*)$", str)) != null) {
			setDialogName(s);
			return true;
		} else if ((s = Util.getFirstGroup("to @SIM set SlotAlias = (.*)$", str)) != null) {
			setSlotAlias(s);
			return true;
		} else if ((s = Util.getFirstGroup("to @SRM set Grammar = (.*)$", str)) != null) {
			try {
				sendGrammar(s);
			} catch (Exception e) {
				// TODO: handle severe error
				logger_.grammarFileUpdated(e.toString());
				return true;
			}
			logger_.grammarFileUpdated(getGrammarForDisplay(s));
			return true;
		} else if (str.startsWith("to @SRM set Dic =")) {
			return true;
		}
		return false;
	}
	
	public void sendGrammar(String s) throws Exception {
		String file = s.replaceAll(".dfa", "");
		Charset charset_ = Util.getSystemDefaultCharset();
//		try {
//			String dfa  = Util.loadFromFile(file + ".dfa",  charset_);
//			String dict = Util.loadFromFile(file + ".dict", charset_);
//			client_.send("CHANGEGRAM " + file + "\n");
//			client_.send(dfa);
//			client_.send("DFAEND\n");
//			client_.send(dict);
//			client_.send("DICEND\n");
//			// TODO: wait <GRAMMAR STATUS="RECEIVED"/>
//			client_.send("RESUME\n");
//		} catch (Exception e) {
//			e.printStackTrace();
//		}
		String dfa  = Util.loadFromFile(file + ".dfa",  charset_);
		String dict = Util.loadFromFile(file + ".dict", charset_);
		client_.send("CHANGEGRAM " + file + "\n");
		client_.send(dfa);
		client_.send("DFAEND\n");
		client_.send(dict);
		client_.send("DICEND\n");
		// TODO: wait <GRAMMAR STATUS="RECEIVED"/>
		client_.send("RESUME\n");
	}
	
	public String getGrammarForDisplay(String s) {
		String file = s.replaceAll(".dfa", "");
		Charset charset_ = Util.getSystemDefaultCharset();
		String XML_ENCODING = "EUC-JP";
		Charset xmlCharset_ = Charset.forName(XML_ENCODING);
		try {
			String s1 = Util.loadFromFile(file + ".voca", charset_);
			String s2 = Util.loadFromFile(file + ".grammar", charset_);
			// String s3 = Util.loadFromFile(file + ".xml", xmlCharset_);
			return s 
					+ "\n\n[voca]\n" + s1 
					+ "\n\n[grammar]\n" + s2; 
					//+ "\n\n[xml]\n" + s3;
		} catch (Exception e) {
			return s + "\n" + file + "\n" + e.toString();
		}
	}

}
