package protocolP2P;
import protocolP2P.Payload;
import protocolP2P.RequestResponseCode;
import localException.ProtocolError;
import localException.InternalError;
import localException.SizeError;
import localException.TransmissionError;
import tools.BytesArrayTools;

/** Representation of payload for load response.
 * @author	Louis Royer
 * @author	Flavien Haas
 * @author	JS Auge
 * @version	1.0
 */
public class FilePart extends Payload {
	private String filename;
	private long offset;
	private byte[] partialContent;
	static final private int OFFSET_POSITION = PAYLOAD_START_POSITION;
	static final private int FILENAME_SIZE_POSITION = OFFSET_POSITION + 8;
	static final private int FILENAME_POSITION = FILENAME_SIZE_POSITION + 4;

	/** Constructor (typically used by server) with informations about file part to send as parameters.
	 * @param filename	name of the file to send
	 * @param totalSize	total size of the file to send
	 * @param offset	where in the file begins the part we are sending
	 * @param partialContent	content of the file we send
	 * @throws	InternalError
	 */
	public FilePart(String filename, long offset, byte[] partialContent) throws InternalError {
		super(RequestResponseCode.LOAD_RESPONSE);
		/* asserts to help debugging */
		assert partialContent.length != 0 : "partialContent.length cannot be zero, see RRCode.EMPTY_FILE";
		assert offset >= 0 : "offset cannot be negative";
		assert filename != null : "filename is required";
		if (partialContent.length == 0 || offset < 0 || filename == null) {
			throw new InternalError();
		}
		this.filename = filename;
		this.offset = offset;
		this.partialContent = partialContent;
	}

	/** Constructor (typically used by client) with Packet received as parameter.
	 * @param packet	the full Packet received
	 * @throws	SizeError
	 * @throws	InternalError
	 * @throws	TransmissionError
	 */
	protected FilePart(byte[] packet) throws TransmissionError, SizeError, ProtocolError, InternalError {
		super(packet);
		/* assert to help debugging */
		assert requestResponseCode == RequestResponseCode.LOAD_RESPONSE : "FilePart subclass is incompatible with this Packet, request/response code must be checked before using this constructor";
		/* InternalErrorException */
		if (requestResponseCode != RequestResponseCode.LOAD_RESPONSE) {
			throw new InternalError();
		}
		setOffset(packet); // this can throw SizeError
		setFilename(packet); // this can throw ProtocolError, SizeError
		setPartialContent(packet); // this can throw SizeError
	}

	/** Returns a byte[] containing Packet with padding.
	 * This Packet is still incomplete and should not be send directly.
	 * ProtocolP2PPacket will use this method to generate the complete Packet.
	 * @return Packet with padding
	 * @throws	InternalError
	 */
	protected byte[] toPacket() throws InternalError {
		// compute total size
		int size = FILENAME_POSITION + filename.length() + partialContent.length;
		byte[] packet = new byte[size]; // java initialize all to zero
		// set request/response code
		packet[RequestResponseCode.RRCODE_POSITION] = requestResponseCode.codeValue;
		// set Payload size
		setPayloadSize(size - PAYLOAD_START_POSITION, packet);
		// write offset to Packet
		BytesArrayTools.write(packet, OFFSET_POSITION, offset);
		// write filename’s size to Packet
		BytesArrayTools.write(packet, FILENAME_SIZE_POSITION, filename.length());
		// write filename to Packet
		BytesArrayTools.write(packet, filename, FILENAME_POSITION);
		// write partialContent to  Packet
		BytesArrayTools.write(packet, partialContent, FILENAME_POSITION + filename.length());
		return packet;
	}

	/** Write from Packet into offset.
	 * @param packet	received Packet
	 * @throws	SizeError
	 */
	private void setOffset(byte[] packet) throws SizeError {
		offset = BytesArrayTools.readLong(packet, OFFSET_POSITION);
	}

	/** Read filename’s size from Packet.
	 * @param packet	received Packet
	 * @throws	ProtocolError
	 * @throws	SizeError
	 * @return	filename’s size
	 */
	private int getFilenameSize(byte[] packet) throws SizeError, ProtocolError {
		int size = BytesArrayTools.readInt(packet, FILENAME_SIZE_POSITION); // this can throw SizeError
		// filename size cannot be zero
		if (size == 0) {
			throw new ProtocolError();
		}
		// cannot excess packet size
		if ((FILENAME_POSITION + size) > (getPayloadSize(packet) + OFFSET_POSITION)) {
			throw new ProtocolError();
		}
		return size;
	}

	/** Write from Packet into filename.
	 * @param packet	received Packet
	 * @throws	ProtocolError
	 * @throws	SizeError
	 * @throws	InternalError
	 */
	private void setFilename(byte[] packet) throws ProtocolError, SizeError, InternalError {
		int filenameSize = getFilenameSize(packet); // this can throw ProtocolError or SizeError
		filename = BytesArrayTools.readString(packet, FILENAME_POSITION, filenameSize);
	}

	/** Write from Packet into partialContent.
	 * @param packet	received Packet
	 * @throws	SizeError
	 * @throws	ProtocolError
	 */
	private void setPartialContent(byte[] packet) throws ProtocolError, SizeError {
		int start = FILENAME_POSITION + getFilenameSize(packet); // this can throw SizeError or ProtocolError
		int end = OFFSET_POSITION + getPayloadSize(packet); // this can throw SizeError
		partialContent = BytesArrayTools.readByteArray(packet, start, end);
	}

	/** partialContent getter.
	 * @return	partialcontent
	 */
	public byte[] getPartialContent() {
		return partialContent;
	}

	/** filename getter.
	 * @return	String
	 */
	public String getFilename() {
		return filename;
	}

	/** offset getter.
	 * @return	offset
	 */
	public long getOffset() {
		return offset;
	}
}
