//..............................................................................
//
// ModBus Protocol Analyzer log record declarations and representer
//

import "log.jnc"
import "io_ModBus.jnc"

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

representModBusCoils (
	log.RepresenterTarget* target,
	uint8_t const* p,
	size_t count,
	size_t baseOffset,
	uint_t foldFlags
	)
{
	switch (count)
	{
	case 0:
		target.addHyperText ("Value:     [\e[31mNONE\e[m]");
		break;

	case 1:
		target.addHyperText (
			$"Value:     \e^!-%1,%2\e[34m%3\e[m" (
			baseOffset,
			sizeof (uint8_t),
			*p & 1
			));
		break;

	case 2:
	case 3:
	case 4:
		target.addHyperText ($"Values [$count]");
		representModBusCoilValues (target, p, count, baseOffset);
		break;

	default:
		if (foldFlags)
		{
			target.addHyperText ($"[\e^-1\e[34m-\e[m] Values [$count]");
			representModBusCoilValues (target, p, count, baseOffset);
		}
		else
		{
			target.addHyperText ($"[\e^+1\e[34m+\e[m] Values [$count]");
		}
	}
}

representModBusCoilValues (
	log.RepresenterTarget* target,
	uint8_t const* p,
	size_t count,
	size_t baseOffset
	)
{
	size_t size = count / 8;
	if (count & 7)
		size++;

	size_t i = 0;
	for (size_t j = 0; i < count && j < size; j++)
	{
		uint8_t c = p [j];

		for (uint8_t mask = 1; i < count && mask; i++, mask <<= 1)
		{
			bool value = (c & mask) != 0;
			target.addHyperText (
				$"    #%(1; -3d): \e^!-%2,%3\e[34m%4\e[m" (
				i,
				baseOffset + j,
				sizeof (uint8_t),
				value
				));
		}
	}
}

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

representModBusRegisters (
	log.RepresenterTarget* target,
	bigendian uint16_t const* p,
	size_t count,
	size_t baseOffset,
	uint_t foldFlags
	)
{
	switch (count)
	{
	case 0:
		target.addHyperText ("Value:     [\e[31mNONE\e[m]");
		break;

	case 1:
		target.addHyperText (
			$"Value:     \e^!-%1,%2\e[34m0x%(3; 04X)/%3\e[m" (
			baseOffset,
			sizeof (uint16_t),
			p [0]
			));
		break;

	case 2:
	case 3:
	case 4:
		target.addHyperText ($"Values [$count]");
		representModBusRegisterValues (target, p, count, baseOffset);
		break;

	default:
		if (foldFlags)
		{
			target.addHyperText ($"[\e^-1\e[34m-\e[m] Values [$count]");
			representModBusRegisterValues (target, p, count, baseOffset);
		}
		else
		{
			target.addHyperText ($"[\e^+1\e[34m+\e[m] Values [$count]");
		}
	}
}

representModBusRegisterValues (
	log.RepresenterTarget* target,
	bigendian uint16_t const* p,
	size_t count,
	size_t baseOffset
	)
{
	printf ("representModBusRegisterValues: count: %d\n", count);

	for (size_t i = 0; i < count; i++)
		target.addHyperText (
			$"    #%(1; -3d): \e^!-%2,%3\e[34m0x%(4; 04X)/%4\e[m" (
			i,
			baseOffset + i * sizeof (uint16_t),
			sizeof (uint16_t),
			p [i]
			));
}

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

representModBusPdu (
	log.RepresenterTarget* target,
	io.ModBusRole role,
	io.ModBusPduHdr const* hdr,
	size_t size,
	size_t baseOffset,
	uint_t foldFlags
	)
{
	target.addHyperText (
		$"Function:  \e^!-%1,%2\e[34m0x%(3; 02X)/%3\e[m - %4" (
		baseOffset + offsetof (hdr.m_func),
		sizeof (hdr.m_func),
		hdr.m_func,
		io.getModBusFuncString (hdr.m_func)
		));

	switch (hdr.m_func)
	{
	case io.ModBusFunc.ReadCoils:
	case io.ModBusFunc.ReadDiscreteInputs:
	case io.ModBusFunc.ReadHoldingRegisters:
	case io.ModBusFunc.ReadInputRegisters:
		if (role == io.ModBusRole.Master)
		{
			io.ModBusReadPdu const* pdu = (io.ModBusReadPdu const*) hdr;
			target.addHyperText (
				$"Address:   \e^!-%1,%2\e[34m0x%(3; 04X)/%3\e[m\n"
				$"Count:     \e^!-%4,%5\e[34m%6\e[m" (

				baseOffset + offsetof (pdu.m_address),
				sizeof (pdu.m_address),
				pdu.m_address,

				baseOffset + offsetof (pdu.m_count),
				sizeof (pdu.m_count),
				pdu.m_count
				));
		}
		else
		{
			io.ModBusReadReplyPdu const* pdu = (io.ModBusReadReplyPdu const*) hdr;

			if (hdr.m_func <= io.ModBusFunc.ReadDiscreteInputs)
			{
				target.addHyperText (
					$"Size:      \e^!-%1,%2\e[34m%3\e[m" (
					baseOffset + offsetof (pdu.m_size),
					sizeof (pdu.m_size),
					pdu.m_size
					));

				representModBusCoils (
					target,
					(uint8_t const*) (pdu + 1),
					pdu.m_size * 8,
					baseOffset + sizeof (io.ModBusReadReplyPdu),
					foldFlags
					);
			}
			else
			{
				target.addHyperText (
					$"Size:      \e^!-%1,%2\e[34m%3\e[m%4" (
					baseOffset + offsetof (pdu.m_size),
					sizeof (pdu.m_size),
					pdu.m_size,
					(pdu.m_size & 1) ? " [\e[31mINVALID\e[m] \e[90m(expected: a multiple of 2)" : ""
					));

				representModBusRegisters (
					target, (bigendian uint16_t const*) (pdu + 1),
					pdu.m_size / 2,
					baseOffset + sizeof (io.ModBusReadReplyPdu),
					foldFlags
					);
			}
		}
		break;

	case io.ModBusFunc.WriteCoil:
	case io.ModBusFunc.WriteRegister:
		io.ModBusWritePdu const* pdu = (io.ModBusWritePdu const*) hdr;
		target.addHyperText (
			$"Address:   \e^!-%1,%2\e[34m0x%(3; 04X)/%3\e[m\n"
			$"Value:     \e^!-%4,%5\e[34m0x%(6; 04X)/%6\e[m" (

			baseOffset + offsetof (pdu.m_address),
			sizeof (pdu.m_address),
			pdu.m_address,

			baseOffset + offsetof (pdu.m_value),
			sizeof (pdu.m_value),
			pdu.m_value
			));
		break;

	case io.ModBusFunc.WriteMultipleCoils:
	case io.ModBusFunc.WriteMultipleRegisters:
		if (role == io.ModBusRole.Master)
		{
			io.ModBusWriteMultiplePdu const* pdu = (io.ModBusWriteMultiplePdu const*) hdr;
			target.addHyperText (
				$"Address:   \e^!-%1,%2\e[34m0x%(3; 04X)/%3\e[m\n"
				$"Count:     \e^!-%4,%5\e[34m%6\e[m\n"
				$"Size:      \e^!-%7,%8\e[34m%9\e[m" (

				baseOffset + offsetof (pdu.m_address),
				sizeof (pdu.m_address),
				pdu.m_address,

				baseOffset + offsetof (pdu.m_count),
				sizeof (pdu.m_count),
				pdu.m_count,

				baseOffset + offsetof (pdu.m_size),
				sizeof (pdu.m_size),
				pdu.m_size
				));

			if (hdr.m_func == io.ModBusFunc.WriteMultipleCoils)
				representModBusCoils (
					target,
					(uint8_t const*) (pdu + 1),
					pdu.m_count,
					baseOffset + sizeof (io.ModBusWriteMultiplePdu),
					foldFlags
					);
			else
				representModBusRegisters (
					target,
					(bigendian uint16_t const*) (pdu + 1),
					pdu.m_count,
					baseOffset + sizeof (io.ModBusWriteMultiplePdu),
					foldFlags
					);
		}
		else
		{
			io.ModBusWriteMultipleReplyPdu const* pdu = (io.ModBusWriteMultipleReplyPdu const*) hdr;
			target.addHyperText (
				$"Address:   \e^!-%1,%2\e[34m0x%(3; 04X)/%3\n"
				$"Count:     \e^!-%4,%5\e[34m%6" (

				baseOffset + offsetof (pdu.m_address),
				sizeof (pdu.m_address),
				pdu.m_address,

				baseOffset + offsetof (pdu.m_count),
				sizeof (pdu.m_count),
				pdu.m_count
				));
		}
		break;

	case io.ModBusFunc.ReadExceptionStatus:
	case io.ModBusFunc.Diagnostics:
	case io.ModBusFunc.GetCommEventCounter:
	case io.ModBusFunc.GetCommEventLog:
	case io.ModBusFunc.ReportServerId:
	case io.ModBusFunc.ReadFileRecord:
	case io.ModBusFunc.WriteFileRecord:
	case io.ModBusFunc.MaskWriteRegister:
	case io.ModBusFunc.ReadWriteMultipleRegisters:
	case io.ModBusFunc.ReadFifoQueue:
	case io.ModBusFunc.EncapsulatedInterface:
		break; // not supported yet

	default:
		if (hdr.m_func & io.ModBusFunc.ExceptionFlag)
		{
			io.ModBusExceptionCode exception = (io.ModBusExceptionCode) (hdr.m_func & ~io.ModBusFunc.ExceptionFlag);

			target.addHyperText (
				$"Exception: \e^!-%1,%2\e[31m0x%(3; 02X)/%3\e[m - %4" (
				baseOffset + offsetof (hdr.m_func),
				sizeof (hdr.m_func),
				exception,
				io.getModBusFuncString (exception)
				));
		}
	}
}

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