/**
 *  Layer abstract class implementing generic behaviour of a protocol layer
 *  
 *  @author     ETSI / STF424
 *  @version    $URL$
 *              $Id$
 *
 */
package org.etsi.its.adapter.layers;

import java.util.Map;
import java.util.Stack;

import org.etsi.its.adapter.IManagementLayers;

/**
 *  Layer abstract class implementing generic behaviour of a protocol layer
 */
public abstract class Layer {

    /**
     * Parameter name for reception timestamp
     */
    public static final String RECEPTION_TIMESTAMP = "ReceptionTimestamp";
 
    
	/**
	 * Constructor
	 * @param  management   Layer management instance
     * @param  lowerStack   Lower protocol stack   
	 */
	public Layer(IManagementLayers management, Stack<String> lowerStack) {
		this.management = management;
		this.lowerStack = lowerStack;
		this.initialized = false;
	}
	
	/**
	 * Initialises lower stack
	 */
	protected void initialize() {
		if(!initialized && lowerStack != null && !lowerStack.isEmpty()) {
			lowerLayerName = lowerStack.pop();
			lowerLayer = LayerFactory.getInstance().createLayer(management, lowerLayerName, lowerStack);
			lowerLayer.register(this);
		}
		initialized = true;
	}
	
	/**
	 * Transmits a message to registered lower layer after encapsulation
     * @param  message Encoded message to be sent
     * @param  params  Layer parameters for sending message
	 * @return true if send operation is successful, false otherwise
	 */
	public boolean send(byte[] message, Map<String, Object> params) {
		if(lowerLayer != null) {
			return lowerLayer.send(message, params);
		}
		return true;
	}
	
	/**
	 * Callback method invoked by registered lower layer upon message reception from lower stack
	 * @param  message       Message received from lower layer
	 * @param  lowerInfo     Additional information transmitted by lower layers
	 * @param receptionTime  Message reception time
	 */
	public void receive(byte[] message, Map<String, Object> lowerInfo) {
		if(registeredUpperLayer != null) {
			registeredUpperLayer.receive(message, lowerInfo);
		}
	}
	
	/**
	 * Registers an upper layer. 
	 * This method will also cause current layer to initialise its lower stack
	 * Messages received from lower layer will now be transmitted to upper layer, if necessary
	 * @param  upperLayer  Instance of the upper layer
	 */
	public void register(Layer upperLayer) {
		registeredUpperLayer = upperLayer;
		initialize();
	}
	
	/**
	 * Unregisters upper layer.
	 * This method will also cause current layer to unregister from its lower layer
	 * @param upperLayer
	 */
	public void unregister(Layer upperLayer) {
		registeredUpperLayer = null;
		if(lowerLayer != null) {
			lowerLayer.unregister(this);
		}
	}
	
	/**
	 * Name of the lower layer
	 */
	protected String lowerLayerName;
	
	/**
	 * Registered upper layer instance
	 */
	protected Layer registeredUpperLayer = null;
	
	/**
	 * Lower layer instance
	 */
	protected Layer lowerLayer = null;
	
	/**
	 * Management instance
	 */
	protected IManagementLayers management = null;
	
	/**
	 * Lower stack
	 */
	private Stack<String> lowerStack;
	
	/**
	 * Set to true if lower stack has been initialised (true if layer is operational), false otherwise
	 */
	private boolean initialized;
}
