// 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.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

import de.ugoe.cs.util.console.Console;

/**
 * TODO comment
 * 
 * TODO rename class to GUIElementFactory
 * 
 * @version $Revision: $ $Date: 13.05.2012$
 * @author 2012, last modified by $Author: patrick$
 */
public class AbstractDefaultGUIElementFactory implements IGUIElementFactory {
    
    /** */
    private static AbstractDefaultGUIElementFactory instance = new AbstractDefaultGUIElementFactory();

    /**
     * TODO: comment
     * 
     */
    private AbstractDefaultGUIElementFactory() {
    }

    /**
     * TODO: comment
     * 
     * @return
     */
    public static synchronized AbstractDefaultGUIElementFactory getInstance() {
        return instance;
    }

    
    /** */
    private Properties mappingsFromConfiguration;

    /**
     * TODO: comment
     * 
     * @param object1
     * @param object2
     * @return
     */
    protected boolean equals(Object object1, Object object2) {
        if (object1 == object2) {
            return true;
        }
        else if (object1 != null) {
            return object1.equals(object2);
        }
        else {
            // object 1 is null but object 2 not --> return false
            return false;
        }
    }

    /**
     * TODO: comment
     * 
     * @param parameterTypes
     * @param parameters
     * @return
     * @throws GUIModelConfigurationException
     */
    @Override
    public IGUIElement instantiateGUIElement(IGUIElementSpec specification) {
        Properties mappings = getMappingsFromConfiguration();
        IGUIElement guiElement = null;

        String className = mappings.getProperty(specification.getType());
        if (className != null) {
            try {
                Class<?> clazz = this.getClass().getClassLoader().loadClass(className);

                if (!IGUIElement.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<?>[1];
                parameterTypes[0] = specification.getClass();

                guiElement =
                    (IGUIElement) clazz.getConstructor(parameterTypes).newInstance(specification);

                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);
            }
        }
        if( guiElement==null ) {
            Console.printerrln("could not find GUI element representing class " + specification.getType());
        }

        return guiElement;
    }

    /**
     * TODO: comment
     * 
     * @return
     */
    private synchronized Properties getMappingsFromConfiguration() {
        if (mappingsFromConfiguration != null) {
            return mappingsFromConfiguration;
        }
        else {
            mappingsFromConfiguration = new Properties();
            
            File mappingsFolder = new File("data/guimappings");
            for( File mappingsFile : mappingsFolder.listFiles()) {
                if(!mappingsFile.isDirectory() && mappingsFile.getName().startsWith("guimapping") && mappingsFile.getName().endsWith(".txt")) {
                    InputStream inStream;
                    try {
                        inStream = new FileInputStream(mappingsFile);
                        mappingsFromConfiguration.load(inStream);
                        inStream.close();
                    }
                    catch (FileNotFoundException e1) {
                        // TODO Auto-generated catch block
                        e1.printStackTrace();
                    }
                    catch (IOException e) {
                        Logger.getLogger(this.getClass().getName()).warning
                            ("invalid GUI mappings files " + mappingsFile.getName());
                    }
                }
            }

            return mappingsFromConfiguration;
        }
    }
}
