/*
 * Copyright (C) 2007-2012 Xagasoft, All rights reserved.
 *
 * This file is part of the libgats library and is released under the
 * terms of the license contained in the file LICENSE.
 */

package com.xagasoft.gats;

import java.io.InputStream;
import java.io.OutputStream;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.Hashtable;

/**
 * Gats dictionary, or hash table.  This stores any number of items, keyed with
 * strings.  The values can be any valid com.xagasoft.gats.GatsObject descendant
 * object.  This class is often used as the root of complex Gats structures.
 * <p>
 * This class implements all standard java Map interface features but contains
 * additional helper functions to make inserting and extracting values from the
 * dictionary much easier, please see the extra get / put functions for more
 * information.
 * <p>
 * Keys are stored as GatsStrings in the end, but are plain Java strings until
 * encoding occurs to make use easier.  This means that using extended
 * characters in the strings may be dangerous.  In a future version, the keys
 * may have to be encoded in UTF-8 which will solve this problem.
 */
public class GatsDictionary extends GatsObject implements Map<String,GatsObject>
{
	private Hashtable<String, GatsObject> hValue =
		new Hashtable<String, GatsObject>();

	public GatsDictionary()
	{
	}

	public int getType()
	{
		return GatsObject.DICTIONARY;
	}

	void read( InputStream is, char cType ) throws java.io.IOException
	{
		for(;;)
		{
			GatsObject objKey = GatsObject.read( is );
			if( objKey == null )
				break;
			if( objKey.getType() != GatsObject.STRING )
				throw new java.io.IOException("bleh");
			put( objKey.toString(), GatsObject.read( is ) );
		}
	}

	void write( OutputStream os ) throws java.io.IOException
	{
		os.write( (int)'d' );
		for( String sKey : hValue.keySet() )
		{
			new GatsString( sKey ).write( os );
			hValue.get( sKey ).write( os );
		}
		os.write( (int)'e' );
	}

	public String toString()
	{
		return hValue.toString();
	}

	public void clear() 
	{
		hValue.clear();
	}

	public boolean containsKey( Object key )
	{
		return hValue.containsKey( key );
	}

	public boolean containsValue( Object value )
	{
		return hValue.containsValue( value );
	}

	public Set<java.util.Map.Entry<String, GatsObject>> entrySet()
	{
		return hValue.entrySet();
	}

	public GatsObject get( Object key )
	{
		return hValue.get( key );
	}

	public boolean isEmpty()
	{
		return hValue.isEmpty();
	}

	public Set<String> keySet()
	{
		return hValue.keySet();
	}

	public GatsObject put( String key, GatsObject value )
	{
		return hValue.put( key, value );
	}

	/**
	 * Helper function that inserts a new com.xagasoft.gats.GatsInteger with
	 * the value val.
	 */
	public GatsObject put( String key, long val )
	{
		return hValue.put( key, new GatsInteger( val ) );
	}

	/**
	 * Helper function that inserts a new com.xagasoft.gats.GatsFloat with the
	 * value val.
	 */
	public GatsObject put( String key, double val )
	{
		return hValue.put( key, new GatsFloat( val ) );
	}

	/**
	 * Helper function that inserts a new com.xagasoft.gats.GatsBoolean with the
	 * value val.
	 */
	public GatsObject put( String key, boolean val )
	{
		return hValue.put( key, new GatsBoolean( val ) );
	}

	/**
	 * Helper function that inserts a new com.xagasoft.gats.GatsString with the
	 * value val.
	 */
	public GatsObject put( String key, String val )
	{
		return hValue.put( key, new GatsString( val ) );
	}

	/**
	 * Helper function that inserts a new com.xagasoft.gats.GatsString with the
	 * value val.
	 */
	public GatsObject put( String key, byte val[] )
	{
		return hValue.put( key, new GatsString( val ) );
	}

	/**
	 * Helper function that gets the specified GatsObject, casts it to a
	 * com.xagasoft.gats.GatsInteger and extracts the value from it.  If the
	 * key specified does not appear in the GatsDictionary or is not the correct
	 * type you will get the expected exception.
	 */
	public long getInt( String key ) throws KeyNotFoundException
	{
		GatsInteger ret = (GatsInteger)hValue.get( key );
		if( ret == null )
			throw new KeyNotFoundException( this, key );
		return ret.getValue();
	}

	/**
	 * Helper function that gets the specified GatsObject, casts it to a
	 * com.xagasoft.gats.GatsFloat and extracts the value from it.  If the
	 * key specified does not appear in the GatsDictionary or is not the correct
	 * type you will get the expected exception.
	 */
	public double getFloat( String key ) throws KeyNotFoundException
	{
		GatsFloat ret = (GatsFloat)hValue.get( key );
		if( ret == null )
			throw new KeyNotFoundException( this, key );
		return ret.getValue();
	}

	/**
	 * Helper function that gets the specified GatsObject, casts it to a
	 * com.xagasoft.gats.GatsBool and extracts the value from it.  If the
	 * key specified does not appear in the GatsDictionary or is not the correct
	 * type you will get the expected exception.
	 */
	public boolean getBool( String key ) throws KeyNotFoundException
	{
		GatsBoolean ret = (GatsBoolean)hValue.get( key );
		if( ret == null )
			throw new KeyNotFoundException( this, key );
		return ret.getValue();
	}

	/**
	 * Helper function that gets the specified GatsObject, casts it to a
	 * com.xagasoft.gats.GatsString and extracts the value from it.  If the
	 * key specified does not appear in the GatsDictionary or is not the correct
	 * type you will get the expected exception.
	 */
	public byte[] getString( String key ) throws KeyNotFoundException
	{
		GatsString ret = (GatsString)hValue.get( key );
		if( ret == null )
			throw new KeyNotFoundException( this, key );
		return ret.getValue();
	}

	/**
	 * Helper function that gets the specified GatsObject, casts it to a
	 * com.xagasoft.gats.GatsDictionary and returns it.  If the
	 * key specified does not appear in the GatsDictionary or is not the correct
	 * type you will get the expected exception.
	 */
	public GatsDictionary getDict( String key ) throws KeyNotFoundException
	{
		GatsDictionary ret = (GatsDictionary)hValue.get( key );
		if( ret == null )
			throw new KeyNotFoundException( this, key );
		return ret;
	}

	/**
	 * Helper function that gets the specified GatsObject, casts it to a
	 * com.xagasoft.gats.GatsList and returns it.  If the
	 * key specified does not appear in the GatsDictionary or is not the correct
	 * type you will get the expected exception.
	 */
	public GatsList getList( String key ) throws KeyNotFoundException
	{
		GatsList ret = (GatsList)hValue.get( key );
		if( ret == null )
			throw new KeyNotFoundException( this, key );
		return ret;
	}

	public void putAll( Map<? extends String, ? extends GatsObject> src )
	{
		hValue.putAll( src );
	}

	public GatsObject remove( Object key )
	{
		return hValue.remove( key );
	}

	public int size()
	{
		return hValue.size();
	}

	public Collection<GatsObject> values()
	{
		return hValue.values();
	}
};