//..............................................................................
//
// Sniffer log ARP/InARP packets handling
//

import "io_Arp.jnc"

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

void representArpDigest(
	log.Representation* representation,
	void const* p,
	size_t size
) {
	if (size < sizeof(io.ArpHdr)) {
		representation.addHyperText("\e[1mARP\e[m [INCOMPLETE]");
		return;
	}

	io.ArpHdr const* arpHdr = (io.ArpHdr const*)p;
	size_t arpSize = sizeof(io.ArpHdr) + 2 * (arpHdr.m_hardwareLength + arpHdr.m_protocolLength);
	if (size < arpSize) {
		representation.addHyperText($"\e[1mARP\e[m %1 [BROKEN]"(typeof(io.ArpOpcode).getValueString(arpHdr.m_opcode)));
		return;
	}

	switch (arpHdr.m_opcode) {
	case io.ArpOpcode.Request:
		representation.addHyperText(
			$"\e[1mARP\e[m \e[34m%1\e[m \u2192 request: who-has \e[34m%2"(
			getArpProtocolAddressString(arpHdr, 0),
			getArpProtocolAddressString(arpHdr, 1)
		));
		break;

	case io.ArpOpcode.Reply:
		representation.addHyperText(
			$"\e[1mARP\e[m \e[34m%1\e[m \u2192 \e[34m%2\e[m reply: \e[34m%3"(
			getArpProtocolAddressString(arpHdr, 0),
			getArpProtocolAddressString(arpHdr, 1),
			getArpHardwareAddressString(arpHdr, 0)
		));
		break;

	case io.ArpOpcode.RequestReverse:
		representation.addHyperText(
			$"\e[1mRARP\e[m \e[34m%1\e[m \u2192 request: who-has \e[34m%2"(
			getArpProtocolAddressString(arpHdr, 0),
			getArpHardwareAddressString(arpHdr, 1)
		));

	case io.ArpOpcode.ReplyReverese:
		representation.addHyperText(
			$"\e[1mRARP\e[m \e[34m%1\e[m \u2192 \e[34m%2\e[m reply: \e[34m%3"(
			getArpHardwareAddressString(arpHdr, 0),
			getArpProtocolAddressString(arpHdr, 1),
			getArpProtocolAddressString(arpHdr, 0)
		));

	default:
		representation.addHyperText($"\e[1mARP\e[m [INVALID]");
	}
}

void representArpDetails(
	log.Representation* representation,
	std.Array* protoRangeArray,
	void const* p,
	size_t size,
	size_t baseOffset,
	uint_t foldFlags
) {
	representation.m_lineAttr.m_backColor = ProtoColor.NetworkLayer;

	if (size < sizeof(io.ArpHdr)) {
		representation.addHyperText("\e[1mARP\e[m [INCOMPLETE]");
		return;
	}

	log.representStruct(
		representation,
		typeof(io.ArpHdr),,
		NetSnifferLogPartCode.RawPacket,
		p,
		baseOffset,
		foldFlags,
		ProtoFoldFlag.NetworkLayer
	);

	ProtoRange range = { baseOffset, sizeof(io.ArpHdr), ProtoColor.NetworkLayer }
	protoRangeArray.append(range);

	representArpPayload(representation, protoRangeArray, p, size, baseOffset, foldFlags);
}

void representArpPayload(
	log.Representation* representation,
	std.Array* protoRangeArray,
	void const* p,
	size_t size,
	size_t baseOffset,
	uint_t foldFlags
) {
	io.ArpHdr const* arpHdr = (io.ArpHdr const*)p;
	size_t payloadSize = 2 * (arpHdr.m_hardwareLength + arpHdr.m_protocolLength);
	if (size < sizeof(io.ArpHdr) + payloadSize) {
		representation.addHyperText("\e[1mARP\e[m Payload [INCOMPLETE]");
		return;
	}

	representation.m_lineAttr.m_backColor = ProtoColor.Payload;

	if (foldFlags & ProtoFoldFlag.Payload) {
		representation.addHyperText(
			$"[\e^-%1\e[34m-\e[m] \e[1mARP\e[m Payload\n"
			$"    Sender hardware address: %2\n"
			$"    Sender protocol address: %3\n"
			$"    Target hardware address: %4\n"
			$"    Target protocol address: %5"(
			ProtoFoldFlag.Payload,
			formatArpHardwareAddress(arpHdr, baseOffset, 0),
			formatArpProtocolAddress(arpHdr, baseOffset, 0),
			formatArpHardwareAddress(arpHdr, baseOffset, 1),
			formatArpProtocolAddress(arpHdr, baseOffset, 1)
		));
	} else {
		representation.addHyperText(
			$"[\e^+%1\e[34m+\e[m] \e[1mARP\e[m Payload (%2 bytes)"(
			ProtoFoldFlag.Payload,
			payloadSize
		));
	}

	baseOffset += sizeof(io.ArpHdr);

	ProtoRange range = { baseOffset, payloadSize, ProtoColor.Payload };
	protoRangeArray.append(range);
}

string_t getArpHardwareAddressString(
	io.ArpHdr const* hdr,
	size_t index
) {
	void const* p = hdr + 1;
	p += index * (hdr.m_hardwareLength + hdr.m_protocolLength);

	switch (hdr.m_hardwareType) {
	case io.ArpHardwareType.Ethernet:
		return hdr.m_hardwareLength >= 6 ? io.formatEthernetAddress(p) : "[INCOMPLETE-MAC]";

	default:
		return "[UNKNOWN-HARDWARE]";
	}
}

string_t getArpProtocolAddressString(
	io.ArpHdr const* hdr,
	size_t index
) {
	void const* p = hdr + 1;
	p += index * (hdr.m_hardwareLength + hdr.m_protocolLength) + hdr.m_hardwareLength;

	switch (hdr.m_protocolType) {
	case io.EthernetType.Ip:
		return hdr.m_protocolLength >= sizeof(io.Address_ip4) ? ((io.Address_ip4 const*)p).getString() : "[INCOMPLETE-IPv4]";

	case io.EthernetType.Ip6:
		return hdr.m_protocolLength >= sizeof(io.Address_ip6) ? ((io.Address_ip6 const*)p).getString() : "[INCOMPLETE-IPv6]";

	default:
		return "[UNKNOWN-PROTOCOL]";
	}
}

string_t formatArpHardwareAddress(
	io.ArpHdr const* arpHdr,
	size_t baseOffset,
	size_t index
) {
	size_t offset = sizeof(io.ArpHdr) +  index * (arpHdr.m_hardwareLength + arpHdr.m_protocolLength);

	return $"\e^!%1,%2\e[34m%3\e[m"(
		baseOffset + offset,
		arpHdr.m_hardwareLength,
		getArpHardwareAddressString(arpHdr, index)
	);
}

string_t formatArpProtocolAddress(
	io.ArpHdr const* arpHdr,
	size_t baseOffset,
	size_t index
) {
	size_t offset = sizeof(io.ArpHdr) +  index * (arpHdr.m_hardwareLength + arpHdr.m_protocolLength) + arpHdr.m_hardwareLength;

	return $"\e^!%1,%2\e[34m%3\e[m"(
		baseOffset + offset,
		arpHdr.m_hardwareLength,
		getArpProtocolAddressString(arpHdr, index)
	);
}

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