/*
 * Galatea Dialog Manager:
 * (c)2003 Takuya NISHIMOTO (nishi@hil.t.u-tokyo.ac.jp)
 *
 * $Id: GrammarTransformer.java,v 1.14 2008/04/09 10:26:06 nishi Exp $
 */
package galatea.grammar;

import galatea.dialog.DialogManager;
import galatea.logger.Logger;
import galatea.util.Property;
import galatea.util.SubProcess;
import galatea.util.Util;

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

import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

public class GrammarTransformer {
	private Logger dbg = new Logger("GrammarTransformer");
	private static final String xslRes = "/res/grammar/xml2julgrm.xsl";
	private Transformer transformer_;
	private String grammar_ = "";
	private String voca_ = "";
	private String errors_ = "";
	private String outputs_ = "";
//	private String exception_ = "";
	private SubProcess subProcess_;
	private String mkdfaPerl_ = "perl";
	private String mkdfaScript_ = "./mkdfa.pl";
	private String mkdfaDir_ = ".";
	private boolean running_ = false;
	private String mkdfaExe_;
	private boolean useMkdfaExe_ = false;
	private static int count_ = 0;

	public static void main(String argv[]) {
		String renrakuRes = "/res/grammar/renraku.xml";
		//String renrakuRes = "/res/grammar/confirm_yes_no.xml";
		GrammarTransformer gt = new GrammarTransformer();
		String str = DialogManager.getResourceAsString(renrakuRes);
		String base = Util.getPlatformWorkDir() + "/renraku";
		if (gt.doTransform(str, base) == false) {
			System.out.print("[error]\n");
			System.out.print("[E]\n" + gt.getErrors());
			System.out.print("[O]\n" + gt.getOutputs());
		} else {
			System.out.print("[ok]\n");
			System.out.print("[grammar]\n" + gt.getGrammar());
			System.out.print("[voca]\n" + gt.getVoca());
			System.out.print("[E]\n" + gt.getErrors());
			System.out.print("[O]\n" + gt.getOutputs());
		}
	}

	public GrammarTransformer() {
		grammar_ = "";
		voca_ = "";
		errors_ = "";
		outputs_ = "";
//		exception_ = "";
		
		mkdfaPerl_ = Property.getForPlatformAsStr(
				"Grammar.Mkdfa.Perl", 
				"perl");
		mkdfaDir_ = Property.getForPlatformAsStr(
				"Grammar.Mkdfa.Dir", 
				"/data/istc-cdrom-2006-10/linux/bin/SRM/bin"
				);
		mkdfaScript_ = Property.getForPlatformAsStr(
				"Grammar.Mkdfa.Script", 
				"mkdfa.pl");
		mkdfaExe_ = Property.getForPlatformAsStr(
				"Grammar.Mkdfa.Exe", 
				"mkdfa.exe");
		useMkdfaExe_  = Property.getForPlatformAsBoolean("Grammar.UseMkdfaExe", true);
		
		if (count_ == 0) {
			Runtime.getRuntime().addShutdownHook(new Thread() {
				public void run() {
					dbg.print("Shutting down");
					if (subProcess_ != null)
						try {
							subProcess_.destroy();
						} catch (Exception e) {
							// ignore
						}
				}
			});
			count_++;
		}
	}
	
	public boolean doTransform(String str, String base) {
		if (_transform(str) == false) {
//			 System.out.print("transform error");
			return false;
		}
		if (_makeSrcFiles(base) == false) {
//			 System.out.print("makeSrcFiles error");
			return false;
		}
		if (_generate(base) == false) {
//			 System.out.print("generate error");
			return false; 
		}
		return true;
	}
	
	private boolean _generate_ORG(String base) {
		subProcess_ = new SubProcess(mkdfaDir_ + "/" + mkdfaScript_ + " " + base);
		if (subProcess_ == null) {
			return false;
		}
		if (subProcess_.run(mkdfaDir_) == false) {
			errors_ = subProcess_.getError();
			return false;
		}
//		exception_ = "";
		errors_ = "";
		outputs_ = "";
		Thread th = new Thread(new Runnable() {
			public void run() {
				boolean errEnd = false;
				boolean outEnd = false;
				while (!(errEnd && outEnd)) {
					while (true) {
						String er = subProcess_.receiveFromStderr();
						if (er == null) {
							errEnd = true;
							break;
						} else {
							errors_ += er + "\n";
						}
					}
					while (true) {
						String out = subProcess_.receiveFromStdout();
						if (out == null) { 
							outEnd = true;
							break;
						} else {
							outputs_ += out + "\n";
						}
					}
					try {
						Thread.sleep(1);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				subProcess_.destroy();
				running_ = false;
			}
		});
		th.setName("GrammarTransformer(_generate thread)");
		running_ = true;
		th.start();
		while (running_) {
			try {
				Thread.sleep(1);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		if (outputs_.indexOf("generated:") >= 0) {
			return true;
		}
//		exception_ += errors_;
		return false;
	}
	
	/**
	 * run mkdfa
	 * @param base
	 * @return false if error
	 */
	private boolean _generate(String base) {
//		exception_ = "";
		errors_ = "";
		outputs_ = "";
		String cmd = "";
		if (useMkdfaExe_) {
			cmd = mkdfaDir_ + "/" + mkdfaExe_ + " " + base;
		} else {
			cmd = mkdfaPerl_ + " " + mkdfaDir_ + "/" + mkdfaScript_ + " " + base;
		}
		String dir = mkdfaDir_;
		base = Util.getPlatformFileName(base);
		cmd = Util.getPlatformFileName(cmd); 
		dir = Util.getPlatformFileName(dir);
		errors_ = "generate SubProcess(dir:" + dir + ")\n" + cmd + "\n";
		subProcess_ = new SubProcess(cmd);
		if (subProcess_ == null) {
			return false;
		}
		if (subProcess_.run(dir) == false) {
			errors_ = subProcess_.getError();
			return false;
		}
		Thread th = new Thread(new Runnable() {
			public void run() {
				boolean working = true;
				while (working) {
					String er = subProcess_.receiveFromStderrNB();
					if (er != null && er.length() > 0) {
						errors_ += er + "\n";
					}
					String out = subProcess_.receiveFromStdoutNB();
					if (out != null && out.length() > 0) { 
						outputs_ += out + "\n";
						if (out.indexOf("generated:") >= 0) {
							working = false;
						}
						if (out.indexOf("no .dfa or .dict file generated") >= 0) {
							working = false;
						}
					}
					try {
						Thread.sleep(1);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				subProcess_.destroy();
				running_ = false;
			}
		});
		th.setName("GrammarTransformer(_generate thread)");
		running_ = true;
		th.start();
		while (running_) {
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		if (outputs_.indexOf("generated:") >= 0) {
			return true;
		}
//		exception_ += errors_ + outputs_;
		return false;
	}
	
	/**
	 * write to file : grammar_ voca_ => hoge.grammar hoge.voca
	 * @param base
	 * @return false if error
	 */
	private boolean _makeSrcFiles(String base) {
		errors_ = "";
		outputs_ = "";
//		exception_ = "";
		try {
			Charset cs = Util.getSystemDefaultCharset(); // Charset.forName("SJIS");
			Util.writeToFile(base + ".grammar", getGrammar(), cs);
			Util.writeToFile(base + ".voca", getVoca(), cs);
		} catch (Exception e) {
			errors_ = "makeSrcFiles() " + e.toString(); // e.printStackTrace();
			return false;
		}
		return true;
	}
	
	/**
	 * generate as string : grammar_ voca_ 
	 * @param s : ipa-xml-grammar string
	 * @return false if error
	 */
	private boolean _transform(String s) {
		errors_ = "";
		outputs_ = "";
		TransformerFactory factory = TransformerFactory.newInstance();
		InputStream is = DialogManager.class.getResourceAsStream(xslRes);
		StreamSource xsl = new StreamSource(is);
		try {
			transformer_ = factory.newTransformer(xsl);
		} catch (TransformerConfigurationException e) {
			errors_ = "transform() " + e.toString(); // e.printStackTrace();
			return false;
		}
		StringReader sr = new StringReader(s);
		StreamSource src = new StreamSource(sr);
		StringWriter writer = new StringWriter();
		StreamResult target = new StreamResult(writer);
		try {
			transformer_.transform(src, target);
		} catch (Exception e) {
			errors_ = "transform() " +  e.toString(); // e.printStackTrace();
			return false;
		}
		String ws = new String(writer.getBuffer());
		String lines[] = ws.replaceAll("\r","").split("\n");
		Pattern pat = Pattern.compile("^[^\\s]+ :.*$");
		grammar_ = "";
		for (int i = 0, n = lines.length; i < n; i++) {
			Matcher mat = pat.matcher(lines[i]);
			if (mat.matches()) {
				grammar_ += lines[i] + "\n"; // System.out.println(lines[i]);
			}
		}
		voca_ = "";
		Pattern pat2 = Pattern.compile("^\\s*$");
		for (int i = 0, n = lines.length; i < n; i++) {
			Matcher mat = pat.matcher(lines[i]);
			Matcher mat2 = pat2.matcher(lines[i]);
			if (!mat.matches() 
					&& !mat2.matches() 
					&& lines[i].length() > 0 && lines[i].charAt(0) != '#') {
				voca_ += lines[i] + "\n"; //System.out.println(lines[i]);
			}
		}
		return true;
	}
	
	public String getGrammar() { 
		return grammar_; 
	}
	
	public String getVoca() {
		return voca_; 
	}

	public String getErrors() {
		return errors_; 
	}

	public String getOutputs() {
		return outputs_; 
	}
	
//	public String getException() {
//		return exception_;
//	}
}
