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

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

namespace ui {

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

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

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

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

public:
	InformationValue* readonly m_sessionTimeInfo;
	InformationValue* readonly m_txTotalSizeInfo;
	InformationValue* readonly m_txThroughputInfo;
	InformationValue* readonly m_rxTotalSizeInfo;
	InformationValue* readonly m_rxThroughputInfo;
	InformationValue* readonly m_totalSizeInfo;
	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(
		InformationGrid* informationGrid,
		InformationGroup* group = null,
		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(
	InformationGrid* informationGrid,
	InformationGroup* group,
	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:
		size /= 2;
		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 ui
