package protocolP2P;
import localException.InternalError;
import localException.ProtocolError;
import localException.SizeError;
import localException.TransmissionError;
import localException.VersionError;
import localException.SocketClosed;
import remoteException.EmptyDirectory;
import remoteException.InternalRemoteError;
import remoteException.NotFound;
import remoteException.ProtocolRemoteError;
import remoteException.VersionRemoteError;
import remoteException.EmptyFile;
import remoteException.NotATracker;
import remoteException.UnknownHost;
import java.io.IOException;
import tools.HostItem;

/** Representation of packet.
 * @author	Louis Royer
 * @author	Flavien Haas
 * @author	JS Auge
 * @version	1.0
 */
public abstract class ProtocolP2PPacket < T extends Payload>{
	private final static byte PROTOCOL_VERSION = 0x12;
	protected final static int VERSION_POSITION = 0;
	protected byte version;
	protected Payload payload;

	/** Constructor with payload parameter (typically used when sending Packet).
	 * @param payload	the payload associated with the Packet to send
	 */
	public ProtocolP2PPacket (T payload) {
		version = PROTOCOL_VERSION;
		this.payload = (Payload)payload;
	}

	/** Send a request
	 * @param socket	Socket used to send packet.
	 * @throws	InternalError
	 * @throws	IOException
	 * @throws	SocketClosed
	 */
	public abstract void sendRequest(Object socket) throws InternalError, IOException, SocketClosed;

	/** Send a response
	 * @param response	Response to send.
	 * @throws	InternalError
	 * @throws	IOException
	 * @throws	SocketClosed
	 */
	public abstract <U extends ProtocolP2PPacket<?>> void sendResponse(U response) throws InternalError, IOException, SocketClosed;

	/** Get hostItem of the sender
	 * @return	hostItem of the sender
	 * @throws	InternalError
	 */
	public abstract HostItem getHostItem() throws InternalError;

	/** Receive a response
	 * @throws	EmptyFile
	 * @throws	NotFound
	 * @throws	NotATracker
	 * @throws	EmptyDirectory
	 * @throws	InternalRemoteError
	 * @throws	VersionRemoteError
	 * @throws	ProtocolRemoteError
	 * @throws	TransmissionError
	 * @throws	ProtocolError
	 * @throws	VersionError
	 * @throws	InternalError
	 * @throws	SizeError
	 * @throws	IOException
	 * @throws	SocketClosed
	 * @throws	UnknownHost
	 */
	public abstract ProtocolP2PPacket<?> receiveResponse() throws EmptyFile, NotFound, NotATracker, EmptyDirectory, InternalRemoteError, VersionRemoteError, ProtocolRemoteError, TransmissionError, ProtocolError, VersionError, InternalError, SizeError, IOException, SocketClosed, UnknownHost;

	/** Receive a request, subclasses must overwrite this constructor.
	 * @param socket	socket used to get the request
	 * @throws	TransmissionError
	 * @throws	ProtocolError
	 * @throws	VersionError
	 * @throws	InternalError
	 * @throws	SizeError
	 * @throws	IOException
	 * @throws	SocketClosed
	 */
	protected ProtocolP2PPacket(Object socket) throws TransmissionError, ProtocolError, VersionError, InternalError, SizeError, IOException, SocketClosed {}

	/** Construct a packet from byte[], subclasses must overwrite this constructor.
	 * @param packet	Packet received
	 * @throws	TransmissionError
	 * @throws	ProtocolError
	 * @throws	VersionError
	 * @throws	InternalError
	 * @throws	SizeError
	 */
	protected ProtocolP2PPacket(byte[] packet) throws TransmissionError, ProtocolError, VersionError, InternalError, SizeError {}

	/** Returns Payload associated with the Packet.
	 * @return	payload associated with the Packet.
	 */
	public Payload getPayload() {
		return payload;
	}

	/** Used to check protocol version when a Packet is constructed from bytes[].
	 * @throws	VersionError
	 */
	protected void checkProtocolVersion() throws VersionError {
		if (PROTOCOL_VERSION != version) {
			System.err.println("Error: wrong version in packet:" + version);
			throw new VersionError();
		}
	}
}
