//..............................................................................
//
// WebSocket properties
//

import "ui_PropertyGrid.jnc"
import "ui_StdEditToolBar.jnc"
import "ui_DictionaryWidget.jnc"

namespace ui {

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

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

enum WebSocketTransmitMode {
	Auto,   // if it's valid UTF-8, send as text, otherwise as binary
	Text,   // always send as text
	Binary, // always send as binary
}

const EnumPropertyOption TransmitModeOptionTable[] = {
	{ "Auto",   WebSocketTransmitMode.Auto },
	{ "Text",   WebSocketTransmitMode.Text },
	{ "Binary", WebSocketTransmitMode.Binary },
}

char const* const TransmitModeToolTipTable[] = {
	"Transmit as text if the packet is a valid UTF-8; transmit as binary otherwise",
	"Transmit as text always",
	"Transmit as binary always",
}

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

enum WebSocketCloseMode {
	Graceful, // wait for close confirmation
	Abrupt,   // close socket immediately
}

EnumPropertyOption CloseModeOptionTable[] = {
	{ "Graceful", WebSocketCloseMode.Graceful },
	{ "Abrupt",   WebSocketCloseMode.Abrupt },
}

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

class WebSocketUi {
	bitflag enum SettingId {
		Headers,
		TransmitMode,
		CloseMode,
		CloseTimeout,

		All =
			Headers |
			TransmitMode |
			CloseMode |
			CloseTimeout
	}

	enum Defaults {
		TransmitMode = WebSocketTransmitMode.Auto,
		CloseMode    = WebSocketCloseMode.Graceful,
		CloseTimeout = 3000,
	}

	DictionaryWidget* m_headersWidget;
	StdEditToolBar* m_headersToolBar;
	ComboBox* m_transmitModeCombo;

	DictionaryProperty* m_headersProp;
	EnumProperty* m_transmitModeProp;
	EnumProperty* m_closeModeProp;
	IntProperty* m_closeTimeoutProp;

	property m_headers {
		DictionaryEntry const* get() {
			return
				m_headersWidget ? m_headersWidget.m_dictionary :
				m_headersProp ? m_headersProp.m_value :
				null;
		}

		void set(DictionaryEntry const* value) {
			if (m_headersWidget)
				m_headersWidget.m_dictionary = value;
			else if (m_headersProp)
				m_headersProp.m_value = value;
		}
	}

	property m_transmitMode {
		WebSocketTransmitMode get() {
			return
				m_transmitModeCombo ? m_transmitModeCombo.m_currentData :
				m_transmitModeProp ? m_transmitModeProp.m_value :
				null;
		}

		void set(WebSocketTransmitMode value) {
			if (m_transmitModeCombo)
				m_transmitModeCombo.m_currentData = value;
			else if (m_transmitModeProp)
				m_transmitModeProp.m_value = value;
		}
	}

	protected reactor m_headersReactor;

	void createForm(
		VBoxLayout* layout,
		SettingId settingMask =
			SettingId.Headers |
			SettingId.TransmitMode
	);

	void createProperties(
		PropertyGrid* propertyGrid,
		Property* parentProp = null,
		Property* beforeProp = null,
		SettingId settingMask = SettingId.All
	);

	void load(doc.Storage* storage);
	void save(doc.Storage* storage);
	void updateProperties();
	void applyProperties();
	void restoreDefaults();

protected:
	void onCloseModeChanged() {
		m_closeTimeoutProp.m_isEnabled = m_closeModeProp.m_value == WebSocketCloseMode.Graceful;
	}
}

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

void WebSocketUi.createForm(
	VBoxLayout* layout,
	SettingId settingMask
) {
	if (settingMask & SettingId.Headers) {
		layout.addWidget(new ui.Label("Extra headers:"));
		m_headersWidget = new DictionaryWidget;
		m_headersWidget.m_keyColumnName = "Header";
		m_headersWidget.m_placeholderText = "No extra headers";
		layout.addWidget(m_headersWidget, 1);

		m_headersToolBar = new StdEditToolBar;
		m_headersToolBar.addStretch();
		m_headersToolBar.createActions();
		m_headersToolBar.m_addAction.m_onTriggered += m_headersWidget.addEditNew;
		m_headersToolBar.m_editAction.m_onTriggered += m_headersWidget.editSelection;
		m_headersToolBar.m_removeAction.m_onTriggered += m_headersWidget.removeSelection;
		layout.addWidget(m_headersToolBar);

		m_headersReactor.start();
	}

	if (settingMask & SettingId.TransmitMode) {
		m_transmitModeCombo = new ComboBox(
			TransmitModeOptionTable,
			countof(TransmitModeOptionTable)
		);

		for (size_t i = 0; i < countof(TransmitModeToolTipTable); i++)
			m_transmitModeCombo.m_itemToolTip[i] = TransmitModeToolTipTable[i];

		FormLayout subLayout;
		subLayout.addRow("Transmit as:", m_transmitModeCombo);
		layout.addLayout(subLayout);
	}
}

void WebSocketUi.createProperties(
	PropertyGrid* propertyGrid,
	Property* parentProp,
	Property* beforeProp,
	SettingId settingMask
) {
	if (settingMask & SettingId.Headers) {
		m_headersProp = propertyGrid.createDictionaryProperty(
			parentProp,
			beforeProp,
			"Extra headers",
			"Extra HTTP headers for WebSocket handshake"
		);

		m_headersProp.m_editDialogTitle = "Edit Extra Headers";
		m_headersProp.m_keyColumnName = "Header";
		m_headersProp.m_placeholderText = "No extra headers";
	}

	if (settingMask & SettingId.TransmitMode)
		m_transmitModeProp = propertyGrid.createEnumProperty(
			parentProp,
			beforeProp,
			"Transmit mode",
			"Specify the WebSocket transmission mode",
			TransmitModeOptionTable,
			countof(TransmitModeOptionTable)
		);

	if (settingMask & SettingId.CloseMode) {
		m_closeModeProp = propertyGrid.createEnumProperty(
			parentProp,
			beforeProp,
			"Close mode",
			"Specify the WebSocket close mode",
			CloseModeOptionTable,
			countof(CloseModeOptionTable)
		);

		if (settingMask & SettingId.CloseTimeout) {
			m_closeTimeoutProp = propertyGrid.createIntProperty(
				parentProp,
				beforeProp,
				"Close timeout",
				"Specify the WebSocket close timeout"
			);

			m_closeModeProp.m_onChanged += onCloseModeChanged;
		}
	}
}

void WebSocketUi.load(doc.Storage* storage) {
	m_headers = loadDictionary(storage, "webSocketHeaders");
	m_transmitMode = (WebSocketTransmitMode)storage.readInt("webSocketTransmitMode", Defaults.TransmitMode);

	if (m_closeModeProp)
		m_closeModeProp.m_value = storage.readInt("webSocketCloseMode", Defaults.CloseMode);

	if (m_closeTimeoutProp)
		m_closeTimeoutProp.m_value = storage.readInt("webSocketCloseTimeout", Defaults.CloseTimeout);
}

void WebSocketUi.save(doc.Storage* storage) {
	saveDictionary(m_headers, storage, "webSocketHeaders");
	storage.writeInt("webSocketTransmitMode", m_transmitMode);

	if (m_closeModeProp)
		storage.writeInt("webSocketCloseMode", m_closeModeProp.m_value);

	if (m_closeTimeoutProp)
		storage.writeInt("webSocketCloseTimeout", m_closeTimeoutProp.m_value);
}

void WebSocketUi.updateProperties() {
	if (m_headersWidget && m_headersProp)
		m_headersProp.m_value = m_headersWidget.m_dictionary;

	if (m_transmitModeCombo && m_transmitModeProp)
		m_transmitModeProp.m_value = m_transmitModeCombo.m_editText;
}

void WebSocketUi.applyProperties() {
	if (m_headersWidget && m_headersProp)
		m_headersWidget.m_dictionary = m_headersProp.m_value;

	if (m_transmitModeCombo && m_transmitModeProp)
		m_transmitModeCombo.m_currentData = m_transmitModeProp.m_value;
}

void WebSocketUi.restoreDefaults() {
	if (m_headersProp)
		m_headersProp.m_value = null;

	if (m_transmitModeProp)
		m_transmitModeProp.m_value = Defaults.TransmitMode;

	if (m_closeModeProp)
		m_closeModeProp.m_value = Defaults.CloseMode;

	if (m_closeTimeoutProp)
		m_closeTimeoutProp.m_value = Defaults.CloseTimeout;
}

reactor WebSocketUi.m_headersReactor {
	m_headersToolBar.m_editAction.m_isEnabled = m_headersWidget.m_selectedItemCount == 1;
	m_headersToolBar.m_removeAction.m_isEnabled = m_headersWidget.m_selectedItemCount > 0;
}

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

//! @}

} // namespace ui
