/*****************************
**********************************************
*  The following java code is based off of the mxp library which
*  was written in c++ by Thomas Mecir and as such follows the
*  same license.
 *   Copyright (C) 2010 by slothmud.org                                     *
 *   splork@slothmud.org                     
 *  If you use this code, could you please add the following link
 * somewhere on your projects page
 *  <a href="http://www.slothmud.org">Slothmud - a multiplayer free online rpg game</a>         *
 *   His library can be found at http://www.kmuddy.com/libmxp/                                                                       *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU Library General Public License as       *
 *   published by the Free Software Foundation; either version 2 of the    *
 *   License, or (at your option) any later version.                       *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU Library General Public License for more details.                  *
 ***************************************************************************/

package com.jmxp;

import java.util.HashMap;

public class EntityManager 
{
	/** empty string, to speed up some things a little bit */
	private String empty_string;
	/** partial entity */
	private String partent;
	/** are we in an entity? */
	private boolean inEntity;
	private HashMap<String, String> entities = new HashMap<String, String>();
	
	private String ENTITY_NAMES[] = { 
			  "Aacute",
			  "aacute",
			  "Acirc",
			  "acirc",
			  "acute",
			  "AElig",
			  "aelig",
			  "Agrave",
			  "agrave",
			  "amp",
			  "apos",
			  "Aring",
			  "aring",
			  "Atilde",
			  "atilde",
			  "Auml",
			  "auml",
			  "brvbar",
			  "Ccedil",
			  "ccedil",
			  "cedil",
			  "cent",
			  "copy",
			  "curren",
			  "deg",
			  "divide",
			  "Eacute",
			  "eacute",
			  "Ecirc",
			  "ecirc",
			  "Egrave",
			  "egrave",
			  "ETH",
			  "eth",
			  "Euml",
			  "euml",
			  "frac12",
			  "frac14",
			  "frac34",
			  "gt",
			  "Iacute",
			  "iacute",
			  "Icirc",
			  "icirc",
			  "iexcl",
			  "Igrave",
			  "igrave",
			  "iquest",
			  "Iuml",
			  "iuml",
			  "laquo",
			  "lt",
			  "macr",
			  "micro",
			  "middot",
			  "nbsp",
			  "not",
			  "Ntilde",
			  "ntilde",
			  "Oacute",
			  "oacute",
			  "Ocirc",
			  "ocirc",
			  "Ograve",
			  "ograve",
			  "ordf",
			  "ordm",
			  "Oslash",
			  "oslash",
			  "Otilde",
			  "otilde",
			  "Ouml",
			  "ouml",
			  "para",
			  "plusmn",
			  "pound",
			  "quot",
			  "raquo",
			  "reg",
			  "sect",
			  "shy",
			  "sup1",
			  "sup2",
			  "sup3",
			  "szlig",
			  "THORN",
			  "thorn",
			  "times",
			  "Uacute",
			  "uacute",
			  "Ucirc",
			  "ucirc",
			  "Ugrave",
			  "ugrave",
			  "uml",
			  "Uuml",
			  "uuml",
			  "Yacute",
			  "yacute",
			  "yen",
			};

		private	int ENTITY_DEF[] = {
			  193,
			  225,
			  194,
			  226,
			  180,
			  198,
			  230,
			  192,
			  224,
			  38,
			  39,
			  197,
			  229,
			  195,
			  227,
			  196,
			  228,
			  166,
			  199,
			  231,
			  184,
			  162,
			  169,
			  164,
			  176,
			  247,
			  201,
			  233,
			  202,
			  234,
			  200,
			  232,
			  208,
			  240,
			  203,
			  235,
			  189,
			  188,
			  190,
			  62,
			  205,
			  237,
			  206,
			  238,
			  161,
			  204,
			  236,
			  191,
			  207,
			  239,
			  171,
			  60,
			  175,
			  181,
			  183,
			  160,
			  172,
			  209,
			  241,
			  211,
			  243,
			  212,
			  244,
			  210,
			  242,
			  170,
			  186,
			  216,
			  248,
			  213,
			  245,
			  214,
			  246,
			  182,
			  177,
			  163,
			  34,
			  187,
			  174,
			  167,
			  173,
			  185,
			  178,
			  179,
			  223,
			  222,
			  254,
			  215,
			  218,
			  250,
			  219,
			  251,
			  217,
			  249,
			  168,
			  220,
			  252,
			  221,
			  253,
			  165,
			};
	
	private static final int NUM_MXP_ENTITIES = 100;

	
	public EntityManager(boolean noStdEntities)
	{
		reset(noStdEntities);
	}

	//~cEntityManager ();

	/** 
	* add or update entity 
	*/
	public void addEntity (String name, String value)
	{
		if (name.equals("")) return;		
		//add or modify the entity
		entities.put(name, value);		
	}
	
	/** 
	 * delete entity 
	 */
	public void deleteEntity (String name)
	{
		entities.remove(name);	
	}
	
	public String entity(String name)
	{
		String result = entities.get(name);
		if ( result == null )
		{
			result = "";
		}
		return result;
	}
	
	public boolean exists(String name) 
	{ 
		return entities.containsKey(name); 
	};
	
	/** 
	 * expand entities in a string 
	 */
	public String expandEntities(String s, boolean finished)
	{
		  String s1 = "";
		  if (!partent.equals(""))  //some unfinished entity is waiting...
		  {
			  inEntity = true;
		  }

		  for (int i = 0; i < s.length(); i++ )
		  {
			  char ch = s.charAt(i);
		    if (inEntity)
		    {
		      
		      if (ch == ';')  //end of entity
		      {
		        inEntity = false;
		        if (partent.equals("")) //received &;
		        {
		          s1 += "&;";
		        }
		        else
		        if (partent.charAt(0) == '_')  //invalid entity name - IGNORED
		        {
		          partent = "";
		        }
		        else
		        if (partent.charAt(0) == '#')  //&#nnn; entity
		        {
		          //compute number
		          int n = 0;
		          //starting from second character
		          for (int j = 1; j < partent.length(); j++)
		          {
		            int x = partent.charAt(j) - 48;
		            if ((x < 0) || (x > 9)) //WRONG
		            {
		              n = 0;
		              break;
		            }
		            n = n * 10 + x;
		            if (n > 255)  //number too big!
		            {
		              n = 0;
		              break;
		            }
		          }
		          //verify number, IGNORE entity if it's wrong
		          if ((n >= 32) && (n <= 255))
		          {
		            s1 += (char) n;
		          }
		          partent = "";
		        }
		        else
		        {
		          //now we have correct entity name, let's expand it, if possible :)
		          if (entities.containsKey(partent))
		            s1 += entities.get(partent);
		          else
		            //keep the same string if the entity doesn't exist...
		            s1 += "&" + partent + ";";
		          partent = "";
		        }
		      }
		      else if (ch == '&')
		      //unterminated entity, new entity may start here
		      {
		        s1 += "&" + partent;
		        partent = "";
		        //isEntity remains set
		      }
		      else if ((partent.equals("") && correct1(ch)) || 
		    		  ((!partent.equals("")) && correctN(ch)))
		      {
		        partent += ch;
		      }
		      //this wasn't an entity after all
		      else
		      {
		        inEntity = false;
		        s1 += "&" + partent + ch;
		        partent = "";
		      }
		    }
		    else
		    {
		      if (ch == '&')
		        inEntity = true;
		      else
		        //copy without change
		        s1 += ch;
		    }
		  }
		  //string ends in an unterminated entity, but only if the string is finished
		  if (inEntity && finished)
		  {
		    s1 += "&" + partent;
		    partent = "";
		    inEntity = false;
		  }

		  //return the resulting string
		  return s1;
		
	}
	
	public boolean needMoreText()
	{
		return !partent.equals("");		
	}
	  
	void reset(boolean noStdEntities)
	{
		  partent = "";
		  entities.clear();
		  inEntity = false;
		  
		  if (noStdEntities)
		  {
		    return;
		  }
		  
		  
		  //restore standard HTML entities
		  for (int i = 0; i < NUM_MXP_ENTITIES; i++)
		  {
			  String s = "";
			  s += (char)ENTITY_DEF[i];
			  entities.put(ENTITY_NAMES[i], s);
		  }
	}
	
	//can this be the first letter of an entity?
	private boolean correct1 (char l)
	{
	  return (((l >= 'a') && (l <= 'z')) || ((l >= 'A') && (l <= 'Z')) || (l == '#'));
	}

	//can this be a letter of entity?
	private boolean correctN (char l)
	{
	  return (((l >= 'a') && (l <= 'z')) || ((l >= 'A') && (l <= 'Z')) || (l == '_') ||
	      ((l >= '0') && (l <= '9')));
	}
	
}