/*
 * The contents of this file are subject to the Mozilla Public License Version 1.1
 * (the "License"); you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at <http://www.mozilla.org/MPL/>.
 * 
 * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT
 * WARRANTY OF ANY KIND, either express or implied. See the License for the specific
 * language governing rights and limitations under the License.
 * 
 * The Original Code is the Venice Web Communities System.
 * 
 * The Initial Developer of the Original Code is Eric J. Bowersox <erbo@ricochet.com>,
 * for Silverwrist Design Studios.  Portions created by Eric J. Bowersox are
 * Copyright (C) 2004 Eric J. Bowersox/Silverwrist Design Studios.  All Rights Reserved.
 * 
 * Contributor(s): 
 */
package com.silverwrist.venice.ui.dlg;

import java.io.IOException;
import java.lang.reflect.*;
import java.util.*;
import org.w3c.dom.*;
import com.silverwrist.util.*;
import com.silverwrist.venice.except.*;
import com.silverwrist.venice.ui.*;
import com.silverwrist.venice.util.XMLLoader;

public class EnumPickListField extends PickListField
{
  /*--------------------------------------------------------------------------------
   * Internal class for translating between strings and enums
   *--------------------------------------------------------------------------------
   */

  static class EnumExtractor
  {
    /*====================================================================
     * Attributes
     *====================================================================
     */

    private Class m_klass;
    private Method m_getname;
    private Map m_valmap;
    private List m_list;

    /*====================================================================
     * Constructor
     *====================================================================
     */

    EnumExtractor(String klassname)
    {
      try
      { // load the class
	m_klass = Class.forName(klassname);

	// get the "getName" method
	m_getname = m_klass.getMethod("getName",NO_PARAMS);

	// get the "getEnumMap" method and call it to get the value map
	Method m = m_klass.getMethod("getEnumMap",NO_PARAMS);
	m_valmap = (Map)(m.invoke(null,NO_ARGS));

	// get the "getEnumList" method and call it to get the value list
	m = m_klass.getMethod("getEnumList",NO_PARAMS);
	m_list = (List)(m.invoke(null,NO_ARGS));
	
      } // end try
      catch (ClassNotFoundException e)
      { // bounce me
	throw new VeniceRuntimeException("class not found: " + klassname,e);

      } // end catch
      catch (NoSuchMethodException e)
      { // bounce me
	throw new VeniceRuntimeException("method failure",e);

      } // end catch
      catch (IllegalAccessException e)
      { // bounce me
	throw new VeniceRuntimeException("illegal access",e);

      } // end catch
      catch (IllegalArgumentException e)
      { // bounce me
	throw new VeniceRuntimeException("illegal argument",e);

      } // end catch
      catch (InvocationTargetException e)
      { // an exception got thrown
	throw new VeniceRuntimeException("target threw something",e);

      } // end catch
	
    } // end constructor

    /*====================================================================
     * External operations
     *====================================================================
     */

    final boolean isInstance(Object o)
    {
      return m_klass.isInstance(o);

    } // end isInstance

    final String getNameFromEnum(Object enum)
    {
      try
      { // invoke the method to return the name
	return (String)(m_getname.invoke(enum,NO_ARGS));

      } // end try
      catch (IllegalAccessException e)
      { // bounce me
	throw new VeniceRuntimeException("illegal access",e);

      } // end catch
      catch (IllegalArgumentException e)
      { // bounce me
	throw new VeniceRuntimeException("illegal argument",e);

      } // end catch
      catch (InvocationTargetException e)
      { // an exception got thrown
	throw new VeniceRuntimeException("target threw something",e);

      } // end catch

    } // end getNameFromEnum
    
    final Object getEnumFromName(String name)
    {
      return m_valmap.get(name);

    } // end getEnumFromName

  } // end class EnumExtractor

  /*--------------------------------------------------------------------------------
   * Internal class holding choices
   *--------------------------------------------------------------------------------
   */

  static class PickListChoice
  {
    /*====================================================================
     * Attributes
     *====================================================================
     */

    private String m_name;
    private String m_value;

    /*====================================================================
     * Constructor
     *====================================================================
     */

    PickListChoice(String name, String value)
    {
      m_name = name;
      m_value = value;

    } // end constructor

    /*====================================================================
     * External operations
     *====================================================================
     */

    final String getName()
    {
      return m_name;

    } // end getName

    final String getValue()
    {
      return m_value;

    } // end getValue

  } // end class PickListChoice

  /*--------------------------------------------------------------------------------
   * Static data members
   *--------------------------------------------------------------------------------
   */

  public static final String TAGNAME = "enumlist";

  private static final Class[] NO_PARAMS = new Class[0];
  private static final Object[] NO_ARGS = new Object[0];

  /*--------------------------------------------------------------------------------
   * Attributes
   *--------------------------------------------------------------------------------
   */

  private EnumExtractor m_extractor;

  /*--------------------------------------------------------------------------------
   * Constructors
   *--------------------------------------------------------------------------------
   */

  public EnumPickListField(String name, String caption, String caption2, boolean required, String classname,
			   String clist)
  {
    super(name,caption,caption2,required,Collections.EMPTY_LIST);
    setChoicesList(createChoiceListFromString(classname,clist));

  } // end constructor

  public EnumPickListField(Element elt) throws ConfigException
  {
    super(elt,Collections.EMPTY_LIST);
    setChoicesList(createChoiceListFromXML(elt));

  } // end constructor

  protected EnumPickListField(EnumPickListField other)
  {
    super(other);
    m_extractor = other.m_extractor;

  } // end constructor

  /*--------------------------------------------------------------------------------
   * Internal operations
   *--------------------------------------------------------------------------------
   */

  private final List createChoiceListFromString(String classname, String s)
  {
    m_extractor = new EnumExtractor(classname);
    char separator = s.charAt(0);
    String work = s.substring(1);
    ArrayList tmp = new ArrayList();
    while (work!=null)
    { // split up the "choice list" string into name/value pairs
      int p = work.indexOf(separator);
      if (p<0)
	break;
      String n1 = work.substring(0,p);
      work = work.substring(p+1);
      p = work.indexOf(separator);
      String v1;
      if (p<0)
      { // take the rest of the string
	v1 = work;
	work = null;

      } // end if
      else
      { // chop the string off the end
	v1 = work.substring(0,p);
	work = work.substring(p+1);
	if (work.length()==0)
	  work = null;

      } // end else

      if (m_extractor.getEnumFromName(n1)!=null)
	tmp.add(new PickListChoice(n1,v1));

    } // end while

    tmp.trimToSize();
    if (tmp.isEmpty())
      return Collections.EMPTY_LIST;
    else
      return Collections.unmodifiableList(tmp);

  } // end createChoiceListFromString

  private final List createChoiceListFromXML(Element elt) throws ConfigException
  {
    XMLLoader loader = XMLLoader.get();
    m_extractor = new EnumExtractor(loader.configGetAttribute(elt,"enumClass"));
    NodeList nl = elt.getChildNodes();
    ArrayList tmp = new ArrayList();
    for (int i=0; i<nl.getLength(); i++)
    { // look for associated elements
      Node n = nl.item(i);
      if (n.getNodeType()==Node.ELEMENT_NODE)
      { // make sure we have a <choice/> element here
	loader.configVerifyNodeName(n,"choice",elt);
	String n1 = loader.configGetAttribute((Element)n,"id");
	String v1 = loader.configGetText((Element)n);
	if (m_extractor.getEnumFromName(n1)!=null)
	  tmp.add(new PickListChoice(n1,v1));

      } // end if
      // else just ignore that

    } // end for

    tmp.trimToSize();
    if (tmp.isEmpty())
      return Collections.EMPTY_LIST;
    else
      return Collections.unmodifiableList(tmp);

  } // end createChoiceListFromXML

  /*--------------------------------------------------------------------------------
   * Overrides from class PickListField
   *--------------------------------------------------------------------------------
   */

  protected String getChoiceName(Object o)
  {
    return ((PickListChoice)o).getName();

  } // end getChoiceName

  protected String getChoiceValue(Object o)
  {
    return ((PickListChoice)o).getValue();

  } // end getChoiceValue

  /*--------------------------------------------------------------------------------
   * Implementations from interface DialogField
   *--------------------------------------------------------------------------------
   */

  public Object getValue()
  {
    Object o = super.getValue();
    if (o==null)
      return null;
    return m_extractor.getEnumFromName(o.toString());

  } // end getValue

  public void setValue(Object o)
  {
    if (o==null)
    { // null value
      super.setValue(null);
      return;

    } // end if

    if (!(m_extractor.isInstance(o)))
      throw new IllegalArgumentException("not a valid enum instance");
    super.setValue(m_extractor.getNameFromEnum(o));

  } // end setValue

  public DialogField duplicate()
  {
    return new EnumPickListField(this);

  } // end duplicate

} // end class EnumPickListField
