/**
 * @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 Adapter Control port
 */
public class AdapterControlCodecImpl 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 AdapterControlCodecImpl() {
		_logger.entering("AdapterControlCodecImpl", "Constructor", String.format("version:%s - %s", Version, this.toString()));

		_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(IEnumConverter type, 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(final IEnumConverter type, final byte[] message) {
		_logger.entering("AdapterControlCodecImpl", "Decode_r", String.format("type:%s", type.name()));

		if (_logger.isLoggable(Level.ALL)) ByteHelper.dump("AdapterControlCodecImpl.Decode_r:", message);
		
		// End of recursive calls
		if (
				(message == null) || 
				(type == TestAdapterMessageTypeEnum.AcGetLongPosVector) || 
				(type == TestAdapterMessageTypeEnum.AcStartBeaconing) || 
				(type == TestAdapterMessageTypeEnum.AcStartPassBeaconing) || 
				(type == TestAdapterMessageTypeEnum.AcStopPassBeaconing) || 
				(type == TestAdapterMessageTypeEnum.AcStopBeaconing)) {
			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);
				if (type != TestAdapterMessageTypeEnum.AcGn6Primitive) {
					Decode_r(typeId, value.toByteArray());
				}
			}
			index += (length + 3);
		} // End of 'while' statement
	} // End of method Decode_r

} // End of class AdapterControlCodecImpl
