package protocolP2P;
import protocolP2P.Payload;
import java.util.HashMap;
import java.util.Map;
import localException.TransmissionError;
import localException.SizeError;
import localException.ProtocolError;
import localException.InternalError;
import tools.BytesArrayTools;


/** Representation of payload for hash response.
 * @author	Louis Royer
 * @author	Flavien Haas
 * @author	JS Auge
 * @version	1.0
 */

public class HashResponse extends Payload {
	private String filename;
	private Map<HashAlgorithm, byte[]> hashes = new HashMap<>();
	private static final int FILENAME_SIZE_POSITION = PAYLOAD_START_POSITION;
	private static final int FILENAME_POSITION = FILENAME_SIZE_POSITION + 4;

	/** Constructor (typically used by the server) with a filename parameter.
	 * @param filename	name of the file to download. Must not be empty.
	 * @param hashes	HashMap containing hashes for file.
	 * @throws	InternalError
	 *
	 */
	public HashResponse(String filename, Map<HashAlgorithm, byte[]> hashes) throws InternalError {
		super(RequestResponseCode.HASH_RESPONSE);
		/* assert to help debugging */
		assert filename.length() != 0 : "Size of filename in HashResponse must not be empty";
		if (filename.length() == 0) {
			throw new InternalError();
		}
		this.filename = filename;

		// initialize hashes
		assert !hashes.isEmpty() : "At least 1 hash must be provided";
		if (hashes.isEmpty())  {
			throw new InternalError();
		}
		this.hashes = hashes;
	}

	/** Constructor (typically used by client) with a byte[] parameter containing the Packet received.
	 * @param packet	the full Packet received
	 * @throws	SizeError
	 * @throws	InternalError
	 * @throws	ProtocolError
	 * @throws	TransmissionError
	 */
	protected HashResponse(byte[] packet) throws TransmissionError, SizeError, ProtocolError, InternalError {
		super(packet);
		/* assert to help debugging */
		assert requestResponseCode == RequestResponseCode.HASH_RESPONSE : "HashResponse subclass is incompatible with this Packet, request/response code must be checked before using this constructor";
		/* InternalErrorException */
		if (requestResponseCode!= RequestResponseCode.HASH_RESPONSE) {
			throw new InternalError();
		}
		/* Read filename */
		int filenameSize = BytesArrayTools.readInt(packet, FILENAME_SIZE_POSITION);
		filename = BytesArrayTools.readString(packet, FILENAME_POSITION, filenameSize);

		int size = getPayloadSize(packet);
		int start = FILENAME_POSITION + filenameSize;
		do {
			int algoNameSize = BytesArrayTools.readInt(packet, start);
			start +=4;
			String algoName = BytesArrayTools.readString(packet, start, algoNameSize);
			start += algoNameSize;
			int hashSize = BytesArrayTools.readInt(packet, start);
			start += 4;
			if (hashSize != 0) {
				byte[] b = BytesArrayTools.readByteArray(packet, start, start+hashSize);
				hashes.put(HashAlgorithm.fromName(algoName), b);
			}
			start += hashSize;
		} while (start < size);

	}

	/** 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 size
		int filenameSize = filename.length();
		int size = FILENAME_POSITION + filenameSize;
		for (HashAlgorithm h : hashes.keySet()) {
			size += 4 + h.getName().length();
		}
		for (byte[] s : hashes.values()) {
			size += 4 + s.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 - FILENAME_SIZE_POSITION, packet);
		// write filename size
		BytesArrayTools.write(packet, FILENAME_SIZE_POSITION, filenameSize);
		// write filename
		BytesArrayTools.write(packet, filename, FILENAME_POSITION);
		int bCount = FILENAME_POSITION + filename.length();
		for(HashAlgorithm h : hashes.keySet()) {
			String s = h.getName();
			BytesArrayTools.write(packet, bCount, s.length());
			bCount += 4;
			// write algoname
			BytesArrayTools.write(packet, s, bCount);
			bCount += s.length();
			// write hash size
			byte[] hashb = hashes.get(HashAlgorithm.fromName(s));
			BytesArrayTools.write(packet, bCount, hashb.length);
			bCount += 4;
			if (hashb.length != 0) {
				// write hash
				BytesArrayTools.write(packet, hashb, bCount);
				bCount += hashb.length;
			}
		}
		return packet;
	}

	/** hash getter
	 * @param hashAlgo	HashAlgorithm to return hash from
	 * @return	hash
	 */
	public byte[] getHash(HashAlgorithm hashAlgo) {
		byte[] b = hashes.get(hashAlgo);
		if (b == null) {
			return new byte[0];
		} else {
			return b;
		}
	}

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

}
