#include "stdafx.h"
#include "HealthTelemetry.h"

void HealthTelemetry::ReportSignalStatus(bool hasSignal, unsigned width, unsigned height, const std::string& modeName)
{
	std::lock_guard<std::mutex> lock(mMutex);
	mSignalStatus.hasSignal = hasSignal;
	mSignalStatus.width = width;
	mSignalStatus.height = height;
	mSignalStatus.modeName = modeName;
}

bool HealthTelemetry::TryReportSignalStatus(bool hasSignal, unsigned width, unsigned height, const std::string& modeName)
{
	std::unique_lock<std::mutex> lock(mMutex, std::try_to_lock);
	if (!lock.owns_lock())
		return false;

	mSignalStatus.hasSignal = hasSignal;
	mSignalStatus.width = width;
	mSignalStatus.height = height;
	mSignalStatus.modeName = modeName;
	return true;
}

void HealthTelemetry::ReportVideoIOStatus(const std::string& backendName, const std::string& modelName,
	bool supportsInternalKeying, bool supportsExternalKeying, bool keyerInterfaceAvailable,
	bool externalKeyingRequested, bool externalKeyingActive, const std::string& statusMessage)
{
	std::lock_guard<std::mutex> lock(mMutex);
	mVideoIOStatus.backendName = backendName;
	mVideoIOStatus.modelName = modelName;
	mVideoIOStatus.supportsInternalKeying = supportsInternalKeying;
	mVideoIOStatus.supportsExternalKeying = supportsExternalKeying;
	mVideoIOStatus.keyerInterfaceAvailable = keyerInterfaceAvailable;
	mVideoIOStatus.externalKeyingRequested = externalKeyingRequested;
	mVideoIOStatus.externalKeyingActive = externalKeyingActive;
	mVideoIOStatus.statusMessage = statusMessage;
}

bool HealthTelemetry::TryReportVideoIOStatus(const std::string& backendName, const std::string& modelName,
	bool supportsInternalKeying, bool supportsExternalKeying, bool keyerInterfaceAvailable,
	bool externalKeyingRequested, bool externalKeyingActive, const std::string& statusMessage)
{
	std::unique_lock<std::mutex> lock(mMutex, std::try_to_lock);
	if (!lock.owns_lock())
		return false;

	mVideoIOStatus.backendName = backendName;
	mVideoIOStatus.modelName = modelName;
	mVideoIOStatus.supportsInternalKeying = supportsInternalKeying;
	mVideoIOStatus.supportsExternalKeying = supportsExternalKeying;
	mVideoIOStatus.keyerInterfaceAvailable = keyerInterfaceAvailable;
	mVideoIOStatus.externalKeyingRequested = externalKeyingRequested;
	mVideoIOStatus.externalKeyingActive = externalKeyingActive;
	mVideoIOStatus.statusMessage = statusMessage;
	return true;
}

void HealthTelemetry::RecordPerformanceStats(double frameBudgetMilliseconds, double renderMilliseconds)
{
	std::lock_guard<std::mutex> lock(mMutex);
	mPerformance.frameBudgetMilliseconds = std::max(frameBudgetMilliseconds, 0.0);
	mPerformance.renderMilliseconds = std::max(renderMilliseconds, 0.0);
	if (mPerformance.smoothedRenderMilliseconds <= 0.0)
		mPerformance.smoothedRenderMilliseconds = mPerformance.renderMilliseconds;
	else
		mPerformance.smoothedRenderMilliseconds = mPerformance.smoothedRenderMilliseconds * 0.9 + mPerformance.renderMilliseconds * 0.1;
}

bool HealthTelemetry::TryRecordPerformanceStats(double frameBudgetMilliseconds, double renderMilliseconds)
{
	std::unique_lock<std::mutex> lock(mMutex, std::try_to_lock);
	if (!lock.owns_lock())
		return false;

	mPerformance.frameBudgetMilliseconds = std::max(frameBudgetMilliseconds, 0.0);
	mPerformance.renderMilliseconds = std::max(renderMilliseconds, 0.0);
	if (mPerformance.smoothedRenderMilliseconds <= 0.0)
		mPerformance.smoothedRenderMilliseconds = mPerformance.renderMilliseconds;
	else
		mPerformance.smoothedRenderMilliseconds = mPerformance.smoothedRenderMilliseconds * 0.9 + mPerformance.renderMilliseconds * 0.1;
	return true;
}

void HealthTelemetry::RecordFramePacingStats(double completionIntervalMilliseconds, double smoothedCompletionIntervalMilliseconds,
	double maxCompletionIntervalMilliseconds, uint64_t lateFrameCount, uint64_t droppedFrameCount, uint64_t flushedFrameCount)
{
	std::lock_guard<std::mutex> lock(mMutex);
	mPerformance.completionIntervalMilliseconds = std::max(completionIntervalMilliseconds, 0.0);
	mPerformance.smoothedCompletionIntervalMilliseconds = std::max(smoothedCompletionIntervalMilliseconds, 0.0);
	mPerformance.maxCompletionIntervalMilliseconds = std::max(maxCompletionIntervalMilliseconds, 0.0);
	mPerformance.lateFrameCount = lateFrameCount;
	mPerformance.droppedFrameCount = droppedFrameCount;
	mPerformance.flushedFrameCount = flushedFrameCount;
}

bool HealthTelemetry::TryRecordFramePacingStats(double completionIntervalMilliseconds, double smoothedCompletionIntervalMilliseconds,
	double maxCompletionIntervalMilliseconds, uint64_t lateFrameCount, uint64_t droppedFrameCount, uint64_t flushedFrameCount)
{
	std::unique_lock<std::mutex> lock(mMutex, std::try_to_lock);
	if (!lock.owns_lock())
		return false;

	mPerformance.completionIntervalMilliseconds = std::max(completionIntervalMilliseconds, 0.0);
	mPerformance.smoothedCompletionIntervalMilliseconds = std::max(smoothedCompletionIntervalMilliseconds, 0.0);
	mPerformance.maxCompletionIntervalMilliseconds = std::max(maxCompletionIntervalMilliseconds, 0.0);
	mPerformance.lateFrameCount = lateFrameCount;
	mPerformance.droppedFrameCount = droppedFrameCount;
	mPerformance.flushedFrameCount = flushedFrameCount;
	return true;
}

void HealthTelemetry::RecordRuntimeEventQueueMetrics(const std::string& queueName, std::size_t depth, std::size_t capacity,
	uint64_t droppedCount, double oldestEventAgeMilliseconds)
{
	std::lock_guard<std::mutex> lock(mMutex);
	mRuntimeEvents.queue.queueName = queueName;
	mRuntimeEvents.queue.depth = depth;
	mRuntimeEvents.queue.capacity = capacity;
	mRuntimeEvents.queue.droppedCount = droppedCount;
	mRuntimeEvents.queue.oldestEventAgeMilliseconds = std::max(oldestEventAgeMilliseconds, 0.0);
}

bool HealthTelemetry::TryRecordRuntimeEventQueueMetrics(const std::string& queueName, std::size_t depth, std::size_t capacity,
	uint64_t droppedCount, double oldestEventAgeMilliseconds)
{
	std::unique_lock<std::mutex> lock(mMutex, std::try_to_lock);
	if (!lock.owns_lock())
		return false;

	mRuntimeEvents.queue.queueName = queueName;
	mRuntimeEvents.queue.depth = depth;
	mRuntimeEvents.queue.capacity = capacity;
	mRuntimeEvents.queue.droppedCount = droppedCount;
	mRuntimeEvents.queue.oldestEventAgeMilliseconds = std::max(oldestEventAgeMilliseconds, 0.0);
	return true;
}

void HealthTelemetry::RecordRuntimeEventDispatchStats(std::size_t dispatchedEvents, std::size_t handlerInvocations,
	std::size_t handlerFailures, double dispatchDurationMilliseconds)
{
	std::lock_guard<std::mutex> lock(mMutex);
	++mRuntimeEvents.dispatch.dispatchCallCount;
	mRuntimeEvents.dispatch.dispatchedEventCount += static_cast<uint64_t>(dispatchedEvents);
	mRuntimeEvents.dispatch.handlerInvocationCount += static_cast<uint64_t>(handlerInvocations);
	mRuntimeEvents.dispatch.handlerFailureCount += static_cast<uint64_t>(handlerFailures);
	mRuntimeEvents.dispatch.lastDispatchDurationMilliseconds = std::max(dispatchDurationMilliseconds, 0.0);
	mRuntimeEvents.dispatch.maxDispatchDurationMilliseconds = std::max(
		mRuntimeEvents.dispatch.maxDispatchDurationMilliseconds,
		mRuntimeEvents.dispatch.lastDispatchDurationMilliseconds);
}

bool HealthTelemetry::TryRecordRuntimeEventDispatchStats(std::size_t dispatchedEvents, std::size_t handlerInvocations,
	std::size_t handlerFailures, double dispatchDurationMilliseconds)
{
	std::unique_lock<std::mutex> lock(mMutex, std::try_to_lock);
	if (!lock.owns_lock())
		return false;

	++mRuntimeEvents.dispatch.dispatchCallCount;
	mRuntimeEvents.dispatch.dispatchedEventCount += static_cast<uint64_t>(dispatchedEvents);
	mRuntimeEvents.dispatch.handlerInvocationCount += static_cast<uint64_t>(handlerInvocations);
	mRuntimeEvents.dispatch.handlerFailureCount += static_cast<uint64_t>(handlerFailures);
	mRuntimeEvents.dispatch.lastDispatchDurationMilliseconds = std::max(dispatchDurationMilliseconds, 0.0);
	mRuntimeEvents.dispatch.maxDispatchDurationMilliseconds = std::max(
		mRuntimeEvents.dispatch.maxDispatchDurationMilliseconds,
		mRuntimeEvents.dispatch.lastDispatchDurationMilliseconds);
	return true;
}

void HealthTelemetry::RecordPersistenceWriteResult(bool succeeded, const std::string& targetKind, const std::string& targetPath,
	const std::string& reason, const std::string& errorMessage, bool newerRequestPending)
{
	std::lock_guard<std::mutex> lock(mMutex);
	if (succeeded)
		++mPersistence.writeSuccessCount;
	else
		++mPersistence.writeFailureCount;
	mPersistence.lastWriteSucceeded = succeeded;
	mPersistence.unsavedChanges = !succeeded || newerRequestPending;
	mPersistence.newerRequestPending = newerRequestPending;
	mPersistence.lastTargetKind = targetKind;
	mPersistence.lastTargetPath = targetPath;
	mPersistence.lastReason = reason;
	mPersistence.lastErrorMessage = errorMessage;
}

bool HealthTelemetry::TryRecordPersistenceWriteResult(bool succeeded, const std::string& targetKind, const std::string& targetPath,
	const std::string& reason, const std::string& errorMessage, bool newerRequestPending)
{
	std::unique_lock<std::mutex> lock(mMutex, std::try_to_lock);
	if (!lock.owns_lock())
		return false;

	if (succeeded)
		++mPersistence.writeSuccessCount;
	else
		++mPersistence.writeFailureCount;
	mPersistence.lastWriteSucceeded = succeeded;
	mPersistence.unsavedChanges = !succeeded || newerRequestPending;
	mPersistence.newerRequestPending = newerRequestPending;
	mPersistence.lastTargetKind = targetKind;
	mPersistence.lastTargetPath = targetPath;
	mPersistence.lastReason = reason;
	mPersistence.lastErrorMessage = errorMessage;
	return true;
}

HealthTelemetry::SignalStatusSnapshot HealthTelemetry::GetSignalStatusSnapshot() const
{
	std::lock_guard<std::mutex> lock(mMutex);
	return mSignalStatus;
}

HealthTelemetry::VideoIOStatusSnapshot HealthTelemetry::GetVideoIOStatusSnapshot() const
{
	std::lock_guard<std::mutex> lock(mMutex);
	return mVideoIOStatus;
}

HealthTelemetry::PerformanceSnapshot HealthTelemetry::GetPerformanceSnapshot() const
{
	std::lock_guard<std::mutex> lock(mMutex);
	return mPerformance;
}

HealthTelemetry::RuntimeEventMetricsSnapshot HealthTelemetry::GetRuntimeEventMetricsSnapshot() const
{
	std::lock_guard<std::mutex> lock(mMutex);
	return mRuntimeEvents;
}

HealthTelemetry::PersistenceSnapshot HealthTelemetry::GetPersistenceSnapshot() const
{
	std::lock_guard<std::mutex> lock(mMutex);
	return mPersistence;
}

HealthTelemetry::Snapshot HealthTelemetry::GetSnapshot() const
{
	std::lock_guard<std::mutex> lock(mMutex);

	Snapshot snapshot;
	snapshot.signal = mSignalStatus;
	snapshot.videoIO = mVideoIOStatus;
	snapshot.performance = mPerformance;
	snapshot.runtimeEvents = mRuntimeEvents;
	snapshot.persistence = mPersistence;
	return snapshot;
}
