/**
 *  Layer factory (Singleton)
 *  
 *  @author     ETSI / STF424
 *  @version    $URL$
 *              $Id$
 *
 */
package org.etsi.its.adapter.layers;

import java.lang.reflect.Constructor;
import java.util.Map;
import java.util.Stack;
import java.util.TreeMap;

import org.etsi.its.adapter.IManagementLayers;
import org.etsi.its.adapter.layers.test.BtpSourceLayer;
import org.etsi.its.adapter.layers.test.CamSourceLayer;
import org.etsi.its.adapter.layers.test.DebugLayer;
import org.etsi.its.adapter.layers.test.DenmSourceLayer;
import org.etsi.its.adapter.layers.test.Gn6SourceLayer;
import org.etsi.its.adapter.layers.test.GnSourceLayer;
import org.etsi.its.adapter.layers.test.LoopbackLayer;

/**
 *  Layer factory (Singleton)
 */
public class LayerFactory {

    /**
     * Unique instance of the factory
     */
	private static LayerFactory instance = new LayerFactory();
	
	/**
     * List of the registered layer types 
     */
	protected Map<String, Class<? extends Layer>> layers = new TreeMap<String, Class<? extends Layer>>();
	
	/**
     * Private constructor (Singleton pattern)
     */
	private LayerFactory() {
		// Register the layers
		layers.put("BTP", BtpLayer.class);
		layers.put("GN", GnLayer.class);
		layers.put("G5", G5Layer.class);
		layers.put("ETH", EthernetLayer.class);
		
		layers.put("Loopback", LoopbackLayer.class);
		layers.put("Debug", DebugLayer.class);
		layers.put("CamSource", CamSourceLayer.class);
		layers.put("DenmSource", DenmSourceLayer.class);
		layers.put("GnSource", GnSourceLayer.class);
		layers.put("BtpSource", BtpSourceLayer.class);
		layers.put("Gn6Source", Gn6SourceLayer.class);
	}

	/**
     * Gets the unique factory instance
     * @return LayerFactory instance
     */
	public static LayerFactory getInstance(){
		return instance;
	}

	/**
     * Creates a port of the desired type
     * @param  management   Layer management instance
     * @param  layerName    Name of the layer
     * @param  lowerStack   Lower protocol stack    
     * @return Protocol port instance
     */
	public Layer createLayer(IManagementLayers management, String layerName, Stack<String> lowerStack) {
		Layer layer = null;
		Class<?>[] ctorParams = {IManagementLayers.class, lowerStack.getClass()};
		
   		try {		
	        Class<? extends Layer> cls = layers.get(layerName);
	        if (cls == null) {
	        	throw new RuntimeException("No class registered under " + layerName);
	        }
            Constructor<? extends Layer> ctor = cls.getConstructor(ctorParams);
            layer = ctor.newInstance(management, lowerStack);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return layer;
	}

}
