//..............................................................................
//
// Some standard checksums collected via selection processors
//

import "ui_ChecksumInfoSet.jnc"
import "crc16.jnc"
import "crc32.jnc"

namespace log {

//! \addtogroup common-log
//! @{

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

class ChecksumCalcRangeProcessor: RangeProcessor
{
protected:
	ui.ChecksumInfoSet* m_infoSet;
	uint64_t m_sum8;
	uint64_t m_sum16_le;
	uint64_t m_sum16_be;
	uint16_t m_crc16_ansi;
	uint16_t m_crc16_modbus;
	uint16_t m_crc16_xmodem;
	uint32_t m_crc32;
	uint32_t m_ip4Checksum;
	size_t m_totalSize;

public:
	static ChecksumCalcRangeProcessor* factory(ui.ChecksumInfoSet* infoSet)
	{
		return new ChecksumCalcRangeProcessor(infoSet);
	}

protected:
	construct(ui.ChecksumInfoSet* infoSet)
	{
		m_infoSet = infoSet;
		m_crc16_modbus = -1;
	}

	override processBin(
		uint64_t partCode,
		void const* p,
		size_t size
		);

	override processEndOfRange();
}

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

void ChecksumCalcRangeProcessor.processBin(
	uint64_t partCode,
	void const* p,
	size_t size
	)
{
	m_sum8 = sum8(p, size, m_sum8);

	m_crc16_ansi = crc16_ansi(p, size, m_crc16_ansi);
	m_crc16_modbus = crc16_ansi(p, size, m_crc16_modbus);
	m_crc16_xmodem = crc16_ccitt(p, size, m_crc16_xmodem);
	m_crc32 = crc32(p, size, m_crc32);

	// do all 16-bit calculations in a single loop

	if (m_totalSize & 1)
	{
		uint16_t be = *(uint8_t const*) p;
		uint16_t le = be << 8;

		m_sum16_le += le;
		m_sum16_be += be;
		m_ip4Checksum += be;

		if (m_ip4Checksum & 0x10000)
			m_ip4Checksum -= 0xffff;

		p++;
		size--;
		m_totalSize++;
	}

	void const* end = p + (size & ~1);

	for (; p < end; p += 2)
	{
		uint16_t le = *(uint16_t const*) p;
		uint16_t be = *(bigendian uint16_t const*) p;

		m_sum16_le += le;
		m_sum16_be += be;
		m_ip4Checksum += be;

		if (m_ip4Checksum & 0x10000)
			m_ip4Checksum -= 0xffff;
	}

	if (size & 1)
	{
		uint16_t le = *(uint8_t const*) p;
		uint16_t be = le << 8;

		m_sum16_le += le;
		m_sum16_be += be;
		m_ip4Checksum += be;

		if (m_ip4Checksum & 0x10000)
			m_ip4Checksum -= 0xffff;
	}

	m_totalSize += size;
}

void ChecksumCalcRangeProcessor.processEndOfRange()
{
	if (m_infoSet.m_crc16Info_ansi)
		m_infoSet.m_crc16Info_ansi.m_value = $"$(m_crc16_ansi; 04X)";

	if (m_infoSet.m_crc16Info_modbus)
		m_infoSet.m_crc16Info_modbus.m_value = $"$(m_crc16_modbus; 04X)";

	if (m_infoSet.m_crc16Info_xmodem)
		m_infoSet.m_crc16Info_xmodem.m_value = $"$(m_crc16_xmodem; 04X)";

	if (m_infoSet.m_crc16Info_usb)
		m_infoSet.m_crc16Info_usb.m_value = $"$(~m_crc16_modbus & 0xffff; 04X)";

	if (m_infoSet.m_crc32Info)
		m_infoSet.m_crc32Info.m_value = $"$(m_crc32; 08X)";

	if (m_infoSet.m_ip4ChecksumInfo)
		m_infoSet.m_ip4ChecksumInfo.m_value = $"$(~m_ip4Checksum & 0xffff; 04X)";

	if (m_infoSet.m_sum8Info)
		m_infoSet.m_sum8Info.m_value = $"$(m_sum8; 02X)";

	if (m_infoSet.m_sum16Info_le)
		m_infoSet.m_sum16Info_le.m_value = $"$(m_sum16_le; 04X)";

	if (m_infoSet.m_sum16Info_be)
		m_infoSet.m_sum16Info_be.m_value = $"$(m_sum16_be; 04X)";
}

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

//! @}

} // namespace log


//! \addtogroup common-crc
//! @{

// simple helper

uint64_t sum8(
	void const* p,
	size_t size,
	uint64_t seed = 0
	)
{
	uint8_t const* b = (uint8_t const*) p;
	uint32_t sum = seed;

	for (; size; size--)
		sum += *b++;

	return sum;
}

//! @}
