//..............................................................................
//
// Standard session information (session time and byte counters)
//

import "gui_InformationGrid.jnc"
import "sys_Timer.jnc"
import "sys_Lock.jnc"
import "log_Log.jnc"
import "formatInteger.jnc"

namespace gui {

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

class StdSessionInfoSet
{
public:
	bitflag enum InfoId
	{
		SessionTime,
		TxTotalSize,
		TxThroughput,
		RxTotalSize,
		RxThroughput,
		TotalSize,
		TotalThroughput,

		Default =
			SessionTime |
			TxTotalSize |
			TxThroughput  |
			RxTotalSize |
			RxThroughput,
	}

public:
	gui.InformationValue* readonly m_sessionTimeInfo;
	gui.InformationValue* readonly m_txTotalSizeInfo;
	gui.InformationValue* readonly m_txThroughputInfo;
	gui.InformationValue* readonly m_rxTotalSizeInfo;
	gui.InformationValue* readonly m_rxThroughputInfo;
	gui.InformationValue* readonly m_totalSizeInfo;
	gui.InformationValue* readonly m_totalThroughputInfo;

protected:
	// m_txTotalSize and m_rxTotalSize are modified in the log index thread

	sys.Lock m_lock;
	uint64_t m_txTotalSize;
	uint64_t m_rxTotalSize;

	uint64_t m_txPrevTotalSize;
	uint64_t m_rxPrevTotalSize;

	sys.Timer m_timer;
	uint64_t m_sessionTimestamp;
	uint64_t m_throughputTimestamp;
	uint_t m_syncId;

public:
	createInformationValues (
		gui.InformationGrid* informationGrid,
		gui.InformationGroup* group = null,
		gui.InformationValue* insertBefore = null,
		InfoId mask = InfoId.Default
		);

	addLogListener (log.Log* log)
	{
		log.addListener (onLogRecord);
	}

	startSession (jnc.Scheduler* mainThreadScheduler);
	endSession ();

	addData (
		size_t txSize,
		size_t rxSize
		);

	addTxData (size_t size)
	{
		addData (size, 0);
	}

	addRxData (size_t size)
	{
		addData (0, size);
	}

protected:
	update (uint64_t timestamp = sys.getTimestamp ());
	initialUpdate ();

	onTimer (uint_t syncId);

	onLogRecord (
		uint64_t timestamp,
		uint64_t recordCode,
		void const* p,
		size_t size
		);
}

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

StdSessionInfoSet.createInformationValues (
	gui.InformationGrid* informationGrid,
	gui.InformationGroup* group,
	gui.InformationValue* insertBefore,
	InfoId mask
	)
{
	if (mask & InfoId.SessionTime)
	{
		m_sessionTimeInfo = informationGrid.createValue (
			group,
			insertBefore,,
			"Session time"
			);

		m_sessionTimeInfo.m_isEnabled = false;
	}

	if (mask & InfoId.TxTotalSize)
	{
		m_txTotalSizeInfo = informationGrid.createValue (
			group,
			insertBefore,,
			"TX total bytes"
			);

		m_txTotalSizeInfo.m_isEnabled = false;
	}

	if (mask & InfoId.TxThroughput)
	{
		m_txThroughputInfo = informationGrid.createValue (
			group,
			insertBefore,,
			"TX throughput"
			);

		m_txThroughputInfo.m_isEnabled = false;
	}

	if (mask & InfoId.RxTotalSize)
	{
		m_rxTotalSizeInfo = informationGrid.createValue (
			group,
			insertBefore,,
			"RX total bytes"
			);

		m_rxTotalSizeInfo.m_isEnabled = false;
	}

	if (mask & InfoId.RxThroughput)
	{
		m_rxThroughputInfo = informationGrid.createValue (
			group,
			insertBefore,,
			"RX throughput"
			);

		m_rxThroughputInfo.m_isEnabled = false;
	}

	if (mask & InfoId.TotalSize)
	{
		m_totalSizeInfo = informationGrid.createValue (
			group,
			insertBefore,,
			"Total bytes"
			);

		m_totalSizeInfo.m_isEnabled = false;
	}

	if (mask & InfoId.TotalThroughput)
	{
		m_totalThroughputInfo = informationGrid.createValue (
			group,
			insertBefore,,
			"Total throughput"
			);

		m_totalThroughputInfo.m_isEnabled = false;
	}

	initialUpdate ();
}

StdSessionInfoSet.startSession (jnc.Scheduler* mainThreadScheduler)
{
	if (m_sessionTimeInfo)
		m_sessionTimeInfo.m_isEnabled = true;

	if (m_txTotalSizeInfo)
		m_txTotalSizeInfo.m_isEnabled = true;

	if (m_txThroughputInfo)
		m_txThroughputInfo.m_isEnabled = true;

	if (m_rxTotalSizeInfo)
		m_rxTotalSizeInfo.m_isEnabled = true;

	if (m_rxThroughputInfo)
		m_rxThroughputInfo.m_isEnabled = true;

	if (m_totalSizeInfo)
		m_totalSizeInfo.m_isEnabled = true;

	if (m_totalThroughputInfo)
		m_totalThroughputInfo.m_isEnabled = true;

	m_lock.lock ();
	m_txTotalSize = 0;
	m_rxTotalSize = 0;
	m_lock.unlock ();

	m_txPrevTotalSize = 0;
	m_rxPrevTotalSize = 0;

	m_syncId++;
	m_timer.startPeriodicTimer (onTimer ~(m_syncId) @ mainThreadScheduler, 500);

	initialUpdate ();
}

StdSessionInfoSet.endSession ()
{
	m_timer.stop ();

	update (); // final update

	if (m_sessionTimeInfo)
		m_sessionTimeInfo.m_isEnabled = false;

	if (m_txTotalSizeInfo)
		m_txTotalSizeInfo.m_isEnabled = false;

	if (m_txThroughputInfo)
		m_txThroughputInfo.m_isEnabled = false;

	if (m_rxTotalSizeInfo)
		m_rxTotalSizeInfo.m_isEnabled = false;

	if (m_rxThroughputInfo)
		m_rxThroughputInfo.m_isEnabled = false;

	if (m_totalSizeInfo)
		m_totalSizeInfo.m_isEnabled = false;

	if (m_totalThroughputInfo)
		m_totalThroughputInfo.m_isEnabled = false;

	m_syncId++;
}

StdSessionInfoSet.addData (
	size_t txSize,
	size_t rxSize
	)
{
	m_lock.lock ();
	m_txTotalSize += txSize;
	m_rxTotalSize += rxSize;
	m_lock.unlock ();
}

StdSessionInfoSet.onLogRecord (
	uint64_t timestamp,
	uint64_t recordCode,
	void const* p,
	size_t size
	)
{
	switch (recordCode)
	{
	case log.StdRecordCode.Tx:
		addTxData (size);
		break;

	case log.StdRecordCode.Rx:
		addRxData (size);
		break;

	case log.StdRecordCode.TxRx:
		addData (size, size);
		break;
	}
}

StdSessionInfoSet.onTimer (uint_t syncId)
{
	if (syncId == m_syncId)
		update ();
}

StdSessionInfoSet.initialUpdate ()
{
	uint64_t timestamp = sys.getTimestamp ();
	m_sessionTimestamp = timestamp;
	m_throughputTimestamp = timestamp;
	update (timestamp);
}

StdSessionInfoSet.update (uint64_t timestamp)
{
	uint64_t txTotalSize;
	uint64_t rxTotalSize;
	size_t txThroughput;
	size_t rxThroughput;

	m_lock.lock ();
	txTotalSize = m_txTotalSize;
	rxTotalSize = m_rxTotalSize;
	m_lock.unlock ();

	if (timestamp > m_throughputTimestamp)
	{
		uint64_t timeSpan = timestamp - m_throughputTimestamp;
		txThroughput = (txTotalSize - m_txPrevTotalSize) * 10000000 / timeSpan;
		rxThroughput = (rxTotalSize - m_rxPrevTotalSize) * 10000000 / timeSpan;

		m_txPrevTotalSize = txTotalSize;
		m_rxPrevTotalSize = rxTotalSize;
		m_throughputTimestamp = timestamp;
	}

	if (m_sessionTimeInfo)
		m_sessionTimeInfo.m_value = sys.formatTimestamp (timestamp - m_sessionTimestamp, 0, "%h:%m:%s");

	if (m_txTotalSizeInfo)
		m_txTotalSizeInfo.m_value = $"$(formatIntegerWithThousandSep (txTotalSize))";

	if (m_txThroughputInfo)
		m_txThroughputInfo.m_value = $"$(formatIntegerWithThousandSep (txThroughput))";

	if (m_rxTotalSizeInfo)
		m_rxTotalSizeInfo.m_value = $"$(formatIntegerWithThousandSep (rxTotalSize))";

	if (m_rxThroughputInfo)
		m_rxThroughputInfo.m_value = $"$(formatIntegerWithThousandSep (rxThroughput))";

	if (m_totalSizeInfo)
		m_totalSizeInfo.m_value = $"$(formatIntegerWithThousandSep (txTotalSize + rxTotalSize))";

	if (m_totalThroughputInfo)
		m_totalThroughputInfo.m_value = $"$(formatIntegerWithThousandSep (txThroughput + rxThroughput))";
}

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

} // namespace gui {
