#define LOG_MODULE PacketLogModuleSdpLayer


#include "SdpLayer.h"
#include "Logger.h"
#include <stdlib.h>
#include <string>
#include <sstream>

namespace pcpp
{

std::vector<std::string> splitByWhiteSpaces(const std::string& str)
{
	std::string buf;
	std::stringstream stream(str);
	std::vector<std::string> result;
	while (stream >> buf)
		result.push_back(buf);

	return result;
}


SdpLayer::SdpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : TextBasedProtocolMessage(data, dataLen, prevLayer, packet)
{
	m_Protocol = SDP;
	m_FieldsOffset = 0;
	parseFields();
}

SdpLayer::SdpLayer()
{
	m_Protocol = SDP;
	m_FieldsOffset = 0;
}

SdpLayer::SdpLayer(const std::string& username, long sessionID, long sessionVersion, IPv4Address ipAddress, const std::string& sessionName, long startTime, long stopTime)
{
	m_Protocol = SDP;
	m_FieldsOffset = 0;

	// must initialize m_Data otherwise addField() will fail while trying to extend the layer
	// initializing in length of 1 but keeping m_DataLen with value of 0.
	// when extending the field m_Data is purged so there isn't a memory leak here
	m_Data = new uint8_t[1];
	m_DataLen = 0;

	addField(PCPP_SDP_PROTOCOL_VERSION_FIELD, "0");

	std::stringstream sessionIDStream;
	sessionIDStream << sessionID;
	std::stringstream sessionVersionStream;
	sessionVersionStream << sessionVersion;
	std::string networkInfo = "IN IP4 " + ipAddress.toString();
	std::string originatorFieldValue = username + " " + sessionIDStream.str() + " " + sessionVersionStream.str() + " " + networkInfo;
	addField(PCPP_SDP_ORIGINATOR_FIELD, originatorFieldValue);

	addField(PCPP_SDP_SESSION_NAME_FIELD, sessionName);

	addField(PCPP_SDP_CONNECTION_INFO_FIELD, networkInfo);

	std::stringstream startTimeStream;
	startTimeStream << startTime;
	std::stringstream stopTimeStream;
	stopTimeStream << stopTime;
	addField(PCPP_SDP_TIME_FIELD, startTimeStream.str() + " " + stopTimeStream.str());
}

std::string SdpLayer::toString() const
{
	return "SDP Layer";
}

IPv4Address SdpLayer::getOwnerIPv4Address() const
{
	HeaderField* originator = getFieldByName(PCPP_SDP_ORIGINATOR_FIELD);
	if (originator == nullptr)
		return IPv4Address::Zero;

	std::vector<std::string> tokens = splitByWhiteSpaces(originator->getFieldValue());
	if (tokens.size() < 6)
		return IPv4Address::Zero;

	if (tokens[3] != "IN" || tokens[4] != "IP4")
		return IPv4Address::Zero;

	return IPv4Address(tokens[5]);
}

uint16_t SdpLayer::getMediaPort(const std::string& mediaType) const
{
	int mediaFieldIndex = 0;
	HeaderField* mediaDesc = getFieldByName(PCPP_SDP_MEDIA_NAME_FIELD, mediaFieldIndex);

	while (mediaDesc != nullptr)
	{
		std::vector<std::string> tokens = splitByWhiteSpaces(mediaDesc->getFieldValue());

		if (tokens.size() >= 2 && tokens[0] == mediaType)
			return atoi(tokens[1].c_str());

		mediaFieldIndex++;
		mediaDesc = getFieldByName(PCPP_SDP_MEDIA_NAME_FIELD, mediaFieldIndex);
	}

	return 0;
}

bool SdpLayer::addMediaDescription(const std::string& mediaType, uint16_t mediaPort, const std::string& mediaProtocol, const std::string& mediaFormat, std::vector<std::string> mediaAttributes)
{
	std::stringstream portStream;
	portStream << mediaPort;

	std::string mediaFieldValue = mediaType + " " + portStream.str() + " " + mediaProtocol + " " + mediaFormat;
	if (addField(PCPP_SDP_MEDIA_NAME_FIELD, mediaFieldValue) == nullptr)
	{
		PCPP_LOG_ERROR("Failed to add media description field");
		return false;
	}


	for (std::vector<std::string>::iterator iter = mediaAttributes.begin(); iter != mediaAttributes.end(); iter++)
	{
		if (addField(PCPP_SDP_MEDIA_ATTRIBUTE_FIELD, *iter) == nullptr)
		{
			PCPP_LOG_ERROR("Failed to add media attribute '" << *iter << "'");
			return false;
		}
	}

	return true;
}


}
