package org.greenstone.atlas.server;


import java.io.Serializable;
import java.util.HashMap;

public class GazetteerTrieNode implements Serializable
{
	private static final long serialVersionUID = 8121262849365540679L;
	protected boolean _nameEnd = false;
	protected GazetteerTrieNode[] _children = new GazetteerTrieNode[26];
	protected HashMap<Character,GazetteerTrieNode> _unicodeCharacters = null;
	
	/**
	 * Basic constructor for a non-unicode trie node
	 * @param nameEnd defines whether the new node represents the end of a place name
	 */
	protected GazetteerTrieNode(boolean nameEnd)
	{
		for(int i = 0; i < 26; i++)
		{
			_children[i] = null;
		}
		_nameEnd = nameEnd;
	}
	
	/**
	 * Add a child to the current node
	 * @param c is the character used to decide where to add the child node
	 * @param nameEnd is whether or not this character represents the end of a place name
	 * @return true if a child was added and false if one already existed
	 */
	protected boolean addChild(char c, boolean nameEnd)
	{
		//If the characters is an ASCII letter
		int index;
		if(c >= 'A' && c <= 'Z')
		{
			index = c - 65;
		}
		else if(c >= 'a' && c <= 'z')
		{
			index = c - 97; 
		}
		//If the character is not an ASCII letter
		else
		{		
			//If the node does not currently have a unicode character map then make one
			if(_unicodeCharacters == null)
			{
				_unicodeCharacters = new HashMap<Character, GazetteerTrieNode>();
				_unicodeCharacters.put(c, new GazetteerTrieNode(nameEnd));
				return true;
			}
			
			//Add the unicode character to the map only if it either does not exist already or signifies the end of a place name
			GazetteerTrieNode value;
			if((value = _unicodeCharacters.get(c)) != null)
			{
				if(value.isNameEnd())
				{
					return false;
				}
				else
				{
					value.setNameEnd(nameEnd);
				}
			}
			else
			{
				_unicodeCharacters.put(c, new GazetteerTrieNode(nameEnd));
			}
			
			return true;
		}
		
		//Add the ascii character if it either does not already exist or it signifies the end of a place name
		if(_children[index] != null)
		{
			if(_children[index].isNameEnd() || nameEnd == false)
			{
				return false;
			}
			else
			{
				_children[index].setNameEnd(true);
				return true;
			}
		}
		else
		{
			_children[index] = new GazetteerTrieNode(nameEnd);
			return true;
		}
	}
	
	/**
	 * @return whether or not this node represents the end of a place name
	 */
	protected boolean isNameEnd()
	{
		return _nameEnd;
	}
	
	/**
	 * Set whether or not this node represents the end of a place name
	 * @param nameEnd is the value to set it to
	 */
	protected void setNameEnd(boolean nameEnd)
	{
		_nameEnd = nameEnd;
	}
	
	/**
	 * Returns the child at the given index
	 * @param index is the index of the child to return
	 * @return the child at the given index
	 */
	protected GazetteerTrieNode getChild(char c)
	{
		int index = -1;
		if(c >= 'A' && c <= 'Z')
		{
			index = c - 65;
		}
		else if(c >= 'a' && c <= 'z')
		{
			index = c - 97; 
		}
		if(index == -1)
		{
			if(_unicodeCharacters == null)
			{
				return null;
			}
			return _unicodeCharacters.get(c);
		}
		return _children[index];
	}
	
	public void output()
	{
		if(_unicodeCharacters != null && _unicodeCharacters.size() >= 10)
		{
			System.out.println(_unicodeCharacters.size());
		}
		
		for(GazetteerTrieNode g : _children)
		{
			if(g != null)
			{
				g.output();
			}
		}
	}
}
