package prolog.implementation;

import java.io.*;
import java.util.*;
import prolog.util.*;
import prolog.model.*;

/**
 * <p>Title: Programm</p>
 * <p>Descrpition: this class describes a prolog programm</p>
 */
public class Program
	implements
		IProgram
{
	/**
	 * in this list, all queries found by the parser are inserted
	 */
	protected IQueryList querys = null;

	/**
	 * this is the fact database with all facts in it
	 */
	protected IFactDb facts = null;

	/**
	 * in this map, all statements are added with their name as key
	 */
	protected IStatementMap statements = null;

	/**
	 * this is the symboltable to decode the
	 * programm internal used integers to  strings
	 */
	protected SymbolTable symbolTable = null;

	/**
	 * the logger logs the output of all of the programm e.g. to Console
	 */
	protected ILogger logger = null;

	/**
	 * constructor
	 */
	public Program()
	{
		this.facts = SingletonFactory.getBuilder().createFactDb();
		logger = new StdOutLogger();
		this.symbolTable = (SymbolTable) SingletonFactory.getBuilder().getSymbolTable();
		this.statements = SingletonFactory.getBuilder().createStatementMap();
		this.querys = SingletonFactory.getBuilder().createQueryList();
	}

	/**
	 * sets the logger used for processing the programm.
	 * all output will be routed over this logger
	 */
	public void setLogger( ILogger logger )
	{
		this.logger = logger;
	}

	/**
	 * clear the programm (clears all items found by the parser)
	 */
	public void clear()
	{
		querys.clear();
		facts.clear();
		statements.clear();
		symbolTable.clear();
	}

	/**
	 * returns the used symboltable to en- and decode the names
	 */
	public ISymbolTable getSymbolTable()
	{
		return this.symbolTable;
	}

	/**
	 * sets the symboltable
	 */
	public void setSymbolTable( SymbolTable table )
	{
		this.symbolTable = table;
	}

	/**
	 * returns a list with all queries found by the parser
	 */
	public IQueryList getQuerys()
	{
		return this.querys;
	}

	/**
	 * sets the fact database used to process a program
	 */
	public void setFacts( IFactDb f )
	{
		this.facts = f;
	}

	/**
	 * returns the fact database used to process a program
	 */
	public IFactDb getFacts()
	{
		return this.facts;
	}

	/**
	 * sets the statement map used to resolve queries
	 */
	public void setStatements( IStatementMap sm )
	{
		this.statements = sm;
	}

	/**
	 * returns the statement map used to resolve queries
	 */
	public IStatementMap getStatements()
	{
		return this.statements;
	}

	/**
	 * generates xml describing the datamodel when in memory, e.g.
	 * the statementtrees...
	 */
	public void genXml( Writer out )
		throws IOException
	{
		out.write( "<?xml version=\"1.0\"?>" );
		out.write( "<mprolog>" );
		symbolTable.genXml( out );
		getStatements().genXml(out, symbolTable);
		getFacts().genXml(out,symbolTable);
		getQuerys().genXml( out,symbolTable );
		out.write( "</mprolog>" );
	}

	/**
	 * logs a final result found by a query in prolog syntax
	 * @param format
	 * @param results
	 */
	protected void logFinalResult( IRelation format, Iterator results )
	{
		// ??? DESIGN HACK ???
		// build format array filled with the parameter names
		int valueCount = format.getValueCount();
		int paramNames[] = new int[ valueCount ];
		Iterator formatI = format.getValues();
		int counter = 0;
		while( formatI.hasNext() )
			paramNames[ counter++ ] = ((IAtom)formatI.next()).getName();

		// log the results
		while( results.hasNext() )
			logger.msg( ((IRelation)results.next()).toString(paramNames, symbolTable) );
	}

	/**
	 * the method processing the program
	 * @throws ParameterMatchingError
	 */
	public void run()
		throws ParameterMatchingError
	{
		logger.msg( "running...");

		try
		{
			Iterator i=querys.getQueries();
			while( i.hasNext() )
			{
				IFact action= (IFact) i.next();
				IFactList matching = null;
				IStatement s = statements.getMatchingStatement( action );
				logger.msg( action.toString( symbolTable ) );
				if( s != null )
				{
					IFactList results = s.query( action, facts, statements );
					logFinalResult( results.getRelation(), results.getRelations() );
					//i.remove();
				}
				else if( (matching=facts.getFactList( action.getName() )) != null )
				{
					IFactList results = matching.getMatchings( action );
					logFinalResult( results.getRelation(), results.getRelations() );
					//i.remove();
				}
				else
					throw new ParameterMatchingError(
						"Symbol for " + action.toString() + " not found", null );
			}
		}
		catch( ParameterMatchingError err )
		{
			err.printStackTrace();
		}
	}
}
