package cz.vutbr.feec.packets.ttp;

import java.util.Vector;

import cz.vutbr.feec.packets.PacketGenerateException;
import cz.vutbr.feec.packets.PacketParseException;
import cz.vutbr.feec.packets.PacketUtils;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;

public class FTSPacket extends GeneralTTPHeader {

	private long FTSseqNumber;

	private long sessionSize;

//	private long increaseFactor;

//	private long reservedEnd;
	
	private Vector<FTSGroupBlockFT> groupListFT = new Vector<FTSGroupBlockFT>();
	private Vector<FTSGroupBlockLM> groupListLM = new Vector<FTSGroupBlockLM>();


	public FTSPacket() {
		super(3);
	}
	
	/**
	 * @param version definuje verzi paketu
	 * @param packetType definuje typ paketu FTD=3
	 * @param fbTreeID definuje ID stromu zpětné vazby
	 * @param length definuje délku celého paketu
	 * @param FTSseqNum definuje sekvencni cislo FTS paketu
	 * @param sessionSize definuje velikost cele relace
	 */
	public FTSPacket (int version, int packetType, long fbTreeID, int length,
			long FTSseqNum, long sessionSize){
		setVersion(version);
		setPacketType(packetType);
		setFeedbackTreeId(fbTreeID);
		setLength(length);
		setFTSseqNumber(FTSseqNum);
		setSessionSize(sessionSize);
		
	}
	
	public void addGroupBlockFT(FTSGroupBlockFT group) {
		groupListFT.add(group);
	}

	public FTSGroupBlockFT getGroupBlockFT(int index) {
		return groupListFT.get(index);
	}

	public void addGroupBlockLM(FTSGroupBlockLM group) {
		groupListLM.add(group);
	}

	public FTSGroupBlockLM getGroupBlockLM(int index) {
		return groupListLM.get(index);
	}
	/**
	 * Generate buffer of values in FTS header. <i>This method generates
	 * specific size of bytes into the buffer.</i>
	 * 
	 * @param resultArray -
	 *            memory bytes for generating buffer
	 * @param startPos -
	 *            starting position in resultArray
	 */
	@Override
	public int generate(byte[] resultArray, int startPos)
			throws PacketGenerateException {
		int length = super.generate(resultArray, startPos);
		length += generateRestFTS(resultArray, startPos + length);
		return length;
	}

	private int generateRestFTS(byte[] buffer, int offset) throws PacketGenerateException {

		assert PacketUtils.checkSize(this.getFTSseqNumber(), 32);
		assert PacketUtils.checkSize(this.getSessionSize(), 32);
//		assert PacketUtils.checkSize(this.getIncreaseFactor(), 16);
//		assert PacketUtils.checkSize(this.getReservedEnd(), 16);

		// adding LMS sequence number
		PacketUtils.setOctet4(buffer, offset, this.getFTSseqNumber());
		// adding session size
		PacketUtils.setOctet4(buffer, offset + 4, this.getSessionSize());
		// adding increase factor
//		PacketUtils.setOctet2(buffer, offset + 8, this.getIncreaseFactor());
		// adding ReservedEnd
//		PacketUtils.setOctet2(buffer, offset + 10, this.getReservedEnd());
		// adding Vector of IP addresses

		int len = 8;
		for (FTSGroupBlockFT groupFT : groupListFT) {
			len += groupFT.generate(buffer, offset+len);
		}
		
		for (FTSGroupBlockLM groupLM : groupListLM) {
			len += groupLM.generate(buffer, offset+len);
		}
		// zbytek hlavicky zabere celkem ... bitu
		return len;
	}

	/**
	 * Parsing FTS header values from buffer. <i>This method takes specific
	 * number of bytes from buffer and parse them into FTS header values. </i>
	 * 
	 * @param resultArray -
	 *            memory bytes for parsing the values
	 * @param startPos -
	 *            starting position in resultArray
	 * @param length -
	 *            length of header
	 */
	@Override
	public int parse(byte[] resultArray, int startPos, int length)
			throws PacketParseException {
		int len = super.parse(resultArray, startPos, length);
		len += this.parseRestFTS(resultArray, len, length);
		return len;
	}

	private int parseRestFTS(byte[] buffer, int offset, int length)
			throws PacketParseException {
		this.setFTSseqNumber(PacketUtils.getOctet4(buffer, 0 + offset));
		this.setSessionSize(PacketUtils.getOctet4(buffer, 4 + offset));
//		this.setIncreaseFactor(PacketUtils.getOctet2(buffer, 12 + offset));
//		this.setReservedEnd(PacketUtils.getOctet2(buffer, 14 + offset));
		int len = 8;
		
/*		for (FTSGroupBlock group : groupList) {
			FTSGroupBlock item = new FTSGroupBlock();
			len+= item.parse(buffer, offset, length);
			this.addGroupBlock(item);
		}
*/
/*		for (int i = 0; i < ((length - 8) / 16); i++) {
			FTSGroupBlock item = new FTSGroupBlock();
			len += item.parse(buffer, offset + len, length);
			this.addGroupBlock(item);
		}
*/
		int restLength = getLength()-12;
		while (restLength > 0)
		{
			//zjisti jaky blok nasleduje a podle toho zvoli typ parsovani
			int key = PacketUtils.getOctet1(buffer, offset + len);
		switch (key) {
		case 1:
			FTSGroupBlockFT itemFT = new FTSGroupBlockFT();
			int lenFT = itemFT.parse(buffer, offset + len, length);
			
			len += lenFT;
			
			restLength -= lenFT;
			this.addGroupBlockFT(itemFT);
			break;
		
		case 3:
			FTSGroupBlockLM itemLM = new FTSGroupBlockLM();
			int lenLM = itemLM.parse(buffer, offset + len, length);
			len += lenLM;
			restLength -= lenLM;
			this.addGroupBlockLM(itemLM);
			break;

		default:
			System.out.println("Problem with reading groups!!!!!!");
			break;
		}
			
		}
		
		return len;
	}

	public long getFTSseqNumber() {
		return FTSseqNumber;
	}

	public void setFTSseqNumber(long seqNumber) {
		FTSseqNumber = seqNumber;
	}

	public long getSessionSize() {
		return sessionSize;
	}

	public void setSessionSize(long sessionSize) {
		this.sessionSize = sessionSize;
	}
/*
	public long getIncreaseFactor() {
		return increaseFactor;
	}

	public void setIncreaseFactor(long increaseFactor) {
		this.increaseFactor = increaseFactor;
	}

	public long getReservedEnd() {
		return reservedEnd;
	}

	public void setReservedEnd(long reservedEnd) {
		this.reservedEnd = reservedEnd;
	}
*/
	/**
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return new ToStringBuilder(this).append("version", this.getVersion())
				.append("sessionSize",
						this.sessionSize).append("packetType",
						this.getPacketType()).append("LMSseqNumber",
						this.FTSseqNumber).append("length", this.getLength())
				.append("feedbackTreeId", this.getFeedbackTreeId()).toString();
	}

	/**
	 * @see java.lang.Object#equals(Object)
	 */
	@Override
	public boolean equals(Object object) {
		if (!(object instanceof FTSPacket)) {
			return false;
		}
		FTSPacket rhs = (FTSPacket) object;
		return new EqualsBuilder().appendSuper(super.equals(object)).append(this.sessionSize, rhs.sessionSize)
				.append(this.FTSseqNumber, rhs.FTSseqNumber).isEquals();
	}

	/**
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode() {
		return new HashCodeBuilder(-1169663993, -969153201).append(this.groupListFT).append(this.groupListLM).append(this.sessionSize).append(
				this.FTSseqNumber).toHashCode();
	}
	
}
