//..............................................................................
//
//  Device Monitor machine interface transport for SerialMonProcessor
//

import "io_devmon.jncx"
import "io_DeviceMonitorMi.jnc"
import "SerialMonProcessor_win.jnc"
import "SerialMonProcessor_lnx.jnc"
import "std_Buffer.jnc"

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

class SerialMonitor_mi
{
protected:
	log.Writer* m_logWriter;
	SerialMonProcessor* m_processor;
	size_t m_expectedMsgSize;

public:
	std.Buffer readonly m_buffer;
	size_t readonly m_msgCount;

public:
	construct(log.Writer* logWriter)
	{
		m_logWriter = logWriter;
		reset();
	}

	reset();

	bool errorcode write(
		void const* p,
		size_t size
		);

protected:
	bool errorcode processMsg(io.DeviceMonitorMiMsgHdr const* hdr);

	void handleUndefinedMessageCode(
		void const* p,
		size_t size
		);
}

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

SerialMonitor_mi.reset()
{
	m_expectedMsgSize = sizeof(io.DeviceMonitorMiMsgHdr);
	m_buffer.clear();
	m_processor = null;
	m_msgCount = 0;
}

bool errorcode SerialMonitor_mi.write(
	void const* p,
	size_t size
	)
{
	while (size)
	{
		if (m_buffer.m_size < m_expectedMsgSize)
		{
			size_t leftover = m_expectedMsgSize - m_buffer.m_size;
			if (size < leftover)
			{
				m_buffer.append(p, size);
				break;
			}

			m_buffer.append(p, leftover);
			p += leftover;
			size -= leftover;
		}

		io.DeviceMonitorMiMsgHdr const* hdr = (io.DeviceMonitorMiMsgHdr const*) m_buffer.m_p;
		if (hdr.m_code > io.DeviceMonitorMiMsgCode._Last)
		{
			handleUndefinedMessageCode(p, size);
			return false;
		}

		size_t msgSize = sizeof(io.DeviceMonitorMiMsgHdr) + hdr.m_paramSize;
		if (m_expectedMsgSize < msgSize)
		{
			m_expectedMsgSize = msgSize;
			continue;
		}

		processMsg(hdr);
		m_expectedMsgSize = sizeof(io.DeviceMonitorMiMsgHdr);
		m_buffer.clear();
	}

	return true;
}

bool errorcode SerialMonitor_mi.processMsg(io.DeviceMonitorMiMsgHdr const* hdr)
{
	switch (hdr.m_code)
	{
	case io.DeviceMonitorMiMsgCode.Error:
		std.Error const* error = (std.Error const*) (hdr + 1);
		if (hdr.m_paramSize < sizeof(std.Error) ||
			hdr.m_paramSize < error.m_size)
		{
			std.setError("tdevmon MI error: ERROR message too small");
			return false;
		}

		std.setError(error);
		return false;

	case io.DeviceMonitorMiMsgCode.Start:
		if (hdr.m_paramSize < sizeof(io.DeviceMonitorMiStartParams))
		{
			std.setError("tdevmon MI error: START message too small");
			return false;
		}

		io.DeviceMonitorMiStartParams const* params = (io.DeviceMonitorMiStartParams const*) (hdr + 1);
		if (params.m_flags & io.DeviceMonitorMiStartFlags.Windows)
			m_processor = new SerialMonProcessor_win(m_logWriter);
		else if (params.m_flags & io.DeviceMonitorMiStartFlags.Linux)
			m_processor = new SerialMonProcessor_lnx(m_logWriter);
		else
		{
			std.setError("unsupported target OS");
			return false;
		}

		break;

	case io.DeviceMonitorMiMsgCode.Notification:
		if (!m_processor)
		{
			std.setError("tdevmon MI error: NOTIFY before START");
			return false;
		}

		io.DeviceMonitorNotifyHdr const* notifyHdr = (io.DeviceMonitorNotifyHdr const*) (hdr + 1);
		if (hdr.m_paramSize < sizeof(io.DeviceMonitorNotifyHdr) ||
			hdr.m_paramSize < sizeof(io.DeviceMonitorNotifyHdr) + notifyHdr.m_paramSize)
		{
			std.setError("tdevmon MI error: NOTIFY message too small");
			return false;
		}

		m_processor.processNotification(notifyHdr);
		break;

	default:
		handleUndefinedMessageCode(null, 0);
		return false;
	}

	m_msgCount++;
	return true;
}

void SerialMonitor_mi.handleUndefinedMessageCode(
	void const* p,
	size_t size
	)
{
	if (m_buffer.m_size)
		m_logWriter.write(log.StdRecordCode.Bin, m_buffer.m_p, m_buffer.m_size);

	if (size)
		m_logWriter.write(log.StdRecordCode.Bin, p, size);

	std.setError("tdevmon MI error: invalid message");
}

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