//..............................................................................
//
// Serial log representer
//

import "log_Representation.jnc"
import "io_base.jncx"
import "io_Serial.jnc"
import "std_String.jnc"
import "SerialLogRecordCode.jnc"
import "stringUtils.jnc"

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

string_t getStatusLineChangeString(
	io.SerialStatusLines lines,
	io.SerialStatusLines mask,
	string_t lineKindString,
	string_t dsrString,
	string_t ctsString
) {
	string_t string = $"$lineKindString line(s) changed:";

	if (mask & io.SerialStatusLines.Dsr)
		string = $"%1 %2: %3"(
			string,
			dsrString,
			getOnOffString(lines & io.SerialStatusLines.Dsr)
		);

	if (mask & io.SerialStatusLines.Cts)
		string = $"%1 %2: %3"(
			string,
			ctsString,
			getOnOffString(lines & io.SerialStatusLines.Cts)
		);

	if (mask & io.SerialStatusLines.Dcd)
		string = $"%1 DCD: %2"(
			string,
			getOnOffString(lines & io.SerialStatusLines.Dcd)
		);

	if (mask & io.SerialStatusLines.Ring)
		string = $"%1 RI: %2"(
			string,
			getOnOffString(lines & io.SerialStatusLines.Ring)
		);

	return string;
}

string_t getSerialLineErrorString(io.SerialLineErrors errors) {
	return
		(errors & io.SerialLineErrors.FramingError) ? "FRAMING" :
		(errors & io.SerialLineErrors.ParityError) ? "PARITY" :
		(errors & io.SerialLineErrors.BreakError) ? "BREAK" :
		null;
}

string_t getPortDescString(string_t portName) {
	string_t description = portName.m_p + portName.m_length + 1;
	string_t manufacturer = description.m_p + description.m_length + 1;
	string_t hardwareIds = manufacturer.m_p + manufacturer.m_length + 1;
	string_t driver = hardwareIds.m_p + hardwareIds.m_length + 1;
	string_t location = driver.m_p + driver.m_length + 1;

	std.StringBuilder portDescString;

	if (description)
		portDescString += $"    \e[90mDescription:   \e[34m$description\n";

	if (manufacturer)
		portDescString += $"    \e[90mManufacturer:  \e[34m$manufacturer\n";

	if (hardwareIds)
		portDescString += $"    \e[90mHardware ID:   \e[34m$hardwareIds\n";

	if (driver)
		portDescString += $"    \e[90mDriver:        \e[34m$driver\n";

	if (location)
		portDescString += $"    \e[90mLocation:      \e[34m$location\n";

	portDescString.chop(1); // chop the trailing newline
	return portDescString.detachString();
}

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

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

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

	static string_t const parityStringTable[] = {
		"None",
		"Odd",
		"Even",
		"Mark",
		"Space",
		"Ignore",
	}

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

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

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

		string_t dtrString = getOnOffString(params.m_dtr);
		string_t rtsString = getOnOffString(params.m_rts);
		string_t dsrString = getOnOffString(params.m_statusLines & io.SerialStatusLines.Dsr);
		string_t ctsString = getOnOffString(params.m_statusLines & io.SerialStatusLines.Cts);

		if (!(foldFlags & 1))
			representation.addHyperText(
				$"[\e^+1\e[34m+\e[m] Opened port \e[34m$name $(params.m_baudRate) bps\e[m "
				$"DTR: $dtrString "
				$"RTS: $rtsString "
				$"DSR: $dsrString "
				$"CTS: $ctsString"
			);
		else {
			string_t flowControlString = getTableString(params.m_flowControl, flowControlStringTable);
			string_t stopBitsString = getTableString(params.m_stopBits, stopBitsStringTable);
			string_t parityString = getTableString(params.m_parity, parityStringTable);
			string_t 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;

				string_t readModeString = getTableString(readMode, readModeTable);
				string_t 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";
			}

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

			if (recordCode == SerialLogRecordCode.PortOpenedEx) {
				string_t portDescString = getPortDescString(name);
				if (portDescString) {
					representation.addBreak();
					representation.addHyperText(portDescString);
				}
			}
		}

		break;

	case SerialLogRecordCode.CaptureStarted:
		string_t name = (char const*)p;

		representation.m_lineAttr.m_iconIdx = log.StdLogIcon.Listen;
		representation.m_lineAttr.m_backColor = log.StdLogColor.Info;

		if (!(foldFlags & 1))
			representation.addHyperText($"[\e^+1\e[34m+\e[m] Capture started on \e[34m$name");
		else {
			string_t portDescString = getPortDescString(name);
			representation.addHyperText($"[\e^-1\e[34m-\e[m] Capture started on \e[34m$name\n$portDescString");
		}

		break;

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

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

		if (!(foldFlags & 1))
			representation.addHyperText($"[\e^+1\e[34m+\e[m] Opened port \e[34m$name $(params.m_baudRate) bps");
		else
			representation.addHyperText(
				$"[\e^-1\e[34m-\e[m] Opened port \e[34m$name\n"
				$"    \e[90mBaud rate:    \e[34m$(params.m_baudRate) bps\n"
				$"    \e[90mFlow control: \e[34m%1\n"
				$"    \e[90mData bits:    \e[34m$(params.m_dataBits) 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:
		representation.m_lineAttr.m_iconIdx = log.StdLogIcon.Disconnect;
		representation.m_lineAttr.m_backColor = log.StdLogColor.Disconnect;
		representation.addHyperText("Port closed");
		break;

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

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

	case SerialLogRecordCode.BaudRateChanged:
		uint_t baudRate = *(uint_t const*)p;
		representation.m_lineAttr.m_iconIdx = log.StdLogIcon.Info;
		representation.m_lineAttr.m_backColor = log.StdLogColor.Info;
		representation.addHyperText($"Set baud rate: \e[34m$baudRate\e[m bps");
		break;

	case SerialLogRecordCode.FlowControlChanged:
		uint_t flowControl = *(uint_t const*)p;
		string_t flowControlString = getTableString(flowControl, flowControlStringTable);
		representation.m_lineAttr.m_iconIdx = log.StdLogIcon.Info;
		representation.m_lineAttr.m_backColor = log.StdLogColor.Info;
		representation.addHyperText($"Set flow control: \e[34m$flowControlString");
		break;

	case SerialLogRecordCode.DataBitsChanged:
		uint_t dataBits = *(uint_t const*)p;
		representation.m_lineAttr.m_iconIdx = log.StdLogIcon.Info;
		representation.m_lineAttr.m_backColor = log.StdLogColor.Info;
		representation.addHyperText($"Set data bits: \e[34m$dataBits\e[m bits");
		break;

	case SerialLogRecordCode.StopBitsChanged:
		uint_t stopBits = *(uint_t const*)p;
		string_t stopBitsString = getTableString(stopBits, stopBitsStringTable);
		representation.m_lineAttr.m_iconIdx = log.StdLogIcon.Info;
		representation.m_lineAttr.m_backColor = log.StdLogColor.Info;
		representation.addHyperText($"Set stop bits: $stopBitsString");
		break;

	case SerialLogRecordCode.ParityChanged:
		uint_t parity = *(uint_t const*)p;
		string_t parityString = getTableString(parity, parityStringTable);
		representation.m_lineAttr.m_iconIdx = log.StdLogIcon.Info;
		representation.m_lineAttr.m_backColor = log.StdLogColor.Info;
		representation.addHyperText($"Set parity: \e[34m$parityString");
		break;

	case SerialLogRecordCode.DtrChanged:
		bool state = *(bool const*)p;
		string_t stateString = getOnOffString(state);
		representation.m_lineAttr.m_iconIdx = log.StdLogIcon.Info;
		representation.m_lineAttr.m_backColor = log.StdLogColor.Info;
		representation.addHyperText($"Set DTR: $stateString");
		break;

	case SerialLogRecordCode.RtsChanged:
		bool state = *(bool const*)p;
		string_t stateString = getOnOffString(state);

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

	case SerialLogRecordCode.BreakConditionChanged:
		bool state = *(bool const*)p;
		string_t stateString = getOnOffString(state);

		representation.m_lineAttr.m_iconIdx = log.StdLogIcon.Info;
		representation.m_lineAttr.m_backColor = log.StdLogColor.Info;
		representation.addHyperText($"Set BREAK: $stateString");
		break;

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

		string_t string = getStatusLineChangeString(
			params.m_lines,
			params.m_mask,
			"Status",
			"DSR",
			"CTS"
		);

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

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

		string_t string = getStatusLineChangeString(
			params.m_lines,
			params.m_mask & (io.SerialStatusLines.Dsr | io.SerialStatusLines.Cts), // exclude DCD & RI
			"Control",
			"DTR",
			"RTS"
		);

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

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

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

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

	case SerialLogRecordCode.UpdateIntervalChanged:
		uint_t interval = *(uint_t const*)p;
		representation.m_lineAttr.m_iconIdx = log.StdLogIcon.Info;
		representation.m_lineAttr.m_backColor = log.StdLogColor.Info;
		representation.addHyperText($"Set status line update interval: \e[34m$interval\e[m ms");
		break;

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

	case SerialLogRecordCode.LineError:
		io.SerialLineErrors errors = *(io.SerialLineErrors const*)p;
		representation.m_lineAttr.m_iconIdx = log.StdLogIcon.Warning;
		representation.m_lineAttr.m_backColor = log.StdLogColor.Warning;
		representation.addHyperText($"Line error detected: \e[34m$(getSerialLineErrorString (errors))");
		break;

	default:
		return false;
	}

	return true;
}

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