//..............................................................................
//
// Serial log packet declarations and representer
//

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

enum SerialLogRecordCode
{
	PortOpened     = 0 | log.RecordCodeFlags.Foldable,
	PortOpened_old = 1 | log.RecordCodeFlags.Foldable,
	PortOpenError  = 2,
	PortClosed,
	BaudRateChanged,
	DataBitsChanged,
	StopBitsChanged,
	ParityChanged,
	FlowControlChanged,
	RtsChanged,
	DtrChanged,
	StatusLineChanged,
	ReadModeChanged,
	ReadIntervalChanged,
	BuggyDriverDetected,
}

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

enum SerialReadMode
{
	CheckComstat,
	WaitFirstChar,
	IntervalBased
}

struct SerialOpenParams
{
	uint_t m_baudRate;
	uint_t m_flowControl;
	uint_t m_dataBits;
	uint_t m_stopBits;
	uint_t m_parity;
	uint_t m_readInterval;
	bool m_dtr;
	bool m_rts;
	uint_t m_statusLines;
	uint_t m_options;
	uint_t m_osKind;

	// followed by port name
}

struct SerialOpenParams_old
{
	uint_t m_baudRate;
	uint_t m_flowControl;
	uint_t m_dataBits;
	uint_t m_stopBits;
	uint_t m_parity;

	// followed by port name
}

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

struct SerialStatusLineChangedParams
{
	io.SerialStatusLines m_lines;
	io.SerialStatusLines m_mask;
}

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

char const* getTableString (
	uint_t value,
	char const* const* table
	)
{
	return value < dynamic countof (table) ? table [value] : "\e[31mundefined\e[m";
}

char const* getOnOffString (uint_t line)
{
	return line ? "\e[31mon\e[m" : "\e[90moff\e[m";
}

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

bool representSerialLog (
	log.RepresenterTarget* target,
	uint_t recordCode,
	void const* p,
	size_t size,
	uint_t foldFlags
	)
{
	static char const* flowControlStringTable [] =
	{
		"None",
		"RTS/CTS",
		"XON/XOFF"
	}

	static char const* stopBitsStringTable [] =
	{
		"\e[34m1\e[m bit",
		"\e[34m1.5\e[m bits",
		"\e[34m2\e[m bits"
	}

	static char const* parityStringTable [] =
	{
		"None",
		"Odd",
		"Even",
		"Mark",
		"Space",
	}

	static char const* readModeTable [] =
	{
		"Check COMSTAT.cbInQue",
		"Wait for the 1st char",
		"Interval-based",
	}

	switch (recordCode)
	{
	case SerialLogRecordCode.PortOpened:
		SerialOpenParams const* params = (SerialOpenParams const*) p;
		char const* name = (char const*) (params + 1);

		target.m_lineAttr.m_iconIdx = log.StdLogIcon.Connect;
		target.m_lineAttr.m_backColor = log.StdLogColor.Connect;

		char const* dtrString = getOnOffString (params.m_dtr);
		char const* rtsString = getOnOffString (params.m_rts);
		char const* dsrString = getOnOffString (params.m_statusLines & io.SerialStatusLines.Dsr);
		char const* ctsString = getOnOffString (params.m_statusLines & io.SerialStatusLines.Cts);
		char const* dcdString = getOnOffString (params.m_statusLines & io.SerialStatusLines.Dcd);

		char const* string;

		if (!(foldFlags & 1))
		{
			string =
				$"[\e^+1\e[34m+\e[m] Opened port \e[34m$name\e[m "
				$"baud: \e[34m$(params.m_baudRate)\e[m bps "
				$"DTR: $dtrString "
				$"RTS: $rtsString "
				$"DSR: $dsrString "
				$"CTS: $ctsString "
				$"DCD: $dcdString";
		}
		else
		{
			char const* flowControlString = getTableString (params.m_flowControl, flowControlStringTable);
			char const* stopBitsString = getTableString (params.m_stopBits, stopBitsStringTable);
			char const* parityString = getTableString (params.m_parity, parityStringTable);
			char const* optionString = "";

			if (params.m_osKind == sys.OsKind.Windows)
			{
				uint_t readMode =
					params.m_options & io.SerialOptions.WinReadCheckComstat ? SerialReadMode.CheckComstat :
					params.m_options & io.SerialOptions.WinReadWaitFirstChar ? SerialReadMode.WaitFirstChar :
					SerialReadMode.IntervalBased;

				char const* readModeString = getTableString (readMode, readModeTable);
				char const* readIntervalString = readMode == SerialReadMode.IntervalBased ?
					$"\e[34m$(params.m_readInterval)\e[m ms" :
					"\e[90mirrelevant";

				optionString =
					$"    \e[90mRead mode:     \e[34m$readModeString\n"
					$"    \e[90mRead interval: $readIntervalString\n";
			}

			string =
				$"[\e^-1\e[34m-\e[m] Opened port \e[34m$name\n"
				$"    \e[90mBaud rate:     \e[34m$(params.m_baudRate)\e[m bps\n"
				$"    \e[90mFlow control:  \e[34m$flowControlString\n"
				$"    \e[90mData bits:     \e[34m$(params.m_dataBits)\e[m bits\n"
				$"    \e[90mStop bits:     $stopBitsString\n"
				$"    \e[90mParity:        \e[34m$parityString\n$optionString"
				$"    \e[90mDTR:           $dtrString\n"
				$"    \e[90mRTS:           $rtsString\n"
				$"    \e[90mDSR:           $dsrString\n"
				$"    \e[90mCTS:           $ctsString\n"
				$"    \e[90mDCD:           $dcdString";
		}

		target.addHyperText (string);
		break;

	case SerialLogRecordCode.PortOpened_old:
		SerialOpenParams_old const* params = (SerialOpenParams_old const*) p;
		char const* name = (char const*) (params + 1);

		target.m_lineAttr.m_iconIdx = log.StdLogIcon.Connect;
		target.m_lineAttr.m_backColor = log.StdLogColor.Connect;

		if (!(foldFlags & 1))
			target.addHyperText ($"[\e^+1\e[34m+\e[m] Opened port \e[34m$name\e[m at \e[34m$(params.m_baudRate)\e[m bps");
		else
			target.addHyperText (
				$"[\e^-1\e[34m-\e[m] Opened port \e[34m$name\n"
				$"    \e[90mBaud rate:    \e[34m$(params.m_baudRate)\e[m bps\n"
				$"    \e[90mFlow control: \e[34m%1\n"
				$"    \e[90mData bits:    \e[34m$(params.m_dataBits)\e[m bits\n"
				$"    \e[90mStop bits:    \e[34m%2\n"
				$"    \e[90mParity:       \e[34m%3" (
				getTableString (params.m_flowControl, flowControlStringTable),
				getTableString (params.m_stopBits, stopBitsStringTable),
				getTableString (params.m_parity, parityStringTable)
				));

		break;

	case SerialLogRecordCode.PortClosed:
		char const* name = (char const*) p;
		target.m_lineAttr.m_iconIdx = log.StdLogIcon.Disconnect;
		target.m_lineAttr.m_backColor = log.StdLogColor.Disconnect;
		target.addHyperText ($"Port \e[34m$name\e[m closed");
		break;

	case SerialLogRecordCode.PortOpenError:
		std.Error const* error = (std.Error const*) p;
		target.m_lineAttr.m_iconIdx = log.StdLogIcon.Error;
		target.m_lineAttr.m_backColor = log.StdLogColor.Error;
		target.addHyperText ($"Cannot open port: $(error.m_description)");
		break;

	case SerialLogRecordCode.BaudRateChanged:
		uint_t baudRate = *(uint_t const*) p;

		target.m_lineAttr.m_iconIdx = log.StdLogIcon.Info;
		target.m_lineAttr.m_backColor = log.StdLogColor.Info;
		target.addHyperText ($"Set baud rate: \e[34m$baudRate\e[m bps");
		break;

	case SerialLogRecordCode.FlowControlChanged:
		uint_t flowControl = *(uint_t const*) p;
		char const* flowControlString = getTableString (flowControl, flowControlStringTable);

		target.m_lineAttr.m_iconIdx = log.StdLogIcon.Info;
		target.m_lineAttr.m_backColor = log.StdLogColor.Info;
		target.addHyperText ($"Set flow control: \e[34m$flowControlString");
		break;

	case SerialLogRecordCode.DataBitsChanged:
		uint_t dataBits = *(uint_t const*) p;

		target.m_lineAttr.m_iconIdx = log.StdLogIcon.Info;
		target.m_lineAttr.m_backColor = log.StdLogColor.Info;
		target.addHyperText ($"Set data bits: \e[34m$dataBits\e[m bits");
		break;

	case SerialLogRecordCode.StopBitsChanged:
		uint_t stopBits = *(uint_t const*) p;
		char const* stopBitsString = getTableString (stopBits, stopBitsStringTable);

		target.m_lineAttr.m_iconIdx = log.StdLogIcon.Info;
		target.m_lineAttr.m_backColor = log.StdLogColor.Info;
		target.addHyperText ($"Set stop bits: $stopBitsString");
		break;

	case SerialLogRecordCode.ParityChanged:
		uint_t parity = *(uint_t const*) p;
		char const* parityString = getTableString (parity, parityStringTable);

		target.m_lineAttr.m_iconIdx = log.StdLogIcon.Info;
		target.m_lineAttr.m_backColor = log.StdLogColor.Info;
		target.addHyperText ($"Set parity: \e[34m$parityString");
		break;

	case SerialLogRecordCode.RtsChanged:
		bool rts = *(bool const*) p;
		char const* rtsString = getOnOffString (rts);

		target.m_lineAttr.m_iconIdx = log.StdLogIcon.Info;
		target.m_lineAttr.m_backColor = log.StdLogColor.Info;
		target.addHyperText ($"Set RTS: $rtsString");
		break;

	case SerialLogRecordCode.DtrChanged:
		bool dtr = *(bool const*) p;
		char const* dtrString = getOnOffString (dtr);

		target.m_lineAttr.m_iconIdx = log.StdLogIcon.Info;
		target.m_lineAttr.m_backColor = log.StdLogColor.Info;
		target.addHyperText ($"Set DTR: $dtrString");
		break;

	case SerialLogRecordCode.StatusLineChanged:
		SerialStatusLineChangedParams const* params = (SerialStatusLineChangedParams const*) p;

		char const* string = "Status line(s) changed:";
		if (params.m_mask & io.SerialStatusLines.Dsr)
		{
			char const* valueString = getOnOffString (params.m_lines & io.SerialStatusLines.Dsr);
			string = $"$string DSR: $valueString";
		}

		if (params.m_mask & io.SerialStatusLines.Cts)
		{
			char const* valueString = getOnOffString (params.m_lines & io.SerialStatusLines.Cts);
			string = $"$string CTS: $valueString";
		}

		if (params.m_mask & io.SerialStatusLines.Dcd)
		{
			char const* valueString = getOnOffString (params.m_lines & io.SerialStatusLines.Dcd);
			string = $"$string DCD: $valueString";
		}

		target.m_lineAttr.m_iconIdx = log.StdLogIcon.Info;
		target.m_lineAttr.m_backColor = log.StdLogColor.Info;
		target.addHyperText (string);
		break;

	case SerialLogRecordCode.ReadModeChanged:
		uint_t mode = *(uint_t const*) p;
		char const* modeString = getTableString (mode, readModeTable);

		target.m_lineAttr.m_iconIdx = log.StdLogIcon.Info;
		target.m_lineAttr.m_backColor = log.StdLogColor.Info;
		target.addHyperText ($"Set read mode: \e[34m$modeString");
		break;

	case SerialLogRecordCode.ReadIntervalChanged:
		uint_t interval = *(uint_t const*) p;
		target.m_lineAttr.m_iconIdx = log.StdLogIcon.Info;
		target.m_lineAttr.m_backColor = log.StdLogColor.Info;
		target.addHyperText ($"Set read interval: \e[34m$interval\e[m ms");
		break;

	case SerialLogRecordCode.BuggyDriverDetected:
		char const* driver = (char const*) p;
		target.m_lineAttr.m_iconIdx = log.StdLogIcon.Warning;
		target.m_lineAttr.m_backColor = log.StdLogColor.Warning;
		target.addHyperText ($"Using conservative serial settings due to an unsafe driver: \e[34m$driver");
		break;

	default:
		return false;
	}

	return true;
}

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

class SerialLogFilter: log.Filter
{
	bool m_isSettingChangedVisible;
	bool m_isControlLineChangedVisible;
	bool m_isStatusLineChangedVisible;

	construct ();

	override bool filter (
		uint_t recordCode,
		void const* p,
		size_t size
		);
}

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

SerialLogFilter.construct ()
{
	m_isSettingChangedVisible = true;
	m_isControlLineChangedVisible = true;
	m_isStatusLineChangedVisible = true;
}

bool SerialLogFilter.filter (
	uint_t recordCode,
	void const* p,
	size_t size
	)
{
	switch (recordCode)
	{
	case SerialLogRecordCode.BaudRateChanged:
	case SerialLogRecordCode.DataBitsChanged:
	case SerialLogRecordCode.StopBitsChanged:
	case SerialLogRecordCode.ParityChanged:
	case SerialLogRecordCode.FlowControlChanged:
	case SerialLogRecordCode.ReadModeChanged:
	case SerialLogRecordCode.ReadIntervalChanged:
		return m_isSettingChangedVisible;

	case SerialLogRecordCode.RtsChanged:
	case SerialLogRecordCode.DtrChanged:
		return m_isControlLineChangedVisible;

	case SerialLogRecordCode.StatusLineChanged:
		return m_isStatusLineChangedVisible;

	default:
		return true; // everything else is visible
	}
}

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