//..............................................................................
//
// Socket client log filter (TCP Server, TCP Proxy, SSL Server, WebSocket server)
//

import "io_base.jncx"
import "io_SocketAddress.jnc"
import "doc_Plugin.jnc"
import "log_Filter.jnc"
import "ui_Layout.jnc"
import "ui_GroupBox.jnc"
import "ui_ComboBox.jnc"
import "ui_History.jnc"
import "SocketLogRecordCode.jnc"

using extension ui.ComboBoxHistory;

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

class ServerSocketLogFilter:
	doc.LogFilter,
	log.Filter {
protected:
	log.Log* m_log;

	ui.ComboBox* m_filterCombo;
	ui.StringProperty* m_filterProp;

	bool m_isVisible;
	io.SocketAddress m_filterAddress;
	io.SocketAddress m_lastVisibleAddress;

public:
	construct(doc.PluginHost* pluginHost);

	override void reset();

	override bool filter(
		uint64_t timestamp,
		uint64_t recordCode,
		void const* p,
		size_t size
	);

protected:
	void onApply();
}

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

ServerSocketLogFilter.construct(doc.PluginHost* pluginHost) {
	m_log = pluginHost.m_log;

	m_filterCombo = new ui.ComboBox;
	m_filterCombo.m_isEditable = true;
	m_filterCombo.m_onReturnPressed += onApply;
	m_filterCombo.m_placeholderText = "Enter client address to keep in log...";
	m_filterCombo.m_toolTip = "View only communications with the specified client";

	ui.Action applyAction("images/apply.png", "Apply && rebuild log", onApply);
	ui.ToolBar applyToolBar;
	applyToolBar.addAction(applyAction);

	ui.VBoxLayout layout;
	ui.HBoxLayout subLayout;
	subLayout.addWidget(m_filterCombo, 1);
	subLayout.addWidget(applyToolBar);

	layout.addLabel("Client log filter:");
	layout.addLayout(subLayout);
	pluginHost.createForm(layout);

	pluginHost.m_log.addFilter(this);
}

void ServerSocketLogFilter.onApply() {
	string_t filterText = m_filterCombo.m_currentText;

	io.SocketAddress filterAddress;
	if (filterText) {
		bool result = try filterAddress.parse(filterText);
		if (!result) {
			m_filterCombo.m_backgroundColor = ui.StdColor.PastelRed;
			m_filterCombo.selectAllEditText();
			m_filterCombo.setFocus();
			return;
		}
	}

	if (filterAddress.isEqual(m_filterAddress))
		return;

	m_filterCombo.m_backgroundColor = ui.ColorFlags.Undefined;
	m_log.suspend();
	m_filterAddress = filterAddress;
	m_log.resume();
	m_log.rebuild();
}

void ServerSocketLogFilter.reset() {
	m_isVisible = false;
	m_lastVisibleAddress = null;
}

bool ServerSocketLogFilter.filter(
	uint64_t timestamp,
	uint64_t recordCode,
	void const* p,
	size_t size
) {
	if (!m_filterAddress.m_family) // empty address means, filtering is off
		return true;

	switch (recordCode) {
	case SocketLogRecordCode.ClientConnected:
	case SocketLogRecordCode.ClientChanged:
		io.SocketAddress const* address = (io.SocketAddress const*)p;
		m_isVisible = address.isMatch(m_filterAddress);
		if (!m_isVisible)
			return false;

		if (recordCode == SocketLogRecordCode.ClientChanged && m_lastVisibleAddress.isEqual(address))
			return false; // don't show unnecessary client-changed messages

		m_lastVisibleAddress = *address;
		return true;

	case SocketLogRecordCode.ClientDisconnected:
		SocketLogDisconnectParams const* params = (SocketLogDisconnectParams const*)p;
		bool result = params.m_address.isMatch(m_filterAddress);
		if (params.m_flags & SocketLogDisconnectFlags.KeepClient) {
			if (result)
				m_lastVisibleAddress = params.m_address;

			m_isVisible = result;
			return result;
		}

		if (result)
			m_lastVisibleAddress = null;

		m_isVisible = false;
		return result;

	case SocketLogRecordCode.ClientRemoved:
		if (m_isVisible)
			m_lastVisibleAddress = null;

		m_isVisible = false;
		return true; // not represented, anyway

	default:
		return m_isVisible;
	}
}

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