//..............................................................................
//
// Sniffer log IP packets handling
//

//..............................................................................

char const* formatIpString(bigendian uint32_t ip32)
{
	uint8_t const* ip8 = (uint8_t const*) &ip32;
	return std.format(
		"%d.%d.%d.%d",
		ip8[0], ip8[1], ip8[2], ip8[3]
		);
}

size_t
representIpHdr(
	log.Representation* representation,
	void const* p,
	size_t size,
	size_t baseOffset,
	size_t* hdrSizeTable,
	uint_t* hdrColorTable,
	size_t hdrIdx
	)
{
	io.IpHdr const* ipHdr = (io.IpHdr const*) (p + baseOffset);
	size_t ipHdrSize = ipHdr.m_headerLength * 4;

	representation.addHyperText(std.format(
		"\e[1mIP\n"
		"   \e[90mVERSION:  \e^!%d,%d\e[34m%d\n"
		"   \e[90mHLENGTH:  \e^!%d,%d\e[34m%d\n"
		"   \e[90mTOS:      \e^!%d,%d\e[34m%d\n"
		"   \e[90mTLENGTH:  \e^!%d,%d\e[34m%d\n"
		"   \e[90mID:       \e^!%d,%d\e[34m%d\n"
		"   \e[90mFLAGS:    \e^!%d,%d\e[34m%d\n"
		"   \e[90mFRAGOFFS: \e^!%d,%d\e[34m%d\n"
		"   \e[90mTTL:      \e^!%d,%d\e[34m%d\n"
		"   \e[90mPROTO:    \e^!%d,%d\e[34m%d\n"
		"   \e[90mCHECKSUM: \e^!%d,%d\e[34m%04Xh\n"
		"   \e[90mSRC:      \e^!%d,%d\e[34m%s\n"
		"   \e[90mDST:      \e^!%d,%d\e[34m%s",

		baseOffset + offsetof(ipHdr.m_version),
		sizeof(ipHdr.m_version),
		ipHdr.m_version,

		baseOffset + offsetof(ipHdr.m_headerLength),
		sizeof(ipHdr.m_headerLength),
		ipHdrSize,

		baseOffset + offsetof(ipHdr.m_typeOfService),
		sizeof(ipHdr.m_typeOfService),
		ipHdr.m_typeOfService,

		baseOffset + offsetof(ipHdr.m_totalLength),
		sizeof(ipHdr.m_totalLength),
		ipHdr.m_totalLength,

		baseOffset + offsetof(ipHdr.m_identification),
		sizeof(ipHdr.m_identification),
		ipHdr.m_identification,

		baseOffset + offsetof(ipHdr.m_flags),
		sizeof(ipHdr.m_flags),
		ipHdr.m_flags,

		baseOffset + offsetof(ipHdr.m_fragmentOffset),
		sizeof(ipHdr.m_fragmentOffset),
		ipHdr.m_fragmentOffset,

		baseOffset + offsetof(ipHdr.m_timeToLive),
		sizeof(ipHdr.m_timeToLive),
		ipHdr.m_timeToLive,

		baseOffset + offsetof(ipHdr.m_protocol),
		sizeof(ipHdr.m_protocol),
		ipHdr.m_protocol,

		baseOffset + offsetof(ipHdr.m_headerChecksum),
		sizeof(ipHdr.m_headerChecksum),
		ipHdr.m_headerChecksum,

		baseOffset + offsetof(ipHdr.m_srcAddress),
		sizeof(ipHdr.m_srcAddress),
		formatIpString(ipHdr.m_srcAddress),

		baseOffset + offsetof(ipHdr.m_dstAddress),
		sizeof(ipHdr.m_dstAddress),
		formatIpString(ipHdr.m_dstAddress)
		));

	if	(ipHdrSize < sizeof(io.IpHdr) || baseOffset + ipHdrSize > size)
	{
		representation.addHyperText($"INVALID HEADER SIZE: $ipHdrSize");
		hdrSizeTable[hdrIdx] = sizeof(io.IpHdr);
		return hdrIdx + 1;
	}

	if ((ipHdr.m_flags & io.IpFlags.Mf) || ipHdr.m_fragmentOffset)
	{
		representation.addHyperText("\e[1mFRAGMENTED");
		hdrSizeTable[hdrIdx] = sizeof(io.IpHdr);
		return hdrIdx + 1;
	}

	hdrSizeTable[hdrIdx] = ipHdrSize;
	hdrColorTable[hdrIdx] = SnifferLogHdrColor.Ip;
	hdrIdx++;

	if (ipHdrSize > sizeof(io.IpHdr))
	{
		hdrSizeTable[hdrIdx] = ipHdrSize - sizeof(io.IpHdr);
		hdrColorTable[hdrIdx] = SnifferLogHdrColor.IpOptions;
		hdrIdx++;
	}

	switch (ipHdr.m_protocol)
	{
	case io.IpProtocol.Icmp:
		return representIcmpHdr(representation, p, size, baseOffset + ipHdrSize, hdrSizeTable, hdrColorTable, hdrIdx);

	case io.IpProtocol.Tcp:
		return representTcpHdr(representation, p, size, baseOffset + ipHdrSize, hdrSizeTable, hdrColorTable, hdrIdx);

	case io.IpProtocol.Udp:
		return representUdpHdr(representation, p, size, baseOffset + ipHdrSize, hdrSizeTable, hdrColorTable, hdrIdx);
	}

	return hdrIdx;
}

char const* formatIpLogText(io.IpHdr const* ipHdr)
{
	return $"[\e^+1\e[34m+\e[m] \e[1mIP \e[34m%1\e[m > \e[34m%2\e[m" (
		formatIpString(ipHdr.m_srcAddress),
		formatIpString(ipHdr.m_dstAddress)
		);
}

representIp(
	log.Representation* representation,
	void const* p,
	size_t size,
	size_t baseOffset
	)
{
	if (baseOffset + sizeof(io.IpHdr) > size)
	{
		representation.addHyperText(std.format("[\e^+1\e[34m+\e[m] \e[1mIP\e[m [INVALID SIZE:%d]", size));
		return;
	}

	io.IpHdr const* ipHdr = (io.IpHdr const*) (p + baseOffset);
	size_t ipHdrSize = ipHdr.m_headerLength * 4;
	if (baseOffset + ipHdrSize > size)
	{
		representation.addHyperText(
			$"%1 [PROTO:%2; INVALID HEADER SIZE: %3]" (
				formatIpLogText(ipHdr),
				ipHdr.m_protocol,
				ipHdrSize
				));
		return;
	}

	if ((ipHdr.m_flags & io.IpFlags.Mf) || ipHdr.m_fragmentOffset)
	{
		representation.addHyperText($"%1 [PROTO:%2; FRAGMENTED]" (formatIpLogText(ipHdr), ipHdr.m_protocol));
		return;
	}

	switch (ipHdr.m_protocol)
	{
	case io.IpProtocol.Icmp:
		representIcmp(representation, ipHdr, p, size, baseOffset + ipHdrSize);
		break;

	case io.IpProtocol.Tcp:
		representTcp(representation, ipHdr, p, size, baseOffset + ipHdrSize);
		break;

	case io.IpProtocol.Udp:
		representUdp(representation, ipHdr, p, size, baseOffset + ipHdrSize);
		break;

	default:
		representation.addHyperText($"%1 [PROTO:%2]" (formatIpLogText (ipHdr), ipHdr.m_protocol));
		size_t dataSize = ipHdr.m_totalLength - ipHdrSize;
		representPacketData(representation, p, size, baseOffset + ipHdrSize, dataSize);
	}
}

//..............................................................................

