//-------------------------------------------------------------------------------------------------
// Module    : $RCSfile: AbstractDefaultGUIElementFactory.java,v $
// Version   : $Revision: 0.0 $  $Author: patrick $  $Date: 13.05.2012 $
// Project   : GUIModel
// Creation  : 2012 by patrick
// Copyright : Patrick Harms, 2012
//-------------------------------------------------------------------------------------------------
package de.ugoe.cs.quest.eventcore.guimodel;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

//-------------------------------------------------------------------------------------------------
/**
 * TODO comment
 * 
 * @version $Revision: $ $Date: 13.05.2012$
 * @author 2012, last modified by $Author: patrick$
 */
//-------------------------------------------------------------------------------------------------
public class AbstractDefaultGUIElementFactory
{
  /** */
  private Properties mMappingsFromConfiguration;

  /** */
  private Map<String, List<GUIElement>> mGuiElementCache = new HashMap<String, List<GUIElement>>();
  
  //-----------------------------------------------------------------------------------------------
  /**
   * TODO: comment
   *
   * @param key
   * @return
   */
  //-----------------------------------------------------------------------------------------------
  protected List<GUIElement> getGUIElementCandidates(String key)
  {
    return mGuiElementCache.get(key);
  }

  //-----------------------------------------------------------------------------------------------
  /**
   * TODO: comment
   *
   * @param retVal
   */
  //-----------------------------------------------------------------------------------------------
  protected void addGUIElementToCache(String key, GUIElement guiElement)
  {
    List<GUIElement> candidates = mGuiElementCache.get(key);
    
    if (candidates == null)
    {
      candidates = new ArrayList<GUIElement>();
      mGuiElementCache.put(key, candidates);
    }
    
    candidates.add(guiElement);
  }

  //-----------------------------------------------------------------------------------------------
  /**
   * TODO: comment
   *
   * @param icon
   * @param icon2
   * @return
   */
  //-----------------------------------------------------------------------------------------------
  protected boolean equals(Object object1, Object object2)
  {
    if (object1 == object2)
    {
      return true;
    }
    else if (object1 != null)
    {
      return object1.equals(object2);
    }
    else
    {
      return object2 == null;
    }
  }

  //-----------------------------------------------------------------------------------------------
  /**
   * TODO: comment
   *
   * @param name
   * @return
   */
  //-----------------------------------------------------------------------------------------------
  protected String getKey(String name)
  {
    if (name == null)
    {
      return "noName";
    }
    else
    {
      return name;
    }
  }

  //-----------------------------------------------------------------------------------------------
  /**
   * TODO: comment
   *
   * @param parameterTypes
   * @param parameters
   * @return
   * @throws GUIModelConfigurationException 
   */
  //-----------------------------------------------------------------------------------------------
  protected GUIElement instantiateGUIElementFromConfiguredMappings(String    type,
                                                                   Object... parameters)
    throws GUIModelConfigurationException
  {
    Properties mappings = getMappingsFromConfiguration();
    
    String className = mappings.getProperty(type);
    if (className != null)
    {
      try
      {
        Class<?> clazz = this.getClass().getClassLoader().loadClass(className);
        
        if (!GUIElement.class.isAssignableFrom(clazz))
        {
          Logger.getLogger(this.getClass().getName()).warning
            ("configured GUI element representing class " + className + " is no valid GUIElement " +
             "derivate.");
          
          return null;
        }
        
        Class<?>[] parameterTypes = new Class<?>[parameters.length];
        
        for (int i = 0; i < parameters.length; i++)
        {
          parameterTypes[i] = parameters[i].getClass();
          
          if (Boolean.class.equals(parameterTypes[i]))
          {
            parameterTypes[i] = boolean.class;
          }
          else if (Integer.class.equals(parameterTypes[i]))
          {
            parameterTypes[i] = int.class;
          }
          else if (Double.class.equals(parameterTypes[i]))
          {
            parameterTypes[i] = double.class;
          }
          else if (Character.class.equals(parameterTypes[i]))
          {
            parameterTypes[i] = char.class;
          }
          else if (Byte.class.equals(parameterTypes[i]))
          {
            parameterTypes[i] = byte.class;
          }
          else if (Float.class.equals(parameterTypes[i]))
          {
            parameterTypes[i] = float.class;
          }
          else if (Long.class.equals(parameterTypes[i]))
          {
            parameterTypes[i] = long.class;
          }
          else if (Short.class.equals(parameterTypes[i]))
          {
            parameterTypes[i] = short.class;
          }
        }
        
        GUIElement guiElement =
          (GUIElement) clazz.getConstructor(parameterTypes).newInstance(parameters);
        
        if (guiElement instanceof AbstractDefaultGUIElement)
        {
          ((AbstractDefaultGUIElement) guiElement).setOriginalTypeInfo(type);
        }
        
        return guiElement;
      }
      catch (ClassNotFoundException e)
      {
        Logger.getLogger(this.getClass().getName()).warning
          ("configured GUI element representing class " + className + " can not be loaded.");
        throw new GUIModelConfigurationException
          ("configured GUI element representing class " + className + " can not be loaded.", e);
      }
      catch (SecurityException e)
      {
        Logger.getLogger(this.getClass().getName()).log
          (Level.WARNING, "configured GUI element representing class " + className + " can not " +
           "be instantiated due to security reasons.", e);
        throw new GUIModelConfigurationException
          ("configured GUI element representing class " + className + " can not " +
           "be instantiated due to security reasons.", e);
      }
      catch (NoSuchMethodException e)
      {
        Logger.getLogger(this.getClass().getName()).warning
          ("configured GUI element representing class " + className + " does not provide an " +
           "appropriate constructur.");
        throw new GUIModelConfigurationException
          ("configured GUI element representing class " + className + " does not provide an " +
           "appropriate constructur.", e);
      }
      catch (IllegalArgumentException e)
      {
        Logger.getLogger(this.getClass().getName()).warning
          ("configured GUI element representing class " + className + " does not provide an " +
           "appropriate constructur accepting the provided parameters.");
        throw new GUIModelConfigurationException
          ("configured GUI element representing class " + className + " does not provide an " +
           "appropriate constructur accepting the provided parameters.", e);
      }
      catch (InstantiationException e)
      {
        Logger.getLogger(this.getClass().getName()).log
          (Level.WARNING, "configured GUI element representing class " + className + " can not " +
           "be instantiated.", e);
        throw new GUIModelConfigurationException
          ("configured GUI element representing class " + className + " can not " +
           "be instantiated.", e);
      }
      catch (IllegalAccessException e)
      {
        Logger.getLogger(this.getClass().getName()).log
          (Level.WARNING, "configured GUI element representing class " + className + " can not " +
           "be instantiated.", e);
        throw new GUIModelConfigurationException
          ("configured GUI element representing class " + className + " can not " +
           "be instantiated.", e);
      }
      catch (InvocationTargetException e)
      {
        Logger.getLogger(this.getClass().getName()).log
          (Level.WARNING, "configured GUI element representing class " + className + " can not " +
           "be instantiated.", e);
        throw new GUIModelConfigurationException
          ("configured GUI element representing class " + className + " can not " +
           "be instantiated.", e);
      }
    }
    
    return null;
  }

  //-----------------------------------------------------------------------------------------------
  /**
   * TODO: comment
   *
   * @return
   */
  //-----------------------------------------------------------------------------------------------
  private synchronized Properties getMappingsFromConfiguration()
  {
    if (mMappingsFromConfiguration != null)
    {
      return mMappingsFromConfiguration;
    }
    else
    {
      mMappingsFromConfiguration = new Properties();
      
      InputStream inStream =
        this.getClass().getClassLoader().getResourceAsStream("GUIElementMapping.txt");
      
      if (inStream != null)
      {
        try
        {
          mMappingsFromConfiguration.load(inStream);
        }
        catch (IOException e)
        {
          Logger.getLogger(this.getClass().getName()).warning
            ("could not load GUIElementMapping.txt from classpath, but this may be intended");
        }
      }
      
      return mMappingsFromConfiguration;
    }
  }

}
