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

#include "baserecordingsession.h"
#include "baserecordingsessionblock.h"
#include "replay/irecordingsessionblockmanager.h"
#include "replay/replayutils.h"
#include "replay/iclientreplaycontext.h"
#include "replay/shared_defs.h"
#include "KeyValues.h"
#include "replay/replayutils.h"
#include "replay/ireplaycontext.h"
#include "filesystem.h"
#include "iserver.h"
#include "replaysystem.h"
#include "utlbuffer.h"

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

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

CBaseRecordingSession::CBaseRecordingSession( IReplayContext *pContext )
:	m_pContext( pContext ),
	m_bRecording( false ),
	m_bAutoDelete( false ),
	m_bBlocksLoaded( false ),
	m_flStartTime( 0.0f )
{
}

CBaseRecordingSession::~CBaseRecordingSession()
{
}

void CBaseRecordingSession::AddBlock( CBaseRecordingSessionBlock *pBlock )
{
	AddBlock( pBlock, false );
}

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

	m_strName = pIn->GetString( "name" );
	
	if ( m_strName.IsEmpty() )
	{
		CUtlBuffer buf;
		pIn->RecursiveSaveToFile( buf, 0 );
		IF_REPLAY_DBG( Warning( "Session with no session name found - aborting load for this session.  Data:\n---\n%s\n---\n", (const char *)buf.Base() ) );
		return false;
	}

	m_bRecording = pIn->GetBool( "recording" );
	m_strBaseDownloadURL = pIn->GetString( "base_download_url" );
	m_nServerStartRecordTick = pIn->GetInt( "server_start_record_tick", -1 );

	return true;
}

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

	pOut->SetString( "name", m_strName.Get() );
	pOut->SetInt( "recording", m_bRecording ? 1 : 0 );
	pOut->SetString( "base_download_url", m_strBaseDownloadURL.Get() );
	pOut->SetInt( "server_start_record_tick", m_nServerStartRecordTick );
}

const char *CBaseRecordingSession::GetSubKeyTitle() const
{
	return m_strName.Get();
}

const char *CBaseRecordingSession::GetPath() const
{
	return Replay_va( "%s%s%c", m_pContext->GetBaseDir(), SUBDIR_SESSIONS, CORRECT_PATH_SEPARATOR );
}

const char *CBaseRecordingSession::GetSessionInfoURL() const
{
	return Replay_va( "%s%s.%s", m_strBaseDownloadURL.Get(), m_strName.Get(), GENERIC_FILE_EXTENSION );
}

void CBaseRecordingSession::LoadBlocksForSession()
{
	if ( m_bBlocksLoaded )
		return;

	IRecordingSessionBlockManager *pBlockManager = m_pContext->GetRecordingSessionBlockManager();

	// Peek in directory and load files based on what's there
	FileFindHandle_t hFind;
	CFmtStr fmtPath( "%s%s*.%s", pBlockManager->GetBlockPath(), m_strName.Get(), GENERIC_FILE_EXTENSION );
	const char *pFilename = g_pFullFileSystem->FindFirst( fmtPath.Access(), &hFind );
	while ( pFilename )
	{
		// Load the block - this will add the block to this session
		pBlockManager->LoadBlockFromFileName( pFilename, this );	

		// Get next file
		pFilename = g_pFullFileSystem->FindNext( hFind );
	}

	// Blocks loaded
	m_bBlocksLoaded = true;
}

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

	// Dynamically load blocks if necessary, then delete from the block manager and from disk
	DeleteBlocks();
}

void CBaseRecordingSession::DeleteBlocks()
{
	if ( !m_bBlocksLoaded )
	{
		// Load blocks now based on the session name
		LoadBlocksForSession();
	}

	// Delete all blocks associated w/ the session
	FOR_EACH_VEC( m_vecBlocks, i )
	{
		CBaseRecordingSessionBlock *pCurBlock = m_vecBlocks[ i ];
		m_pContext->GetRecordingSessionBlockManager()->DeleteBlock( pCurBlock );
	}
}

void CBaseRecordingSession::OnUnload()
{
	BaseClass::OnUnload();

	FOR_EACH_VEC( m_vecBlocks, i )
	{
		CBaseRecordingSessionBlock *pCurBlock = m_vecBlocks[ i ];
		m_pContext->GetRecordingSessionBlockManager()->UnloadBlock( pCurBlock );
	}
}

void CBaseRecordingSession::PopulateWithRecordingData( int nCurrentRecordingStartTick )
{
	Assert( nCurrentRecordingStartTick >= 0 );

	m_strBaseDownloadURL = Replay_GetDownloadURL();
	m_bRecording = true;
	m_nServerStartRecordTick = nCurrentRecordingStartTick;
}

void CBaseRecordingSession::AddBlock( CBaseRecordingSessionBlock *pBlock, bool bFlagForFlush )
{
	Assert( pBlock->m_hSession == GetHandle() );

	Assert( m_vecBlocks.Find( pBlock ) == m_vecBlocks.InvalidIndex() );
	m_vecBlocks.Insert( pBlock );

	if ( bFlagForFlush )
	{
		// Mark as dirty
		m_pContext->GetRecordingSessionManager()->FlagSessionForFlush( this, false );
	}

	m_bBlocksLoaded = true;
}

int CBaseRecordingSession::FindBlock( CBaseRecordingSessionBlock *pBlock ) const
{
	int itResult = m_vecBlocks.Find( pBlock );
	if ( itResult == m_vecBlocks.InvalidIndex() )
		return -1;

	return itResult;
}

bool CBaseRecordingSession::ShouldDitchSession() const
{
	return m_bAutoDelete;
}

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

bool CBaseRecordingSession::CLessFunctor::Less( const CBaseRecordingSessionBlock *pSrc1, const CBaseRecordingSessionBlock *pSrc2, void *pContext )
{
	return pSrc1->m_iReconstruction < pSrc2->m_iReconstruction;
}

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