/*
 * Galatea Dialog Manager:
 * (c)2003 Takuya NISHIMOTO (nishi@hil.t.u-tokyo.ac.jp)
 * Based on Phoenix By Takuya NISHIMOTO and Mitsuhiro KIZU
 *
 * $Id: GrammarRuleSet.java,v 1.6 2008/02/14 02:00:07 nishi Exp $
 */

package galatea.grammar;

import java.io.IOException;
import java.io.InputStream;

import javax.xml.parsers.ParserConfigurationException;

import org.xml.sax.SAXException;

import galatea.dialog.DialogManager;
import galatea.logger.Logger;
import galatea.relaxer.vxml20.IVxmlGrammarChoice;
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.HashArray;
import galatea.util.Property;
import galatea.util.Util;


public class GrammarRuleSet
{
	private Logger dbg = new Logger("GrammarRuleSet");
	
	// Map<String gramAdrs:gramId, VxmlRule rule>
	private HashArray rules_ = null;
	
	private int unique_ = 0;
	
	public GrammarRuleSet() 
	{
		rules_ = new HashArray();
	}
	
	
	public String toString()
	{
		/*
		 String gramStr = "srgs:";
		 for ( int i = 0; i < rules_.size(); i++ ) {
		 VxmlRule rule = (VxmlRule)rules_.get(i);
		 gramStr += rule.getId() + ";";
		 }
		 return gramStr;
		 */
		return getRulesAsString();
	}
	
	
	public void setupTemplateGrammar()
	{
		VxmlGrammar templateGrammar = new VxmlGrammar();
		
//		String gfile = Property.getAsStr("GrammarTemplateFile", "../lib/grammar.xml");
//		try {
//			templateGrammar.setup(gfile);
//		} catch (Exception e) {
//			e.printStackTrace();
//		}
		
		final String propertiesPath = "/res/grammar/grammar.xml";
		InputStream is = DialogManager.class.getResourceAsStream(propertiesPath);
		try {
			templateGrammar.setup(is);
		} catch (IOException e1) {
			e1.printStackTrace();
		} catch (SAXException e1) {
			e1.printStackTrace();
		} catch (ParserConfigurationException e1) {
			e1.printStackTrace();
		}
		
		for ( int i = 0; i < templateGrammar.sizeContent(); i++ ) {
			IVxmlGrammarChoice c = templateGrammar.getContent(i);
			if ( c instanceof VxmlRule ) {
				String key = _getUniqueId();
				rules_.put(key, (Object)c);
			}
		}
	}
	
	
	public void addRulesFromGrammar(VxmlGrammar gramXml, String gramAdrs)
	{
		if ( gramXml == null )
			return;
		if ( gramAdrs == null )
			gramAdrs = "";
		if ( gramXml.getRoot() != null ) {
			_addRulesWithRoot(gramXml, gramAdrs);
		} else {
			_addRulesWithoutRoot(gramXml, gramAdrs);
		}
	}
	
	
//	public String getGrammarChangeCommand()
//	{
//		String gramStr = "";
//		VxmlGrammar gramset = new VxmlGrammar();
//		gramset.setVersion("IPA-XML-1.0");
//		
//		for ( int i = 0; i < rules_.size(); i++ ) {
//			VxmlRule rule = (VxmlRule)rules_.get(i);
//			gramset.addContent(rule);
////			String id = rule.getId();
//		}
//		//GrammarMaker.convertToken(gramset);
//		
//		gramStr = Util.removeNewLines(gramset.makeTextDocument());
//		gramStr = gramStr.replaceAll(" xmlns=\"[^\"]*\"","");
//		gramStr = gramStr.replaceAll("> <","><");
//		gramStr = gramStr.replaceAll("></ruleref>","/>");
//		return "to @SIM set SRM_XML_String = " + gramStr; 
//	}
	
	
	/**
	 *
	 */
	public String getRulesAsString()
	{
		String gramStr = "";
		VxmlGrammar gramset = new VxmlGrammar();
		gramset.setVersion("IPA-XML-1.0");
		for ( int i = 0; i < rules_.size(); i++ ) {
			VxmlRule rule = (VxmlRule)rules_.get(i);
			gramset.addContent(rule);
		}
		
		//GrammarMaker.convertToken(gramset);
		
		//gramStr = GrammarUtil.makeForDocument(gramset);
		
		gramStr = Util.removeNewLines(gramset.makeTextDocument());
		gramStr = gramStr.replaceFirst(" xmlns=\"[^\"]*\"","");
		gramStr = gramStr.replaceAll("<rule ","\n<rule ");
		gramStr = gramStr.replaceAll("</grammar>","\n</grammar>");
		gramStr = gramStr.replaceAll("> <","><");
		gramStr = gramStr.replaceAll("></ruleref>","/>");
		
		return(gramStr);
	}
	
	
	private String _getUniqueId()
	{
		unique_++;
		String id = "ipa" + unique_;
		return id;
	}
	
	
	private String _getTokenContent(VxmlRule rule)
	{
		return Util.removeNewLines(rule.makeTextDocument());
	}
	
	
	private boolean _sameRuleExists(VxmlRule rule)
	{
		String curr = _getTokenContent(rule);
		return _sameRuleExists(curr);
	}
	
	
	private boolean _isSingleTokenRule(VxmlRule rule)
	{
		int nToken = 0;
		int nOther = 0;
		for ( int i = 0; i < rule.sizeContent(); i++ ) {
			if ( rule.getContent(i) instanceof VxmlToken ) {
				nToken ++;
			} else if ( rule.getContent(i) instanceof VxmlItem ) {
				nOther ++;
			} else if ( rule.getContent(i) instanceof VxmlOneOf ) {
				nOther ++;
			} else if ( rule.getContent(i) instanceof VxmlRuleref ) {
				nOther ++;
			}
		}
		if ( nToken == 1 && nOther == 0 ) return true;
		return false;
	}
	
	
	private void _addRuleref( VxmlRule rule, String uri )
	{
		VxmlRuleref ref = new VxmlRuleref();
		ref.setUri(uri);
		rule.addContent(ref);
		rule.setId("root");
	}
	
	
	// rules_ の中の既存の VxmlRule と phoneme が
	// 一致していたら true を返す
	private boolean _sameRuleExists(String curr)
	{
		for ( int i = 0; i < rules_.size(); i++ ) {
			String r = _getTokenContent((VxmlRule)(rules_.get(i)));
			if ( curr.equals(r) )
				return true;
		}
		dbg.print("new rule found:" + curr);
		return false;
	}
	
	private void _addRootRule(VxmlRule rule, String key)
	{
		if ( ! _sameRuleExists(rule) ) {
			rules_.put(key, (Object)rule);
		}
	}
	
	private void _addNonRootRule(VxmlRule rule, String key)
	{
		if ( ! _sameRuleExists(rule) ) {
			rules_.put(key, (Object)rule);
		}
	}
	
	
	private void _addRulesWithoutRoot(VxmlGrammar gramXml, String gramAdrs)
	{
		int nContent = gramXml.sizeContent();
		for ( int i = 0; i < nContent; i++ ) {
			IVxmlGrammarChoice choice = gramXml.getContent(i);
			if ( choice instanceof VxmlRule ) {
				
				VxmlRule rule = (VxmlRule)choice;
				
				//dbg.print( "using rule " + rule );
				
				// <grammar> に root 属性がない場合は，
				// 含まれるすべての <rule> を root と見なす（本当にいいのか？）
				//boolean isRootGrammar = false;
				//if ( rule.getId() != null && rule.getId().equals(root) ) {
				//    isRootGrammar = true;
				//}
				boolean isRootGrammar = true;
				
				boolean isAnonymousRule = false;
				
				String id = rule.getId();
				if ( id == null ) {
					id = _getUniqueId();
					rule.setId(id);
					isAnonymousRule = true;
				} else {
					rule.setId( id.replaceAll("#", "") );
				}
				
				// 大文字のみは内部ルール
				if ( id.matches("[A-Z_]+") ) {
					isRootGrammar = false;
				}
				
				String key = gramAdrs + ":" + id + ":" + _getUniqueId();
				
				if ( isRootGrammar ) {
					// ex. file.vxml:yes 
					//     file.vxml#dialog:yes 
					if ( _isSingleTokenRule(rule) ) {
						dbg.print( "(root) addRootRule " + key );
						_addRootRule(rule, key);
					}
				} else {
					dbg.print( "(not root) addNonRootRule " + key );
					_addNonRootRule(rule, key);
				}
				
				if ( isAnonymousRule ) {
					// <rule id="root">
					//  <ruleref uri="yes"/>
					// </rule>
					VxmlRule rootRule = new VxmlRule();
					_addRuleref( rootRule, id );
					_addRootRule( rootRule, key + ":root");
				}
				
			}
		}
	}
	
	
	private void _addRulesWithRoot(VxmlGrammar gramXml, String gramAdrs)
	{
		GrammarMaker maker = new GrammarMaker();
		try {
			gramXml = maker.makeIpaXml(gramXml, gramAdrs);
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		String myroot = gramXml.getRoot().replaceAll("#", "");
		if ( ! myroot.equals("root") ) {
			VxmlRule rootRule = new VxmlRule();
			_addRuleref( rootRule, myroot );
			_addRootRule( rootRule, _getUniqueId() );
		}
		
		int nContent = gramXml.sizeContent();
		for ( int i = 0; i < nContent; i++ ) {
			IVxmlGrammarChoice choice = gramXml.getContent(i);
			if ( choice instanceof VxmlRule ) {
				VxmlRule rule = (VxmlRule)choice;
				String key = _getUniqueId();
				rules_.put(key, (Object)rule);
			}
		}
	}
	
}
