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

import java.util.LinkedList;
import java.util.Map;

import org.etsi.common.ByteHelper;
import org.etsi.its.codec.TestAdapterMessageTypeEnum;
import org.etsi.its.codec.adapter.IType;
import org.etsi.its.codec.adapter.IValue;
import org.etsi.its.codec.adapter.UpperTesterCodecImpl;

/**
 * This class provides a representation in TLV sequence format of Cam/denm PDU
 */
public class DenmPduInstance implements IPduInstance {

	/**
	 * This list hosts all Cam/denm PDU elements
	 * @see PduItem
	 */
	private LinkedList<PduItem> _items;

	/**
	 * This map will contains all fields set in the CAM/DENM PDU
	 * The key shall match TTCN-3 field name, i.g. generationTime
	 * The value shall be the name of the method to be called in Hitachi ITSCam object to be compared with our CAM/DENM PDU
	 */
	private Map<String, String> _pduTypeIdentifiers;

	/**
	 * The instance of the class UpperTesterCodecImpl
	 */
	private static UpperTesterCodecImpl _utImpl;

	/**
	 * Counter used to parse <code>_items</code>
	 * 
	 * @see "GetFirstItem(), GetNextItem(), GetPrevItem()"
	 */
	private int _idx;

	/**
	 * Constructor
	 */
	private DenmPduInstance(Map<String, String> camPduTypeIdentifiers) {
		_items = new LinkedList<PduItem>();
		_idx = -1;
		_pduTypeIdentifiers = camPduTypeIdentifiers;
		_utImpl = new UpperTesterCodecImpl();
	}

	/**
	 * Add a new <code>PduItem</code> value in the list
	 * @param item The  <code>PduItem</code> value to be added
	 */
	public void AddItem(final PduItem item) {
		_items.add(item);
	}

	/**
	 * This method returns the name of the method (provided by the IUT's API, e.g. Hitachi or Nec) to be executed to retrieve the physical value of the Cam/denm PDU element 
	 * @param itemType The the Cam/denm PDU element
	 * @return The name of a method
	 */
	public String GetMethodFromItemType(final String itemType) {
		// Sanity check
		if (itemType == null) {
			return null;
		}

		if (_pduTypeIdentifiers.containsKey(itemType)) {
			return _pduTypeIdentifiers.get(itemType);
		}

		return null;
	} // End of method GetMethodFromItemType

	/**
	 * This methods provides the first Cam/Denm PDU element from the list <code>_items</code>
	 * @return The first element of the list, null if the list is empty
	 */
	public PduItem GetFirstItem() {
		// Sanity check
		if (_items.size() == 0) {
			return null;
		}

		_idx = 0;
		return _items.get(_idx);
	} // End of method GetFirstItem

	/**
	 * This methods provides the next Cam/Denm PDU element from the list <code>_items</code>
	 * @return The next element of the list, null if the end of the list was reached
	 */
	public PduItem GetNextItem() {
		_idx += 1;

		if (_idx < _items.size()) {
			return _items.get(_idx);
		}

		return null;
	} // End of method GetNextItem

	/**
	 * This methods provides the previous Cam/Denm PDU element from the list <code>_items</code>
	 * @return The previous element of the list, null if the beginning of the list was reached
	 */
	public PduItem GetPrevItem() {
		_idx -= 1;

		if ((_idx >= 0) && (_idx < _items.size())) {
			return _items.get(_idx);
		}

		return null;
	} // End of method GetPreviousItem

	/**
	 * Initialise this module
	 * 
	 * @param pduInTLVFormat
	 * @return true on success, false otherwise
	 */
	private boolean initialize(final byte[] pduInTLVFormat) {
		// Sanity checks already done

		// Convert byte array into CamPduItem suite
		IValue tlvs = _utImpl.Decode(pduInTLVFormat);
		if (tlvs == null) {
			return false;
		}
		// Skip UT fields
		
		IType item = tlvs.getFirstType(); // Skip UtDenmEvent
		item = tlvs.getNextType(); // Skip Situation
		while (item != null) {
			System.out.println(String.format("DenmPduInstance.initialize: process '%s'", ((TestAdapterMessageTypeEnum)item.getTypeId()).name())); // TODO To be removed
			switch ((TestAdapterMessageTypeEnum)item.getTypeId()) {
				case Situation: {
						// Fill cause
						IType field = tlvs.getNextType();
						String key = (String) field.getTypeId().name();
						if (_pduTypeIdentifiers.containsKey(key)) {
							AddItem(new PduItem(key, ByteHelper.byteArrayToInt((byte[]) field.getValue())));
						} else {
							throw new RuntimeException(String.format("Key not found: %s", key));
						}
						// Fill sub-cause
						field = tlvs.getNextType();
						key = (String) field.getTypeId().name();
						if (_pduTypeIdentifiers.containsKey(key)) {
							AddItem(new PduItem(key, ByteHelper.byteArrayToInt((byte[]) field.getValue())));
						} else {
							throw new RuntimeException(String.format("Key not found: %s", key));
						}
					} // End of case camParameters
					break;
				case ActionID: {
					// Fill stationID
					IType field = tlvs.getNextType();
					String key = (String) field.getTypeId().name();
					if (_pduTypeIdentifiers.containsKey(key)) {
						AddItem(new PduItem(key, ByteHelper.byteArrayToInt((byte[]) field.getValue())));
					} else {
						throw new RuntimeException(String.format("Key not found: %s", key));
					}
					// Fill sequence number
					field = tlvs.getNextType();
					key = (String) field.getTypeId().name();
					if (_pduTypeIdentifiers.containsKey(key)) {
						AddItem(new PduItem(key, ByteHelper.byteArrayToInt((byte[]) field.getValue())));
					} else {
						throw new RuntimeException(String.format("Key not found: %s", key));
					}
				} // End of case camParameters
				break;
				case EventOption: 
					// Should be skipped
					break;
				case eventCancellation: {
						String key = (String) item.getTypeId().name();
						if (_pduTypeIdentifiers.containsKey(key)) {
							AddItem(new PduItem(key, new Byte((byte) 0xff)));
						} else {
							throw new RuntimeException(String.format("Key not found: %s", key));
						}
					}
					break;
				case eventNegation:
					// Should be skipped
					break;
				default:
					throw new RuntimeException();
			} // End of 'switch' statement

			item = tlvs.getNextType();
		} // End of 'while' statement

		return true;
	}

	/**
	 * Create a new instance of this class
	 * 
	 * @param pduInTLVFormat
	 * @param pduTypeIdentifiers 
	 * @return A new instance of DenmPduInstance
	 */
	public static DenmPduInstance createInstance(final byte[] pduInTLVFormat, final Map<String, String> pduTypeIdentifiers) {
		// Sanity checks
		if ((pduInTLVFormat == null) || (pduInTLVFormat.length == 0) || (pduTypeIdentifiers == null) || (pduTypeIdentifiers.size() == 0)) {
			throw new IllegalArgumentException("DenmPduInstance.createInstance: Wrong parameters, check that Cam/Denm PDU is not null or empty, or camPduTypeIdentifiers is properly built");
		}
		// Create a new instance of DenmPduInstance class
		DenmPduInstance instance = new DenmPduInstance(pduTypeIdentifiers);
		if (!instance.initialize(pduInTLVFormat)) {
			throw new RuntimeException("DenmPduInstance.createInstance: failed to initialize DenmPduInstance instance");
		}
		
		return instance;
	} // End of method createInstance
	
} // End of class DenmPduInstance
