package madynes.flowoid.exporter;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;


public class TemplateRecord implements Exportable {
	
	/** RFC3954 5.1 > format */
    private final short headerSize = 4;
    private static ArrayList<Short> templatesIDs = new ArrayList<Short>();
	private short templateID;
	private ArrayList<TemplateField> templateFields;
    
	public static class AlreadyUsedIDException extends Exception {
		private static final long serialVersionUID = 1L;

		public String getMessage() {
			return "You CANNOT redefine a template.";
		}
	}
	
	public static class InadequateIDException extends Exception {
		private static final long serialVersionUID = 1L;

		public String getMessage() {
			return "Template IDs of Data FlowSets"
					+ " MUST be numbered from 256 to 65535.";
		}
	}
	
	/**
	 * Defines a template record (NetFlow).
	 * See RFC3954 5.1 > Field Type / Field Length.
	 * 
	 * @param templateID
	 * @throws AlreadyUsedIDException
	 * @throws InadequateIDException
	 */
	public TemplateRecord(int templateID) throws AlreadyUsedIDException, InadequateIDException {
		templateFields = new ArrayList<TemplateField>();
		
		// RFC3954 5.2 > Template ID > uniqueness is local		
		if(templatesIDs.contains(templateID))
			throw new AlreadyUsedIDException();
		
		// RFC3954 5.2 > Template ID
		if(templateID < 256 || templateID > 65535)
			throw new InadequateIDException();

		this.templateID = (short) templateID;
		templatesIDs.add(this.templateID);
	}

	/**
	 * Adds a template field to this template record definition.
	 * 
	 * @param templateField template field to add
	 */
	public void addTemplateField(TemplateField templateField) {
		templateFields.add(templateField);
	}
	
	@Override
	public short getTotalLength() {
		short length = 0;
		
		for(TemplateField field : templateFields) {
			length += field.getTotalLength();
		}
		
		return (short) (headerSize + length);
	}
	
	/**
	 * Returns the number of template fields in this record template.
	 * 
	 * @return number of template fields
	 */
	public short getCount() {
		return (short) templateFields.size();
	}
	
	/**
	 * Returns the corresponding template ID.
	 * 
	 * @return template ID
	 */
	public short getTemplateID() {
		return templateID;
	}
	
	/**
	 * Returns the nth template field defined for this template record.
	 * 
	 * @param nth index of the template field to retrieve
	 * @return
	 */
	public TemplateField getNthTemplateField(int nth) {
		return templateFields.get(nth);
	}
	
	@Override
	public boolean equals(Object arTemplateRecord){
		if(this == arTemplateRecord)
			return true;

		if(!(arTemplateRecord instanceof TemplateRecord))
			return false;
		
		TemplateRecord rTemplateRecord = (TemplateRecord) arTemplateRecord;
		
		return templateID == rTemplateRecord.getTemplateID();
	}

	@Override
	public ArrayList<Byte> toBytes() {
		ArrayList<Byte> bytes = new ArrayList<Byte>();
		
    	ByteBuffer headers = ByteBuffer.allocate(headerSize);
    	headers.order(ByteOrder.BIG_ENDIAN);

    	headers.putShort(getTemplateID());
    	headers.putShort(getCount());
    	
    	for(byte b : headers.array()) {
    		bytes.add((Byte) b);
    	}
		
		for(TemplateField field : templateFields) {
			bytes.addAll(field.toBytes());
		}
		
		return bytes;
	}
}
