/**
 * @author	STF 424_ITS_Test_Platform
 * @version    $URL$
 *             $Id$
 */
package org.etsi.its.codec.adapter;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.LinkedList;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.etsi.common.ByteHelper;
import org.etsi.its.codec.IEnumConverter;
import org.etsi.its.codec.TestAdapterMessageTypeEnum;

/**
 * This class provides a codec to encode/decode TLV messages exchanged between TTCN-3 and Upper Tester port
 */
public class UpperTesterCodecImpl implements ICodec {

	/**
	 * Module version
	 */
	public static final String Version = "1.0.0.0";

	/**
	 * Logger instance
	 */
	private final static Logger _logger = Logger.getLogger("org.etsi.its");

	/**
	 * The built list of TLV elements during decoding operation
	 */
	private LinkedList<IType> _typeIds;

	/**
	 * Constructor
	 */
	public UpperTesterCodecImpl() {
		_logger.entering("UpperTesterCodecImpl", "Constructor", String.format("version:%s", Version));

		_typeIds = null;
	}
	
	/**
	 * Decodes the specified message
	 * 
	 * @param message The message to decode in bytes format
	 * @return A list of TLV sequences
	 */
	@Override
	public IValue Decode(final byte[] message) {
		// Sanity checks
		if ((message == null) || (message.length == 0)) {
			return null;
		}
		_typeIds = new LinkedList<IType>();
		Decode_r(TestAdapterMessageTypeEnum.valueOf(message[0x00]), message); // Fill _typeIds
		return new TLVs(_typeIds);
	}

	/**
	 * Encode the specified type/value into a TLV sequence
	 * 
	 * @param type The type to encode
	 * @param buffer The value for this type
	 * @param timestamp Reception time
	 * @return A TLV sequence in byte format
	 * @throws IOException Signals that an I/O exception of some sort has occurred
	 */
	@Override
	public byte[] Encode(final IEnumConverter type, final byte[] buffer, final long timestamp) throws IOException {

		ByteArrayOutputStream value = new ByteArrayOutputStream();
		value.write(type.convert());
		if ((buffer == null) || (buffer.length == 0)) {
			value.write((byte)0x00); // Length
			value.write((byte)0x00);
		} else {
			value.write(ByteHelper.intToByteArray(buffer.length, 2));
			value.write(buffer);
		}
		
		return value.toByteArray();
	} // End of method Encode

	/**
	 * This method provides a recursive mechanism to decode the specified TLV sequence
	 * 
	 * @param type The requested type
	 * @param message The TLV sequence to decode
	 */
	private void Decode_r(IEnumConverter type, byte[] message) {
		_logger.entering("UpperTesterCodecImpl", "Decode_r", String.format("type:%s", type.name()));

		if (_logger.isLoggable(Level.ALL)) ByteHelper.dump("UpperTesterCodecImpl.Decode_r: ", message);
		
		// End of recursive checks
		if ( // FIXME Try to optimise code with 
				(message == null) ||
				(type == TestAdapterMessageTypeEnum.ChangePosition) || 
				(type == TestAdapterMessageTypeEnum.GeoNetworkingPacket) || 
				(type == TestAdapterMessageTypeEnum.gnAddress) || 
//				(type == TestAdapterMessageTypeEnum.area) || 
				(type == TestAdapterMessageTypeEnum.lifetime) || 
				(type == TestAdapterMessageTypeEnum.payload) ||
				(type == TestAdapterMessageTypeEnum.nbHops) ||
				(type == TestAdapterMessageTypeEnum.angle) ||
				(type == TestAdapterMessageTypeEnum.distanceA) ||
				(type == TestAdapterMessageTypeEnum.distanceB) ||
				(type == TestAdapterMessageTypeEnum.shape) ||
				(type == TestAdapterMessageTypeEnum.geoAreaPosLatitude) ||
				(type == TestAdapterMessageTypeEnum.geoAreaPosLongitude) ||
				(type == TestAdapterMessageTypeEnum.destinationPort) || 
				(type == TestAdapterMessageTypeEnum.sourcePort) || 
				(type == TestAdapterMessageTypeEnum.destinationPortInfo) || 
				(type == TestAdapterMessageTypeEnum.changeHeading) || 
				(type == TestAdapterMessageTypeEnum.changePosition) || 
				(type == TestAdapterMessageTypeEnum.changeSpeed) || 
				(type == TestAdapterMessageTypeEnum.setCrashSignal) || 
				(type == TestAdapterMessageTypeEnum.setDangerousGoodsStatus) || 
				(type == TestAdapterMessageTypeEnum.setLengthWidthPrecision) || 
				(type == TestAdapterMessageTypeEnum.setDistanceToStopLine) || 
				(type == TestAdapterMessageTypeEnum.changeCurvature) || 
				(type == TestAdapterMessageTypeEnum.setOccupancy) || 
				(type == TestAdapterMessageTypeEnum.setDoorStatus) || 
				(type == TestAdapterMessageTypeEnum.setLightBarStatus) || 
				(type == TestAdapterMessageTypeEnum.setSireneStatus) || 
				(type == TestAdapterMessageTypeEnum.setTrafficLightPriority) || 
				(type == TestAdapterMessageTypeEnum.setScheduleDeviation) || 
				(type == TestAdapterMessageTypeEnum.courseOfJourney) || 
				(type == TestAdapterMessageTypeEnum.lineRef ) || 
				(type == TestAdapterMessageTypeEnum.routeRef) || 
				(type == TestAdapterMessageTypeEnum.setExteriorLightsStatus) || 
				(type == TestAdapterMessageTypeEnum.distance) || 
				(type == TestAdapterMessageTypeEnum.direction) || 
				(type == TestAdapterMessageTypeEnum.curvature) || 
				(type == TestAdapterMessageTypeEnum.cause) || 
				(type == TestAdapterMessageTypeEnum.subCause) || 
				(type == TestAdapterMessageTypeEnum.sequenceNo) || 
				(type == TestAdapterMessageTypeEnum.eventTrafficFlowEffect) || 
				(type == TestAdapterMessageTypeEnum.eventExpirationTime) || 
				(type == TestAdapterMessageTypeEnum.eventSeverity) || 
				(type == TestAdapterMessageTypeEnum.eventReliability) || 
				(type == TestAdapterMessageTypeEnum.expiryTime) || 
				(type == TestAdapterMessageTypeEnum.expirationTime) || 
				(type == TestAdapterMessageTypeEnum.frequency) || 
				(type == TestAdapterMessageTypeEnum.eventmobility) || 
				(type == TestAdapterMessageTypeEnum.causeType) || 
				(type == TestAdapterMessageTypeEnum.relevance) || 
				(type == TestAdapterMessageTypeEnum.timeCriticality) || 
				(type == TestAdapterMessageTypeEnum.BtpPayload) || 
				(type == TestAdapterMessageTypeEnum.protocolVersion) || 
				(type == TestAdapterMessageTypeEnum.messageID) || 
				(type == TestAdapterMessageTypeEnum.generationTime) || 
				(type == TestAdapterMessageTypeEnum.stationID) || 
				(type == TestAdapterMessageTypeEnum.mobileItsStation) || 
				(type == TestAdapterMessageTypeEnum.privateItsStation) || 
				(type == TestAdapterMessageTypeEnum.physicalRelevantItsStation) || 
				(type == TestAdapterMessageTypeEnum.hemisphere) || 
				(type == TestAdapterMessageTypeEnum.degree) || 
				(type == TestAdapterMessageTypeEnum.elevation) || 
				(type == TestAdapterMessageTypeEnum.heading) || 
				(type == TestAdapterMessageTypeEnum.streetName) || 
				(type == TestAdapterMessageTypeEnum.positionConfidence) || 
				(type == TestAdapterMessageTypeEnum.elevationConfidence) || 
				(type == TestAdapterMessageTypeEnum.roadSegmentID) || 
				(type == TestAdapterMessageTypeEnum.vehicleType) || 
				(type == TestAdapterMessageTypeEnum.vehicleSpeed) || 
				(type == TestAdapterMessageTypeEnum.vehicleSpeedConfidence) || 
				(type == TestAdapterMessageTypeEnum.stationLength) || 
				(type == TestAdapterMessageTypeEnum.stationLengthConfidence) || 
				(type == TestAdapterMessageTypeEnum.stationWidth) || 
				(type == TestAdapterMessageTypeEnum.stationWidthConfidence) || 
				(type == TestAdapterMessageTypeEnum.longAcceleration) || 
				(type == TestAdapterMessageTypeEnum.longAccelerationConfidence) || 
				(type == TestAdapterMessageTypeEnum.accelerationControl) || 
				(type == TestAdapterMessageTypeEnum.yawDirection) || 
				(type == TestAdapterMessageTypeEnum.yawRateValue) || 
				(type == TestAdapterMessageTypeEnum.yawRateConfidence) || 
				(type == TestAdapterMessageTypeEnum.exteriorLights) || 
				(type == TestAdapterMessageTypeEnum.distanceToStopLine) || 
				(type == TestAdapterMessageTypeEnum.occupancy) || 
				(type == TestAdapterMessageTypeEnum.doorOpen) || 
				(type == TestAdapterMessageTypeEnum.semiMajorConfidence) || 
				(type == TestAdapterMessageTypeEnum.semiMinorConfidence) || 
				(type == TestAdapterMessageTypeEnum.semiMajorOrientation) || 
				(type == TestAdapterMessageTypeEnum.curvatureChange) || 
				(type == TestAdapterMessageTypeEnum.curvatureConfidence) || 
				(type == TestAdapterMessageTypeEnum.crashStatus) || 
				(type == TestAdapterMessageTypeEnum.headingConfidence) || 
				(type == TestAdapterMessageTypeEnum.dangerousGoods)) {
			return;
		}

		int index = 0;
		while (index < message.length) {
			TestAdapterMessageTypeEnum typeId = TestAdapterMessageTypeEnum.valueOf(message[index]);
			int length = ByteHelper.byteArrayToShort(ByteHelper.extract(message, index + 1, 2));
			_logger.info(String.format("New type: %s - length:%d", typeId.name(), length));
			ByteArrayOutputStream value = new ByteArrayOutputStream();
			_typeIds.add(new TLV(typeId, value));
			if (length != 0x00) { // Length
				value.write(message, index + 3/*Payload*/, length);
				Decode_r(typeId, value.toByteArray());
			}
			index += (length + 3);
		} // End of 'while' statement
	} // End of method Decode_r

} // End of class UpperTesterCodecImpl
