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

package galatea.document;

import galatea.command.AddOutItemCommand;
import galatea.command.Command;
import galatea.command.CompositeCommand;
import galatea.command.EvaluateCommand;
import galatea.command.SetNextCommand;
import galatea.dialog.Tstamp;
import galatea.grammar.GrammarRuleSet;
import galatea.logger.Logger;
import galatea.outitem.NativeOutItem;
import galatea.util.Util;

import java.util.ArrayList;
import java.util.HashMap;

public class StateMap
{
//	public static final String PDOC_ENCODING = "euc-jp";
//	private static float PDOC_VERSION = 0.9f;
	
	private Logger dbg = new Logger("StateMap");
	
	private float pdocVersion_ = 0.0f;
	
	public void setPdocVersion(float f)
	{
		pdocVersion_ = f;
	}
	
	// Map<String, ContentState>
	private HashMap<String, ContentState> hashMap_ = null; 
	
	private String firstStateName_ = null;
	
	// array to keep the order of put
	private ArrayList<String> states_ = null;
	
	// document-wide grammar set
	private GrammarRuleSet grammarSet_ = null;
	
	public StateMap()
	{
		hashMap_ = new HashMap<String, ContentState>();
		states_ = new ArrayList<String>();
		
		setPdocVersion( Tstamp.STATEMAP_PDOC_VERSION );
		
		//Document doc;
		//Node n1 = XPathAPI.selectSingleNode(doc, "/hoge/text()");
	}
	
	/**
	 * if fails, returns null
	 */
	public ContentState put(String key, ContentState value)
	{
		states_.add(key);
		ContentState ret = (ContentState)hashMap_.put(key, value);
		dbg.ASSERT(ret==null, "put with no override");
		return ret;
	}
	
	public ContentState get(String key)
	{
		dbg.print("using StateMap.get(" + key + ")" );
		ContentState cs = (ContentState)hashMap_.get((Object)key);
		/*
		 if (cs == null) {
		 dbg.DIE("StateMap.get(" + key + ") not found !" );
		 }
		 */
		return cs;
	}
	
	public boolean has(String key)
	{
		ContentState cs = (ContentState)hashMap_.get((Object)key);
		if (cs == null) {
			return false;
		}
		return true;
	}
	
	public String newState(String newID) 
	{
		dbg.ASSERT( newID != null && newID.length() > 0, "newID empty");
		ContentState state = (ContentState)hashMap_.get((Object)newID);
		if ( state != null ) {
			dbg.print("overwriting " + newID);
			return newID;
		}
		dbg.ASSERT( state == null, "newState()");
		state = new ContentState();
		state.setName(newID);
		dbg.print( "newState:" + newID );
		put(newID, state);
		return newID;
	}
	
	public void addCommand(String id, Command c)
	{
		ContentState state = get(id);
		if ( state == null ) {
			String newID = newState(id);
			state = get(newID);
			dbg.ASSERT(id.equals(newID), "addCommand() failed 1");
		}
		dbg.ASSERT(state != null, "addCommand() failed 2");
		if (c != null) {
			if ( c instanceof CompositeCommand && ! c.hasCondition() ) {
				ArrayList<Command> commands = ((CompositeCommand)c).getCommands();
				for ( int i = 0; i < commands.size(); i++ ) {
					state.addCommand((Command)commands.get(i));
				}
			} else {
				state.addCommand(c);
			}
		}
	}
	
	public void addCommand(String id, ArrayList<Command> commands)
	{
		ContentState state = get(id);
		/*
		 dbg.ASSERT(state != null, "addCommand() failed 3 id=" + id);
		 */
		if ( state == null ) {
			String newID = newState(id);
			state = get(newID);
			dbg.ASSERT(id.equals(newID), "addCommand() failed 3");
		}
		dbg.ASSERT(state != null, "addCommand() failed 4");
		if (commands != null) {
			for ( int i = 0; i < commands.size(); i++ ) {
				addCommand(id, (Command)commands.get(i));
			}
		}
	}
	
	public void addCommandOnTop(String id, Command c)
	{
		ContentState state = get(id);
		dbg.ASSERT(state != null, "addCommandOnTop()");
		state.addCommandOnTop(c);
	}
	
	public void addScriptCommand(String id, String script)
	{
		addCommand(id, new EvaluateCommand(script));
	}
	
	public void addScriptCommand(String id, String script, String with)
	{
		addCommand(id, new EvaluateCommand(script, with));
	}
	
	public void addTrans(String src, String dst, String input) 
	{
		dbg.print( "addTrans: " + src + ", " + dst + ", " + input );
		ContentState state = get(src);
		dbg.ASSERT(state != null, "addTrans()");
		state.putEventHandler(input, dst);
	}
	
	public void addDefaultTrans(String src, String dst) 
	{
		addTrans(src, dst, ".*"); 
	}
	
	/*
	 public void setNomatch(String src, String dst) 
	 {
	 ContentState state = get(src);
	 dbg.ASSERT(state != null, "addTrans()");
	 state.setNomatch(dst);
	 }
	 */
	
	public void addSetNextCommand(String id, String dst)
	{
		addCommand(id, new SetNextCommand("'"+dst+"'"));
	}
	
	public void addOutputNativeCommand(String id, String msg) {
		addCommand(id, new AddOutItemCommand(new NativeOutItem(msg)));
	}
	
	public void setFirstStateName(String name)
	{
		firstStateName_ = name;
	}
	
	public String getFirstStateName()
	{
		return firstStateName_;
	}
	
	public ContentState getFirstState()
	{
		return (ContentState)(hashMap_.get(firstStateName_));
	}
	
	public void add(ContentState state) {
		put(state.getName(), state);
	}
	
	public GrammarRuleSet getGrammarSet()
	{
		return grammarSet_;
	}
	
	public void setGrammarSet(GrammarRuleSet g)
	{
		grammarSet_ = g;
	}
	
	public String toString()
	{
		StringBuffer s = new StringBuffer();
		s.append("<?xml version=\"1.0\" encoding=\"" );
		s.append(Util.getSystemDefaultCharset());
		s.append("\" ?>\n");
		s.append("<pdoc version=\"");
		s.append(pdocVersion_);
		s.append("\">\n");
		
		if ( grammarSet_ != null ) {
			s.append("<head>\n");
			s.append(grammarSet_.getRulesAsString());
			s.append("\n");
			//s.append("<!--\n");
			//s.append(grammarSet_.getSRMGramStr());
			//s.append("\n");
			//s.append("-->\n");
			s.append("</head>\n\n");
		}
		
		s.append("<body start=\"");
		s.append(firstStateName_);
		s.append("\">\n\n");
		
		for ( int i = 0; i < states_.size(); i++ ) {
			String keyname = (String)states_.get(i);
			ContentState state = (ContentState)hashMap_.get((Object)keyname);
			s.append(state.toString());
			s.append("\n");
		}
		s.append("</body>\n");
		s.append("</pdoc>\n");
		return s.toString();
	}

}
