package prolog.implementation;

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

/**
 * <p>The symboltable is created by the scanner and parser during parsing.
 * All identifiers and strings of the sourceprogram are replaced by a number.
 * In the symboltable, the relation between the number and the original
 * string is stored</p>
 */
public class SymbolTable
	implements
		ISymbolTable
{
	/**
	 * a instance counter for hacked debug purpose
	 */
	static int instanceCount = 0;

	/**
	 * a list storing all words with their index found in program
	 */
	protected List words = new ArrayList(10);

	/**
	 * a map storing all words found in program
	 * the key is the word, the index in the word list the value
	 */
	protected Map wordMap = new HashMap(10);

	/**
	 * create a symboltable
	 */
	public SymbolTable()
	{
		instanceCount++;
		if( instanceCount > 1 )
			throw new RuntimeException( "more than one symboltable" );
	}

	/**
	 * clears the symboltyble
	 */
	public void clear()
	{
		words.clear();
		wordMap.clear();
	}

	/**
	 * encodes the given word
	 * @param word
	 * @return
	 */
	public int encode( String word )
	{
		if( word == null )
			throw new RuntimeException(
				"INTERNAL MACHINE ERROR: SymbolTable.encode failed! " +
				" Can not encode a null string!" );
		if( word.length() == 0 )
			throw new RuntimeException(
				"INTERNAL MACHINE ERROR: SymbolTable.encode failed! " +
				" Can not encode a string with length=0!" );

		Integer index = null;
		if( (index=(Integer)wordMap.get( word )) == null )
		{
			index = new Integer( words.size() );
			wordMap.put( word, index );
			words.add( word );
		}

		return index.intValue();
	}

	/**
	 * decodes the given word
	 */
	public String decode( IAtom index )
	{
		return decode( index.getName() );
	}

	/**
	 * decodes the given word
	 */
	public String decode( int index )
	{
		return ((String)words.get( index ));
	}

	/**
	 * decodes the given word
	 */
	public String decode( Object obj )
	{
		if( obj instanceof IAtom )
			return decode( ((IAtom)obj).getName() );
		else
			throw new RuntimeException( "unknown type" );
	}

	/**
	 * returns the index of the given word in the symboltable
	 * @return
	 */
	private int getIndex( String word )
	{
		int back = -1;
		int count = 0;
		Iterator i = words.iterator();
		while( i.hasNext() )
		{
			if( i.next().equals( word ) )
			{
				back = count;
				break;
			}
			count++;
		}
		return back;
	}

	/**
	 * generates a xml representation of this symboltable
	 * @throws IOException
	 */
	public void genXml( Writer out )
		throws IOException
	{
		out.write( "<symboltable>" );
		int count = words.size();
		for( int i=0;i<count;i++ )
		{
			out.write( "<item key=\"" );
			out.write( Integer.toString( i ) );
			out.write( "\" value=\"" );
			out.write( decode( i ) );
			out.write( "\"/>" );
		}
		out.write( "</symboltable>" );
	}

	/**
	 * returns a string representation of this symboltable
	 */
	public String toString()
	{
		StringBuffer out = new StringBuffer();
		out.append( "symboltable:\n" );
		int count = words.size();
		for( int i=0;i<count;i++ )
		{
			out.append( '\t' );
			out.append( i );
			out.append( '\t' );
			out.append( decode( i ) );
			out.append( '\n' );
		}
		return out.toString();
	}
}
