/**
 *  Main Test Adapter class. Implements TRI API
 *  
 *  @author     ETSI / STF424
 *  @version    $URL$
 *              $Id$
 *  @see        "http://t-ort.etsi.org/view_all_bug_page.php?page_number=1"
 */
package org.etsi.its.adapter;

import java.util.Observable;
import java.util.Observer;
import java.util.logging.ConsoleHandler;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.etsi.adapter.ITERequired;
import org.etsi.adapter.TERFactory;
import org.etsi.common.ByteHelper;
import org.etsi.its.adapter.ports.AdapterControlPort;
import org.etsi.its.adapter.ports.IPort;
import org.etsi.its.adapter.ports.PortEvent;
import org.etsi.its.adapter.ports.ProtocolPortFactory;
import org.etsi.its.adapter.ports.UpperTesterPort;
import org.etsi.ttcn.tci.CharstringValue;
import org.etsi.ttcn.tri.TriActionTemplate;
import org.etsi.ttcn.tri.TriAddress;
import org.etsi.ttcn.tri.TriAddressList;
import org.etsi.ttcn.tri.TriCommunicationSA;
import org.etsi.ttcn.tri.TriComponentId;
import org.etsi.ttcn.tri.TriException;
import org.etsi.ttcn.tri.TriMessage;
import org.etsi.ttcn.tri.TriParameter;
import org.etsi.ttcn.tri.TriParameterList;
import org.etsi.ttcn.tri.TriPortId;
import org.etsi.ttcn.tri.TriPortIdList;
import org.etsi.ttcn.tri.TriSignatureId;
import org.etsi.ttcn.tri.TriStatus;
import org.etsi.ttcn.tri.TriTestCaseId;

/**
 *  Main Test Adapter class. Implements TRI API in a tool independent manner
 */
@SuppressWarnings("serial")
public class TestAdapter implements TriCommunicationSA, Observer {

	/**
	 * Module version
	 */
	public static final String Version = "1.0.0.0";
	
	/**
	 * Logger instance
	 */
	private final static Logger _logger = Logger.getLogger("org.etsi.its");

	/** 
	 * Mapping (component-) support for layered port
	 */
	private IComponentMgr _compPortMgr;

	/**
	 * Provides all TE related interfaces
	 */
	private ITERequired _required;
	
	// TODO: To be enhanced
	private int _localPortNumber = 12320;
	private int _remotePortNumber = 12320;

	/**
	 * Constructor
	 */
	public TestAdapter() {
		super();
		_compPortMgr = new ComponentMgr(this);
		if ((_compPortMgr = new ComponentMgr(this)) == null) {
			throw new RuntimeException("TestAdapter: Unable to create instance of _compPortMgr");
		}
		_required = TERFactory.getInstance();
		if (_required == null) {
			throw new RuntimeException("TestAdapter: Unable to get TERRequired instance");
		}
		
		String debugLevel = ((CharstringValue)_required.getTaParameter("DEBUG_ENABLED")).getString();
		Level level = Level.OFF;
		if (debugLevel.equalsIgnoreCase("ALL")) {
			level = Level.ALL;
		} else if (debugLevel.equalsIgnoreCase("INFO")) {
			level = Level.INFO;
		} else if (debugLevel.equalsIgnoreCase("SEVERE")) {
			level = Level.SEVERE;
		} else if (!debugLevel.equalsIgnoreCase("OFF")) {
			System.err.println("Unsupported logging level: " + debugLevel);
	}
		_logger.addHandler(new ConsoleHandler());
		_logger.setLevel(level);
		_logger.getHandlers()[0].setLevel(level);
		
		_logger.entering("TestAdapter", "Constructor", String.format("version: %s", Version));
	}

	/**
	 * This method will force a Test adapter reset
	 * @see org.etsi.ttcn.tri.TriCommunicationSA#triEndTestCase()
	 */
	@Override
	public TriStatus triEndTestCase() {
		triSAReset();
		return _required.getTriStatus(TriStatus.TRI_OK);
	} // End of method triEndTestCase

	/* (non-Javadoc)
	 * @see org.etsi.ttcn.tri.TriCommunicationSA#triExecuteTestcase(org.etsi.ttcn.tri.TriTestCaseId, org.etsi.ttcn.tri.TriPortIdList)
	 */
	public TriStatus triExecuteTestcase(final TriTestCaseId tcId, final TriPortIdList portList) {
		_logger.entering("TestAdapter", "triExecuteTestcase", String.format("tcId:%s - portList:%d", tcId.getTestCaseName(), portList.size()));

		_logger.exiting("TestAdapter", "triExecuteTestcase");
		return _required.getTriStatus(TriStatus.TRI_OK);
	} // End of method triExecuteTestcase

	/* (non-Javadoc)
	 * @see org.etsi.ttcn.tri.TriCommunicationSA#triMap(org.etsi.ttcn.tri.TriPortId, org.etsi.ttcn.tri.TriPortId)
	 */
	public TriStatus triMap(final TriPortId compPortId, final TriPortId tsiPortId) {
		_logger.entering("TestAdapter", "triMap", String.format("comPortId:%s - tsiPortId:%s", compPortId.getComponent().getComponentId(), tsiPortId));

		IPort port;
			
		if (tsiPortId.getPortName() == "acPort") {
			port = new AdapterControlPort(tsiPortId.getPortName(), compPortId.getComponent().getComponentId());
		} else if (tsiPortId.getPortName() == "utPort") {
			port = new UpperTesterPort(tsiPortId.getPortName(), compPortId.getComponent().getComponentId(), _localPortNumber, _remotePortNumber);
				
			// TODO: To be enhanced
			_localPortNumber += 1;
			_remotePortNumber += 1;
		} else {
			port = ProtocolPortFactory.getInstance().createPort(
			        tsiPortId.getPortName(), 
			        compPortId.getComponent().getComponentId(), 
			        ((CharstringValue)_required.getTaParameter(tsiPortId.getPortName())).getString(),
			        ((CharstringValue)_required.getTaParameter("LinkLayer_" + compPortId.getComponent().getComponentName())).getString());
		}
		_compPortMgr.addComponent(compPortId.getComponent());
		_compPortMgr.addPort(compPortId.getComponent().getComponentId(), tsiPortId, port);
				
		_logger.exiting("TestAdapter", "triMap");
		return _required.getTriStatus(TriStatus.TRI_OK);
	} // End of method triMap

	/**
	 * This method will force port unmapping   
	 * @see org.etsi.ttcn.tri.TriCommunicationSA#triSAReset()
	 */
	@Override
	public TriStatus triSAReset() {

		// Force port unmapping
		_compPortMgr.removeAllPorts();
		
		return _required.getTriStatus(TriStatus.TRI_OK);
	} // End of method triSAReset

	/* (non-Javadoc)
	 * @see org.etsi.ttcn.tri.TriCommunicationSA#triSend(org.etsi.ttcn.tri.TriComponentId, org.etsi.ttcn.tri.TriPortId, org.etsi.ttcn.tri.TriAddress, org.etsi.ttcn.tri.TriMessage)
	 */
	public TriStatus triSend(TriComponentId componentId, TriPortId tsiPortId,
			TriAddress address, TriMessage message) {
		_logger.entering("TestAdapter", "triSend", String.format("componentId:%s - portId:%s - address:%s - message:%s", componentId.getComponentId(), tsiPortId.getPortName(), address, message.toString()));

		IPort port = _compPortMgr.getPort(componentId.getComponentId(), tsiPortId.getPortName());
		if (port != null) {
			port.send(message.getEncodedMessage());
		} else {
			_logger.severe("Unknown port");
		}

		return _required.getTriStatus(TriStatus.TRI_OK);
	} // End of method triSend

	/* (non-Javadoc)
	 * @see org.etsi.ttcn.tri.TriCommunicationSA#triSendBC(org.etsi.ttcn.tri.TriComponentId, org.etsi.ttcn.tri.TriPortId, org.etsi.ttcn.tri.TriMessage)
	 */
	@Override
	public TriStatus triSendBC(TriComponentId componentId, TriPortId portId,
			TriMessage message) {
		return _required.getTriStatus(TriStatus.TRI_ERROR, "broadcast is not implemented");
		// could be handled like a normal triSend() instead:
		// return triSend(componendId, portId, null, message);
	}

	/* (non-Javadoc)
	 * @see org.etsi.ttcn.tri.TriCommunicationSA#triSendMC(org.etsi.ttcn.tri.TriComponentId, org.etsi.ttcn.tri.TriPortId, org.etsi.ttcn.tri.TriAddressList, org.etsi.ttcn.tri.TriMessage)
	 */
	public TriStatus triSendMC(TriComponentId componentId, TriPortId portId,
			TriAddressList addressList, TriMessage message) {
		return _required.getTriStatus(TriStatus.TRI_ERROR, "multicast is not implemented");
		// could be handled like a normal triSend() instead:
		// return triSend(componendId, portId, null, message);
	}

	/* (non-Javadoc)
	 * @see org.etsi.ttcn.tri.TriCommunicationSA#triUnmap(org.etsi.ttcn.tri.TriPortId, org.etsi.ttcn.tri.TriPortId)
	 */
	@Override
	public TriStatus triUnmap(final TriPortId compPortId, final TriPortId tsiPortId) {
		_logger.entering("TestAdapter", "triUnmap", String.format("componentId:%s - portId:%s", compPortId.getComponent().getComponentId(), tsiPortId.getPortName()));

		_compPortMgr.removePort(compPortId.getComponent().getComponentId(), tsiPortId.getPortName());
		
		return _required.getTriStatus(TriStatus.TRI_OK);
	} // End of method triUnmap

	/**
	 * Enqueues a message on the specified port
	 * @param  tsiPort         Port where the message will be enqueued
	 * @param  sutAddress      SUT address
	 * @param  receiverComp    Component on which port the message will be enqueued
	 * @param  rcvMessage      Message to be enqueued
	 */
	private void enqueueMsg(TriPortId tsiPort, TriAddress sutAddress, TriComponentId receiverComp, TriMessage rcvMessage) {
		_logger.entering("TestAdapter", "enqueueMsg", String.format("%s/%s - sutAddress:%s", receiverComp.getComponentName(), tsiPort.getPortName(), sutAddress));

		_required.getCommunicationTE().triEnqueueMsg(tsiPort, sutAddress, receiverComp, rcvMessage);
	} // End of method enqueueMsg

	/* (non-Javadoc)
	 * @see java.util.Observer#update(java.util.Observable, java.lang.Object)
	 */
	@Override
	public void update(Observable o, Object context) {
		_logger.entering("TestAdapter", "update", String.format("%s - sutAddress:%s", o.toString(), context.toString()));

		if (context instanceof PortEvent) {
			// Extract context
			PortEvent p = (PortEvent)context;
			TriPortId port = null;
			TriComponentId compId = null;

			port = (TriPortId)_compPortMgr.getPortId(p.getComponentName(), p.getPortName());
			compId = _compPortMgr.getComponent(p.getComponentName());

			// Enqueue message
			if (port != null) {
				if (_logger.isLoggable(Level.ALL)) ByteHelper.dump(String.format("TestAdapter.update: enqueue: %s/%s", _compPortMgr.getComponent(p.getComponentName()), port.getPortName()), p.get_message());
				enqueueMsg(
						port, 
						_required.getTriAddress(new byte[] { }), 
						compId, 
						_required.getTriMessage(p.get_message()));
			}
		}
	} // End of method update

	/**
	 * TriCall not implemented
	 * @see org.etsi.ttcn.tri.TriCommunicationSA#triCall(org.etsi.ttcn.tri.TriComponentId, org.etsi.ttcn.tri.TriPortId, org.etsi.ttcn.tri.TriAddress, org.etsi.ttcn.tri.TriSignatureId, org.etsi.ttcn.tri.TriParameterList)
	 */
	@Override
	public TriStatus triCall(TriComponentId componentId, TriPortId tsiPortId,
			TriAddress sutAddress, TriSignatureId signatureId,
			TriParameterList parameterList) {
		return _required.getTriStatus(TriStatus.TRI_ERROR, "not implemented");	}

	/**
	 * TriCallBC not implemented
	 * @see org.etsi.ttcn.tri.TriCommunicationSA#triCallBC(org.etsi.ttcn.tri.TriComponentId, org.etsi.ttcn.tri.TriPortId, org.etsi.ttcn.tri.TriSignatureId, org.etsi.ttcn.tri.TriParameterList)
	 */
	@Override
	public TriStatus triCallBC(TriComponentId componentId, TriPortId tsiPortId,
			TriSignatureId signatureId, TriParameterList parameterList) {
		return _required.getTriStatus(TriStatus.TRI_ERROR, "not implemented");	}

	/**
	 * TriCallMC not implemented
	 * @see org.etsi.ttcn.tri.TriCommunicationSA#triCallMC(org.etsi.ttcn.tri.TriComponentId, org.etsi.ttcn.tri.TriPortId, org.etsi.ttcn.tri.TriAddressList, org.etsi.ttcn.tri.TriSignatureId, org.etsi.ttcn.tri.TriParameterList)
	 */
	@Override
	public TriStatus triCallMC(TriComponentId componentId, TriPortId tsiPortId,
			TriAddressList sutAddresses, TriSignatureId signatureId,
			TriParameterList parameterList) {
		return _required.getTriStatus(TriStatus.TRI_ERROR, "not implemented");	}

	/**
	 * triRaise not implemented
	 * @see org.etsi.ttcn.tri.TriCommunicationSA#triRaise(org.etsi.ttcn.tri.TriComponentId, org.etsi.ttcn.tri.TriPortId, org.etsi.ttcn.tri.TriAddress, org.etsi.ttcn.tri.TriSignatureId, org.etsi.ttcn.tri.TriException)
	 */
	@Override
	public TriStatus triRaise(TriComponentId componentId, TriPortId tsiPortId,
			TriAddress sutAddress, TriSignatureId signatureId,
			TriException exception) {
		return _required.getTriStatus(TriStatus.TRI_ERROR, "not implemented");	}

	/**
	 * triRaiseBC not implemented
	 * @see org.etsi.ttcn.tri.TriCommunicationSA#triRaiseBC(org.etsi.ttcn.tri.TriComponentId, org.etsi.ttcn.tri.TriPortId, org.etsi.ttcn.tri.TriSignatureId, org.etsi.ttcn.tri.TriException)
	 */
	@Override
	public TriStatus triRaiseBC(TriComponentId componentId,
			TriPortId tsitPortId, TriSignatureId signatureId, TriException exc) {
		return _required.getTriStatus(TriStatus.TRI_ERROR, "not implemented");	}

	/**
	 * triRaiseMC not implemented
	 * @see org.etsi.ttcn.tri.TriCommunicationSA#triRaiseMC(org.etsi.ttcn.tri.TriComponentId, org.etsi.ttcn.tri.TriPortId, org.etsi.ttcn.tri.TriAddressList, org.etsi.ttcn.tri.TriSignatureId, org.etsi.ttcn.tri.TriException)
	 */
	@Override
	public TriStatus triRaiseMC(TriComponentId componentId,
			TriPortId tsitPortId, TriAddressList sutAddresses,
			TriSignatureId signatureId, TriException exc) {
		return _required.getTriStatus(TriStatus.TRI_ERROR, "not implemented");	}

	/**
	 * triReply not implemented
	 * @see org.etsi.ttcn.tri.TriCommunicationSA#triReply(org.etsi.ttcn.tri.TriComponentId, org.etsi.ttcn.tri.TriPortId, org.etsi.ttcn.tri.TriAddress, org.etsi.ttcn.tri.TriSignatureId, org.etsi.ttcn.tri.TriParameterList, org.etsi.ttcn.tri.TriParameter)
	 */
	@Override
	public TriStatus triReply(TriComponentId componentId, TriPortId tsiPortId,
			TriAddress sutAddress, TriSignatureId signatureId,
			TriParameterList parameterList, TriParameter returnValue) {
		return _required.getTriStatus(TriStatus.TRI_ERROR, "not implemented");	}

	/**
	 * triReplyBC not implemented
	 * @see org.etsi.ttcn.tri.TriCommunicationSA#triReplyBC(org.etsi.ttcn.tri.TriComponentId, org.etsi.ttcn.tri.TriPortId, org.etsi.ttcn.tri.TriSignatureId, org.etsi.ttcn.tri.TriParameterList, org.etsi.ttcn.tri.TriParameter)
	 */
	@Override
	public TriStatus triReplyBC(TriComponentId componentId,
			TriPortId tsiPortId, TriSignatureId signatureId,
			TriParameterList parameterList, TriParameter returnValue) {
		return _required.getTriStatus(TriStatus.TRI_ERROR, "not implemented");	}

	/**
	 * triReplyMC not implemented
	 * @see org.etsi.ttcn.tri.TriCommunicationSA#triReplyMC(org.etsi.ttcn.tri.TriComponentId, org.etsi.ttcn.tri.TriPortId, org.etsi.ttcn.tri.TriAddressList, org.etsi.ttcn.tri.TriSignatureId, org.etsi.ttcn.tri.TriParameterList, org.etsi.ttcn.tri.TriParameter)
	 */
	@Override
	public TriStatus triReplyMC(TriComponentId componentId,
			TriPortId tsiPortId, TriAddressList sutAddresses,
			TriSignatureId signatureId, TriParameterList parameterList,
			TriParameter returnValue) {
		return _required.getTriStatus(TriStatus.TRI_ERROR, "not implemented");	}

	/**
	 * triSutActionInformal not implemented
	 * @see org.etsi.ttcn.tri.TriCommunicationSA#triSutActionInformal(java.lang.String)
	 */
	@Override
	public TriStatus triSutActionInformal(String description) {
		return _required.getTriStatus(TriStatus.TRI_ERROR, "not implemented");	}

	/**
	 * triSutActionTemplate not implemented
	 * @see org.etsi.ttcn.tri.TriCommunicationSA#triSutActionTemplate(org.etsi.ttcn.tri.TriActionTemplate)
	 */
	@Override
	public TriStatus triSutActionTemplate(TriActionTemplate templateValue) {
		return _required.getTriStatus(TriStatus.TRI_ERROR, "not implemented");
	}

	public TriStatus triMapParam(TriPortId compPortId, TriPortId tsiPortId,
			TriParameterList paramList) {
		return _required.getTriStatus(TriStatus.TRI_ERROR, "not implemented");
	}

	public TriStatus triUnmapParam(TriPortId compPortId, TriPortId tsiPortId,
			TriParameterList paramList) {
		return _required.getTriStatus(TriStatus.TRI_ERROR, "not implemented");
	}

} // End of class TestAdapter
