//========= Copyright Valve Corporation, All rights reserved. ============//
//
//=======================================================================================//

#include "sv_recordingsession.h"
#include "sv_recordingsessionmanager.h"
#include "sv_replaycontext.h"
#include "sv_filepublish.h"
#include "sv_recordingsessionblock.h"
#include "vstdlib/jobthread.h"
#include "fmtstr.h"
#include "sv_fileservercleanup.h"
#include <time.h>

// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"

//----------------------------------------------------------------------------------------

#ifdef _DEBUG
ConVar replay_simulate_expired_sessions( "replay_simulate_expired_sessions", "0", FCVAR_DONTRECORD,
	"Simulate expired replay session data - the value of this cvar should be between 0 and 100 and is a probability - any cleanup done (via end of round cleanup or explicit replay_docleanup) will use this value to determine whether data is expired.  E.g, use a value of 100 to delete all sessions, or 50 for a 50 chance of a given session being considered expired.",
	true, 0.0f, true, 100.0f );
#endif

//----------------------------------------------------------------------------------------

CServerRecordingSession::CServerRecordingSession( IReplayContext *pContext )
:	CBaseRecordingSession( pContext ),
	m_bReplaysRequested( false ),
	m_nLifeSpan( 0 )
{
}

CServerRecordingSession::~CServerRecordingSession()
{
}

bool CServerRecordingSession::Read( KeyValues *pIn )
{
	if ( !BaseClass::Read( pIn ) )
		return false;

	m_nLifeSpan = pIn->GetInt( "lifespan", 0 );

	KeyValues *pRecordTimeSubKey = pIn->FindKey( "record_time" );
	if ( pRecordTimeSubKey )
	{
		m_RecordTime.Read( pRecordTimeSubKey );
	}

	return true;
}

void CServerRecordingSession::Write( KeyValues *pOut )
{
	BaseClass::Write( pOut );

	pOut->SetInt( "lifespan", m_nLifeSpan );

	KeyValues *pRecordTime = new KeyValues( "record_time" );
	pOut->AddSubKey( pRecordTime );
	m_RecordTime.Write( pRecordTime );
}

void CServerRecordingSession::OnDelete()
{
	BaseClass::OnDelete();

	SV_GetFileserverCleaner()->MarkFileForDelete( GetFilename() );
}

void CServerRecordingSession::SetLocked( bool bLocked )
{
	BaseClass::SetLocked( bLocked );

	// Propagate to contained blocks
	FOR_EACH_VEC( m_vecBlocks, i )
	{
		m_vecBlocks[ i ]->SetLocked( bLocked );
	}
}

void CServerRecordingSession::PopulateWithRecordingData( int nCurrentRecordingStartTick )
{
	BaseClass::PopulateWithRecordingData( nCurrentRecordingStartTick );

	// Create a new session name
	m_strName = SV_GetRecordingSessionManager()->GetNewSessionName();

	// Cache current date/time and life-span
	extern ConVar replay_data_lifespan;
	m_nLifeSpan = replay_data_lifespan.GetInt() * 24 * 3600;
	m_RecordTime.InitDateAndTimeToNow();
}

bool CServerRecordingSession::ShouldDitchSession() const
{
	return BaseClass::ShouldDitchSession() || !m_bReplaysRequested;
}

#ifdef _DEBUG
void CServerRecordingSession::VerifyLocks()
{
	const bool bLocked = IsLocked();
	FOR_EACH_VEC( m_vecBlocks, i )
	{
		AssertMsg( m_vecBlocks[ i ]->IsLocked() == bLocked, "Parent/child locks out of sync.  The block probably needs to inherit the parent's lock value on creation." );
	}
}
#endif

double CServerRecordingSession::GetSecondsToExpiration() const
{
	tm recordtime_tm;
	V_memset( &recordtime_tm, 0, sizeof( recordtime_tm ) );

	int nDay, nMonth, nYear;
	m_RecordTime.GetDate( nDay, nMonth, nYear );
	recordtime_tm.tm_mday = nDay;
	recordtime_tm.tm_mon = nMonth - 1;
	recordtime_tm.tm_year = nYear - 1900;

	int nHour, nMin, nSec;
	m_RecordTime.GetTime( nHour, nMin, nSec );
	recordtime_tm.tm_hour = nHour;
	recordtime_tm.tm_min = nMin;
	recordtime_tm.tm_sec = nSec;

	time_t recordtime = mktime( &recordtime_tm );

	time_t nowtime;
	time( &nowtime );

	double delta = m_nLifeSpan - difftime( nowtime, recordtime );

#ifdef DBGFLAG_ASSERT
	tm *pTest = localtime( &recordtime );
	Assert( recordtime_tm.tm_mday == pTest->tm_mday );
	Assert( recordtime_tm.tm_mon == pTest->tm_mon );
	Assert( recordtime_tm.tm_year == pTest->tm_year );
	Assert( recordtime_tm.tm_hour == pTest->tm_hour );
	Assert( recordtime_tm.tm_min == pTest->tm_min );
	Assert( recordtime_tm.tm_sec == pTest->tm_sec );
#endif

	return delta;
}

bool CServerRecordingSession::SessionExpired() const
{
#ifdef _DEBUG
	if ( ( 1+rand()%100 ) <= replay_simulate_expired_sessions.GetInt() )
		return true;
#endif

	return GetSecondsToExpiration() <= 0.0;
}

//----------------------------------------------------------------------------------------