#ifndef PACKETPP_LAYER
#define PACKETPP_LAYER

#include <stdint.h>
#include <stdio.h>
#include "ProtocolType.h"
#include <string>

/// @file

/**
 * \namespace pcpp
 * \brief The main namespace for the PcapPlusPlus lib
 */
namespace pcpp
{

	/**
	 * @class IDataContainer
	 * An interface (virtual abstract class) that indicates an object that holds a pointer to a buffer data. The Layer class is an example
	 * of such object, hence it inherits this interface
	 */
	class IDataContainer
	{
	public:
		/**
		 * Get a pointer to the data
		 * @param[in] offset Get a pointer in a certain offset. Default is 0 - get a pointer to start of data
		 * @return A pointer to the data
		 */
		virtual uint8_t* getDataPtr(size_t offset = 0) const = 0;

		virtual ~IDataContainer() {}
	};

	class Packet;

	/**
	 * @class Layer
	 * Layer is the base class for all protocol layers. Each protocol supported in PcapPlusPlus has a class that inherits Layer.
	 * The protocol layer class expose all properties and methods relevant for viewing and editing protocol fields.
	 * For example: a pointer to a structured header (e.g tcphdr, iphdr, etc.), protocol header size, payload size, compute
	 * fields that can be automatically computed, print protocol data to string, etc.
	 * Each protocol instance is obviously part of a protocol stack (which construct a packet). This protocol stack is represented
	 * in PcapPlusPlus in a linked list, and each layer is an element in this list. That's why each layer has properties to the next and previous
	 * layer in the protocol stack
	 * The Layer class, as a base class, is abstract and the user can't create an instance of it (it has a private constructor)
	 * Each layer holds a pointer to the relevant place in the packet. The layer sees all the data from this pointer forward until the
	 * end of the packet. Here is an example packet showing this concept:
	 *
	 @verbatim
	 ====================================================
	 |Eth       |IPv4       |TCP       |Packet          |
	 |Header    |Header     |Header    |Payload         |
	 ====================================================

	 |--------------------------------------------------|
 	 EthLayer data
	            |---------------------------------------|
	            IPv4Layer data
	                        |---------------------------|
	                        TcpLayer data
	                                   |----------------|
	                                   PayloadLayer data
	 @endverbatim
	 *
	*/
	class Layer : public IDataContainer
	{
		friend class Packet;
	public:
		/**
		 * A destructor for this class. Frees the data if it was allocated by the layer constructor (see isAllocatedToPacket() for more info)
		 */
		virtual ~Layer();

		/**
		 * @return A pointer to the next layer in the protocol stack or NULL if the layer is the last one
		 */
		Layer* getNextLayer() const { return m_NextLayer; }

		/**
		 * @return A pointer to the previous layer in the protocol stack or NULL if the layer is the first one
		 */
		Layer* getPrevLayer() const { return m_PrevLayer; }

		/**
		 * @return The protocol enum
		 */
		ProtocolType getProtocol() const { return m_Protocol; }

		/**
		 * @return A pointer to the layer raw data. In most cases it'll be a pointer to the first byte of the header
		 */
		uint8_t* getData() const { return m_Data; }

		/**
		 * @return The length in bytes of the data from the first byte of the header until the end of the packet
		 */
		size_t getDataLen() const { return m_DataLen; }

		/**
		 * @return A pointer for the layer payload, meaning the first byte after the header
		 */
		uint8_t* getLayerPayload() const { return m_Data + getHeaderLen(); }

		/**
		 * @return The size in bytes of the payload
		 */
		size_t getLayerPayloadSize() const { return m_DataLen - getHeaderLen(); }

		/**
		 * Raw data in layers can come from one of sources:
		 * 1. from an existing packet - this is the case when parsing packets received from files or the network. In this case the data was
		 * already allocated by someone else, and layer only holds the pointer to the relevant place inside this data
		 * 2. when creating packets, data is allocated when layer is created. In this case the layer is responsible for freeing it as well
		 *
		 * @return Returns true if the data was allocated by an external source (a packet) or false if it was allocated by the layer itself
		 */
		bool isAllocatedToPacket() const { return m_Packet != NULL; }

		/**
		 * Copy the raw data of this layer to another array
		 * @param[out] toArr The destination byte array
		 */
		void copyData(uint8_t* toArr) const;

		// implement abstract methods

		uint8_t* getDataPtr(size_t offset = 0) const { return (uint8_t*)(m_Data + offset); }


		// abstract methods

		/**
		 * Each layer is responsible for parsing the next layer
		 */
		virtual void parseNextLayer() = 0;

		/**
		 * @return The header length in bytes
		 */
		virtual size_t getHeaderLen() const = 0;

		/**
		 * Each layer can compute field values automatically using this method. This is an abstract method
		 */
		virtual void computeCalculateFields() = 0;

		/**
		 * @return A string representation of the layer most important data (should look like the layer description in Wireshark)
		 */
		virtual std::string toString() const = 0;

		/**
		 * @return The OSI Model layer this protocol belongs to
		 */
		virtual OsiModelLayer getOsiModelLayer() const = 0;

	protected:
		uint8_t* m_Data;
		size_t m_DataLen;
		Packet* m_Packet;
		ProtocolType m_Protocol;
		Layer* m_NextLayer;
		Layer* m_PrevLayer;
		bool m_IsAllocatedInPacket;

		Layer() : m_Data(NULL), m_DataLen(0), m_Packet(NULL), m_Protocol(UnknownProtocol), m_NextLayer(NULL), m_PrevLayer(NULL), m_IsAllocatedInPacket(false) { }

		Layer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) :
			m_Data(data), m_DataLen(dataLen),
			m_Packet(packet), m_Protocol(UnknownProtocol),
			m_NextLayer(NULL), m_PrevLayer(prevLayer), m_IsAllocatedInPacket(false) {}

		// Copy c'tor
		Layer(const Layer& other);
		Layer& operator=(const Layer& other);

		void setNextLayer(Layer* nextLayer) { m_NextLayer = nextLayer; }
		void setPrevLayer(Layer* prevLayer) { m_PrevLayer = prevLayer; }

		virtual bool extendLayer(int offsetInLayer, size_t numOfBytesToExtend);
		virtual bool shortenLayer(int offsetInLayer, size_t numOfBytesToShorten);
	};

} // namespace pcpp

inline std::ostream& operator<<(std::ostream& os, const pcpp::Layer &layer)
{
	os << layer.toString();
	return os;
}

#endif /* PACKETPP_LAYER */
