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

import galatea.logger.Logger;
import galatea.relaxer.vxml20.VxmlGrammar;
import galatea.relaxer.vxml20.VxmlItem;
import galatea.relaxer.vxml20.VxmlOneOf;
import galatea.relaxer.vxml20.VxmlRule;
import galatea.relaxer.vxml20.VxmlRuleref;
import galatea.relaxer.vxml20.VxmlToken;
import galatea.util.Util;

public class GrammarMaker
{
	private Logger dbg = new Logger("GrammarMaker", 0);
	
	public GrammarMaker() 
	{
	}
	
	private int unique_ = 0;
	
	private String _getUniqueId()
	{
		unique_++;
		StringBuffer id = new StringBuffer("grm").append(unique_);
		return id.toString();
	}
	
	/**
	 * item/ruleref+ 
	 */
	private boolean _isRulerefOnly(VxmlItem item)
	{
		int ntoken = 0;
		int nruleref = 0;
		for (int i = 0; i < item.sizeContent(); i++ ) {
			if ( item.getContent(i) instanceof VxmlToken ) {
				ntoken++;
			} else if ( item.getContent(i) instanceof VxmlRuleref ) {
				nruleref++;
			}
		}
		if ( ntoken == 0 )
			return true;
		return false;
	}
	
	/**
	 * item/token
	 * tag は許される
	 */
	private boolean _isToken(VxmlItem item)
	{
		int ntoken = 0;
		int nruleref = 0;
		for (int i = 0; i < item.sizeContent(); i++ ) {
			if ( item.getContent(i) instanceof VxmlToken ) {
				ntoken++;
			} else if ( item.getContent(i) instanceof VxmlRuleref ) {
				nruleref++;
			}
		}
		if ( ntoken == 1 && nruleref == 0 )
			return true;
		return false;
	}
	
	
	// rule/token の場合
	// そのまま IPA-SRG-XML-1.0 にする
	// ただし <tag> を解釈する
	//
	// <rule id="no"><token sym="の">の</token></rule>
	//
	private void _addRuleOfToken(VxmlGrammar ipasrg, String ruleid, VxmlToken token)
	{
		VxmlRule rule = new VxmlRule(); 
		rule.setId(ruleid);
		rule.addContent(token);
		ipasrg.addContent(rule);
	}
	
	private void _addRuleOfToken(VxmlGrammar ipasrg, String ruleid, VxmlItem item)
	{
		VxmlRule rule = new VxmlRule(); 
		rule.setId(ruleid);
		for (int i = 0; i < item.sizeContent(); i++ ) {
			if (! (item.getContent(i) instanceof VxmlToken) )
				continue;
			VxmlToken token = (VxmlToken)(item.getContent(i));
			token.setContent(Util.removeSpaces(token.getContent()));
			rule.addContent(token);
			break;
		}
		ipasrg.addContent(rule);
	}
	
	// item/ruleref+ の場合
	// 同一 id の rule を生成する
	//
	// <item> <ruleref uri="#pre"/> <ruleref uri="#place"/> </item>
	//
	// <rule id="root"> <ruleref uri="pre"/> <ruleref uri="place"/> </rule>
	//
	private void _addRuleOfRuleref(VxmlGrammar ipasrg, String ruleid, VxmlItem item)
	{
		VxmlRule rule = new VxmlRule(); 
		rule.setId(ruleid);
		for (int i = 0; i < item.sizeContent(); i++ ) {
			if (! (item.getContent(i) instanceof VxmlRuleref) )
				continue;
			
			VxmlRuleref ref = (VxmlRuleref)(item.getContent(i));
			String uri = ref.getUri().replaceAll("#", "");
			ref.setUri(uri);
			rule.addContent(ref);
		}
		ipasrg.addContent(rule);
	}
	
	
	private void _makeIpaXmlFromRule(VxmlGrammar ipasrg, VxmlRule rule)
	{
		String ruleid = rule.getId();
		if (ruleid == null ) 
			ruleid = _getUniqueId();
		
		// System.out.println("ruleid="+ruleid);
		// System.out.println("size="+rule.sizeContent());
		
		for ( int i = 0; i < rule.sizeContent(); i++ ) {
			
			if ( rule.getContent(i) instanceof VxmlToken ) {
				// rule/token の場合
				// そのまま IPA-SRG-XML-1.0 にする
				//
				// TODO: <token> に後続する <tag> を解釈する
				VxmlToken token = (VxmlToken)(rule.getContent(i));
				_addRuleOfToken(ipasrg, ruleid, token);
				
				// token の後に one-of が出てきたら無視する
				break;
				
			} else if ( rule.getContent(i) instanceof VxmlOneOf ) {
				VxmlOneOf oneof = (VxmlOneOf)(rule.getContent(i));
				
				// System.out.println("one-of:\n"+oneof.makeTextDocument());
				
				// foreach item+
				for ( int j = 0; j < oneof.sizeItem(); j++ ) {
					
					VxmlItem item = oneof.getItem(j);
					
					if ( _isRulerefOnly(item) ) {
						// rule/one-of/item+/ruleref+ の場合
						// 同一 id の複数 rule を内部で生成する
						_addRuleOfRuleref(ipasrg, ruleid, item);
						
					} else if ( _isToken(item) ) {
						// rule/one-of/item+/token の場合
						// item ごとに new-id を生成し、それぞれ
						// rule/ruleref(@new-id)
						// rule(@new-id)/token
						// を生成する
						
						// <rule id="etto"><ruleref uri="etto_grm1"/></rule>
						// <rule id="etto_grm1"><token sym="あ">あ</token></rule>
						// <rule id="etto"><ruleref uri="etto_grm2"/></rule>
						// <rule id="etto_grm2"><token sym="あの">あの</token></rule>
						
						String newid = _getUniqueId() + "_" + ruleid;
						
						VxmlRuleref nref = new VxmlRuleref();
						nref.setUri(newid);
						
						VxmlRule nrule = new VxmlRule();
						nrule.setId(ruleid);
						nrule.addContent(nref);
						
						ipasrg.addContent(nrule);
						
						_addRuleOfToken(ipasrg, newid, item);
					}
				}
				
				// one-of の後に token が出てきたら無視する
				break;
			}
		}
	}
	
	/**
	 * grammar version="1.0" と version="IPA-SRG-XML-1.0" との違い
	 * 同じ id の要素が複数存在しないこと
	 * 
	 * uri は外部ファイルを参照できる
	 */
	public VxmlGrammar makeIpaXml(VxmlGrammar gram)
	{
		VxmlGrammar ipasrg = new VxmlGrammar();
		
		for (int i = 0; i < gram.sizeContent(); i++) {
			if ( gram.getContent(i) instanceof VxmlRule ) {
				VxmlRule rule = (VxmlRule)(gram.getContent(i));
				_makeIpaXmlFromRule(ipasrg, rule);
			}
		}
		return ipasrg;
	}
	
	
	/**
	 * uri にフラグメントがない: grammar root="id" を見て root の Rule を返す
	 * uri にフラグメントがある: その id の Rule を返す
	 */
	private VxmlRule _getRuleByUri(String uri) throws Exception
	{
		dbg.print("_getRuleByUri() " + uri);
		
		String subfile  = Util.getUriWithoutFragment(uri);
		String fragment = Util.getUriFragment(uri);
		
		dbg.print("new VxmlGrammar(" + subfile + ")");
		VxmlGrammar src = new VxmlGrammar(subfile);
		VxmlRule newrule = new VxmlRule(); 
		
		for (int i = 0; i < src.sizeContent(); i++) {
			if ( src.getContent(i) instanceof VxmlRule ) {
				VxmlRule rule = (VxmlRule)(src.getContent(i));
				if ( rule.getId().equals( fragment ) ) {
					dbg.print("_getRuleByUri() " + fragment + " found");
					newrule.setup(rule);
				}
			}
		}
		
		newrule.setId(fragment);
		return newrule;
	}
	
	/**
	 * grammar version="1.0" の外部参照を解決する
	 *
	 * 再帰的に参照できる
	 */
	public void resolveExtRuleRef(VxmlGrammar dest, VxmlRule rule, String adrs, String org) throws Exception
	{
		dbg.print("resolveExtRuleRef() adrs=" + adrs + " org=" + org);
		VxmlRule newrule = new VxmlRule();
		newrule.setId(rule.getId());
		
		for (int j = 0; j < rule.sizeContent(); j++) {
			if ( rule.getContent(j) instanceof VxmlOneOf ) {
				VxmlOneOf oneof    = (VxmlOneOf)(rule.getContent(j));
				VxmlOneOf newoneof = new VxmlOneOf();
				newoneof.setup(oneof);
				
				for (int k = 0; k < oneof.sizeItem(); k++) {
					VxmlItem item = oneof.getItem(k);
					
					for (int m = 0; m < item.sizeContent(); m++) {
						if ( item.getContent(m) instanceof VxmlRuleref ) {
							VxmlRuleref ref = (VxmlRuleref)(item.getContent(m));
							
							String uri = ref.getUri();
							
							
							if ( uri == null ) continue;
							dbg.print("adrs="+adrs+" uri="+uri);
							uri = Util.resolveAdrs(adrs, uri);
							
							dbg.print("org=" + org + " resolved="+uri);
							
							if ( ! Util.isSameFile(org, uri) ) {
								VxmlRule subrule = _getRuleByUri(uri);
								resolveExtRuleRef(dest, subrule, uri, org);
								
								VxmlRuleref r = (VxmlRuleref)(newoneof.getItem(k).getContent(m));
								r.setUri( "#" + Util.getUriFragment(uri) );
							}
						}
					}
				}
				newrule.addContent( newoneof );
				
			} else if ( rule.getContent(j) instanceof VxmlRuleref ) {
				//  from : <ruleref uri="dialog.grxml#pref"/>
				//  to : <one-of> <item> <ruleref uri="dialog.grxml#pref"/> </item> </one-of>
				VxmlRuleref ref = (VxmlRuleref)(rule.getContent(j));
				String uri = ref.getUri();
				
				dbg.print("adrs="+adrs);
				dbg.print("uri="+uri);
				
				if ( uri == null ) continue;
				uri = Util.resolveAdrs(adrs, uri);
				
				dbg.print("uri org:" + org + " resolved:"+uri);
				
				VxmlOneOf newoneof = new VxmlOneOf();
				
				VxmlItem newitem =  new VxmlItem();
				newoneof.addItem(newitem);
				
				VxmlRuleref newruleref = new VxmlRuleref();
				newitem.addContent(newruleref);
				
				if ( ! Util.isSameFile(org, uri) ) {
					VxmlRule subrule = _getRuleByUri(uri);
					resolveExtRuleRef(dest, subrule, uri, org);
				}
				newruleref.setUri( "#" + Util.getUriFragment(uri) );
				
				newrule.addContent( newoneof );
				
			} else {
				newrule.addContent( rule.getContent(j) );
			}
		}
		dest.addContent(newrule);
	}
	
	/**
	 * grammar version="1.0" の外部参照を解決する
	 *
	 * <rule id="korega"> <one-of> <item> <ruleref uri="dialog.grxml#kore"/>
	 */
	public void resolveExtRuleRef(VxmlGrammar dest, VxmlGrammar src, String adrs, String org) throws Exception
	{
		for (int i = 0; i < src.sizeContent(); i++) {
			if ( src.getContent(i) instanceof VxmlRule ) {
				VxmlRule rule = (VxmlRule)(src.getContent(i));
				resolveExtRuleRef(dest, rule, adrs, org);
			} else {
				dest.addContent(src.getContent(i));
			}
		}
	}
	
	
	public VxmlGrammar makeIpaXml(VxmlGrammar gramXml, String file) throws Exception
	{
		VxmlGrammar src = new VxmlGrammar();
		resolveExtRuleRef(src, gramXml, file, file);
		VxmlGrammar ipasrg = makeIpaXml(src);
		GrammarUtil.convertToken(ipasrg);
		ipasrg.setVersion("IPA-XML-1.0");
		ipasrg.setRoot(gramXml.getRoot());
		return ipasrg;
	}
	
	
	/**
	 * cd phoenix
	 * java -cp ../lib/gdm.jar grammar.GrammarMaker
	 */
//	public static void main(String args[]) throws Exception
//	{
//		GrammarMaker gm = new GrammarMaker();
//		String file = "../tests/sample.grxml";
//		System.out.println("file:" + file);
//		VxmlGrammar gram1 = new VxmlGrammar(file);
//		
//		System.out.println("src:\n" + makeForDocument(gram1));
//		
//		VxmlGrammar src = new VxmlGrammar();
//		gm.resolveExtRuleRef(src, gram1, file, file);
//		
//		System.out.println("resolved:\n" + makeForDocument(src));
//		
//		VxmlGrammar ipasrg = gm.makeIpaXml(src);
//		GrammarUtil.convertToken(ipasrg);
//		
//		System.out.println("ipaxml:\n" + makeForDocument(ipasrg));
//	}
	
}
