//..............................................................................
//
// Generic file session
//

import "io_base.jncx"
import "io_Mailslot.jnc"
import "io_BufferUi.jnc"
import "std_Buffer.jnc"
import "doc_Plugin.jnc"
import "gui_History.jnc"
import "gui_StdSessionInfo.jnc"
import "log_ThroughputCalc.jnc"
import "log_ChecksumCalc.jnc"

using extension gui.ComboBoxHistory;

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

class MailslotSession: doc.Session
{
protected:
	enum State
	{
		Closed,
		Opened,
	}


	enum ActionId
	{
		Open,
		_Count,
	}

	enum IconId
	{
		Open,
		Close,
		_Count,
	}

	enum StatusPaneId
	{
		State,
		_Count,
	}

protected:
	// file

	State bindable m_state;
	io.Mailslot m_mailslot;
	std.Buffer m_readBuffer;
	uint_t m_syncId;

	// toolbar, status bar & menu

	gui.Action* m_actionTable [ActionId._Count];
	gui.Icon* m_iconTable [IconId._Count];
	gui.StatusPane* m_statusPaneTable [StatusPaneId._Count];
	gui.ComboBox* m_nameCombo;

	// property grid

	gui.StringProperty* m_nameProp;
	io.BufferPropertySet m_bufferPropertySet;

	// information grid

	gui.StdSessionInfoSet m_stdSessionInfoSet;
	log.ThroughputInfoSet m_throughputInfoSet;
	log.ChecksumInfoSet m_checksumInfoSet;

public:
	construct (doc.PluginHost* pluginHost);

	bool errorcode open ();
	close ();

	override void load (doc.Storage* storage);
	override void save (doc.Storage* storage);
	override void updateProperties ();
	override bool errorcode applyProperties ();
	override void restoreDefaultProperties ();

protected:
	createUi ();

	onOpen ()
	{
		m_state ? close () : try open ();
	}

	onNameComboEnter ()
	{
		try open ();
	}

	onMailslotEvent (
		uint_t syncId,
		io.MailslotEvents triggeredEvents
		);

	io.MailslotOptions getMailslotOptions ();
	waitMailslotEvents ();

	reactor m_uiReactor ();
}

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

MailslotSession.construct (doc.PluginHost* pluginHost)
{
	basetype.construct (pluginHost);

	createUi ();

	pluginHost.m_log.attachRepresenter (representFileLog);
	pluginHost.m_log.registerSelectionProcessor (log.ThroughputCalcRangeProcessor.factory ~(m_throughputInfoSet));
	pluginHost.m_log.registerSelectionProcessor (log.ChecksumCalcRangeProcessor.factory ~(m_checksumInfoSet));
	m_stdSessionInfoSet.attachLogListener (pluginHost.m_log);
}

MailslotSession.createUi ()
{
	// toolbar

	m_pluginHost.m_toolBar.addLabel ("Mailslot:");
	m_nameCombo = m_pluginHost.m_toolBar.addComboBox (-1);
	m_nameCombo.m_isEditable = true;
	m_nameCombo.m_onEnter += onNameComboEnter;

	m_iconTable [IconId.Open] = new gui.Icon ("images/open.png");
	m_iconTable [IconId.Close] = new gui.Icon ("images/close.png");

	m_actionTable [ActionId.Open] = createAction ("Open", m_iconTable [IconId.Open]);
	m_actionTable [ActionId.Open].m_onTriggered += onOpen;

	// status bar

	m_statusPaneTable [StatusPaneId.State] = m_pluginHost.m_statusBar.addPane (, 80);

	// property grid

	gui.GroupProperty* group = m_pluginHost.m_propertyGrid.createGroupProperty (
		"Connection",
		"File stream-specific connection settings"
		);

	m_nameProp = m_pluginHost.m_propertyGrid.createStringProperty (
		group,,
		"Mailslot name",
		"Specify the mailslot to open"
		);

	m_bufferPropertySet.createPropertiesGrouped (
		m_pluginHost.m_propertyGrid,,,
		io.BufferPropertySet.PropertyId.ReadParallelism |
		io.BufferPropertySet.PropertyId.ReadBlockSize |
		io.BufferPropertySet.PropertyId.ReadBufferSize |
		io.BufferPropertySet.PropertyId.KeepReadBlockSize |
		io.BufferPropertySet.PropertyId.NotifyReadBufferFull
		);

	// information grid

	gui.InformationGroup* infoGroup = m_pluginHost.m_informationGrid.createGroup ("Mailslot");

	m_stdSessionInfoSet.createInformationValues (
		m_pluginHost.m_informationGrid,
		infoGroup,,
		gui.StdSessionInfoSet.InfoId.SessionTime |
		gui.StdSessionInfoSet.InfoId.RxTotalSize
		);

	m_throughputInfoSet.createInformationValuesGrouped (m_pluginHost.m_informationGrid);
	m_checksumInfoSet.createInformationValuesGrouped (m_pluginHost.m_informationGrid);

	m_pluginHost.m_informationGrid.resizeNameColumnToContents ();

	m_uiReactor.start ();
}

bool errorcode MailslotSession.open ()
{
	close ();
	startSession ();

	m_nameCombo.addEditTextToHistory ();

	char const* fileName = m_nameCombo.m_editText;

	m_mailslot.m_readParallelism = m_bufferPropertySet.m_readParallelismProp.m_value;
	m_mailslot.m_readBlockSize = m_bufferPropertySet.m_readBlockSizeProp.m_value;
	m_mailslot.m_readBufferSize = m_bufferPropertySet.m_readBufferSizeProp.m_value;
	m_mailslot.m_options = getMailslotOptions ();
	m_mailslot.open (fileName);

	writeLogString (MailslotLogRecordCode.Opened, fileName);
	m_state = State.Opened;
	waitMailslotEvents ();
	m_stdSessionInfoSet.startSession (m_pluginHost.m_mainThreadScheduler);
	return true;

catch:
	std.Error const* error = std.getLastError ();
	writeLog (
		MailslotLogRecordCode.OpenError,
		error,
		error.m_size
		);

	m_mailslot.close ();
	return false;
}

MailslotSession.close ()
{
	if (m_state != State.Opened)
		return;

	writeLog (MailslotLogRecordCode.Closed);
	m_mailslot.close ();
	m_state = State.Closed;
	m_stdSessionInfoSet.endSession ();
}

io.MailslotOptions MailslotSession.getMailslotOptions ()
{
	io.MailslotOptions options;

	if (m_bufferPropertySet.m_keepReadBlockSizeProp.m_value)
		options |= io.MailslotOptions.KeepReadBlockSize;

	return options;
}

MailslotSession.waitMailslotEvents ()
{
	io.MailslotEvents eventMask =
		io.MailslotEvents.IoError |
		io.MailslotEvents.ReadBufferFull |
		io.MailslotEvents.IncomingData;

	m_mailslot.wait (eventMask, onMailslotEvent ~(++m_syncId) @ m_pluginHost.m_mainThreadScheduler);
}

void MailslotSession.load (doc.Storage* storage)
{
	m_nameCombo.loadHistory (storage, "nameHistory");
	m_nameCombo.m_editText = storage.readString ("name");

	m_bufferPropertySet.load (storage);
	m_readBuffer.setSize (m_bufferPropertySet.m_readBufferSizeProp.m_value);
}

void MailslotSession.save (doc.Storage* storage)
{
	m_bufferPropertySet.save (storage);

	m_nameCombo.saveHistory (storage, "nameHistory");
	storage.writeString ("name", m_nameCombo.m_editText);
}

void MailslotSession.updateProperties ()
{
	m_nameProp.m_value = m_nameCombo.m_editText;
}

bool errorcode MailslotSession.applyProperties ()
{
	m_nameCombo.m_editText = m_nameProp.m_value;
	m_readBuffer.setSize (m_bufferPropertySet.m_readBufferSizeProp.m_value);

	if (!m_mailslot.m_isOpen)
		return true;

	m_mailslot.m_readParallelism = m_bufferPropertySet.m_readParallelismProp.m_value;
	m_mailslot.m_readBlockSize = m_bufferPropertySet.m_readBlockSizeProp.m_value;
	m_mailslot.m_readBufferSize = m_bufferPropertySet.m_readBufferSizeProp.m_value;
	m_mailslot.m_options = getMailslotOptions ();
	return true;
}

void MailslotSession.restoreDefaultProperties ()
{
	m_bufferPropertySet.restoreDefaults ();
}

MailslotSession.onMailslotEvent (
	uint_t syncId,
	io.MailslotEvents triggeredEvents
	)
{
	if (syncId != m_syncId)
		return;

	if (triggeredEvents & io.MailslotEvents.IoError)
	{
		writeLog (log.StdRecordCode.Error, m_mailslot.m_ioError, m_mailslot.m_ioError.m_size);
		close ();
		return;
	}

	if (triggeredEvents & io.MailslotEvents.IncomingData)
	{
		size_t result = m_mailslot.read (m_readBuffer.m_p, m_readBuffer.m_size);
		if (result)
			writeLog (log.StdRecordCode.Rx, m_readBuffer.m_p, result);
	}

	waitMailslotEvents ();
}

reactor MailslotSession.m_uiReactor ()
{
	m_title = $"Mailslot $(m_nameCombo.m_editText)";
	m_isTransmitEnabled = m_state == State.Opened;
	m_actionTable [ActionId.Open].m_text = m_state ? "Close" : "Open";
	m_actionTable [ActionId.Open].m_icon = m_iconTable [m_state ? IconId.Close : IconId.Open];
	m_statusPaneTable [StatusPaneId.State].m_text = m_state ? "Opened" : "Closed";
}

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