//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//=============================================================================//

#include "stdafx.h"
#include <sys\types.h>
#include <sys\stat.h>
#include "ChunkFile.h"
#include "Prefab3D.h"
#include "Options.h"
#include "History.h"
#include "MapGroup.h"
#include "MapWorld.h"
#include "GlobalFunctions.h"

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


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CPrefab3D::CPrefab3D()
{
	m_pWorld = NULL;
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CPrefab3D::~CPrefab3D()
{
	FreeData();
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CPrefab3D::FreeData()
{
	delete m_pWorld;
	m_pWorld = NULL;
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CMapClass *CPrefab3D::Create(void)
{
	if (!IsLoaded() && (Load() == -1))
	{
		return(NULL);
	}

	CMapClass *pCopy;
	CMapClass *pOriginal;

	//
	// Check for just one object - if only one, don't group it.
	//
	if (m_pWorld->GetChildCount() == 1)
	{
		
		pOriginal = m_pWorld->GetChildren()->Element(0);
		pCopy = pOriginal->Copy(false);
	}
	else
	{
		// Original object is world
		pOriginal = m_pWorld;

		// New object is a new group
		pCopy = (CMapClass *)new CMapGroup;
	}

	//
	// Copy children from original (if any).
	//
	pCopy->CopyChildrenFrom(pOriginal, false);

	// HACK: must calculate bounds here due to a hack in CMapClass::CopyChildrenFrom
	pCopy->CalcBounds();

	return(pCopy);
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : point - Where to center the prefab.
// Output : CMapClass
//-----------------------------------------------------------------------------
CMapClass *CPrefab3D::CreateAtPoint(const Vector &point)
{
	//
	// Create the prefab object. It will either be a single object
	// or a group containing the prefab objects.
	//
	CMapClass *pObject = Create();

	if (pObject != NULL)
	{
		//
		// Move the prefab center to match the given point.
		//
		Vector move = point;
		Vector center;
		pObject->GetBoundsCenter(center);
		for (int i = 0; i < 3; i++)
		{
			move[i] -= center[i];
		}

		BOOL bOldLock = Options.SetLockingTextures(TRUE);
		pObject->TransMove(move);
		Options.SetLockingTextures(bOldLock);
	}

	return(pObject);
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : 
// Output : 
//-----------------------------------------------------------------------------
CMapClass *CPrefab3D::CreateAtPointAroundOrigin( Vector const &point )
{
	//
	// Create the prefab object. It will either be a single object
	// or a group containing the prefab objects.
	//
	CMapClass *pObject = Create();

	if( !pObject )
		return NULL;

	Vector move = point;

	BOOL bOldLock = Options.SetLockingTextures( TRUE );
	pObject->TransMove( move );
	Options.SetLockingTextures( bOldLock );

	return ( pObject );
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : pBox - 
// Output : 
//-----------------------------------------------------------------------------
CMapClass *CPrefab3D::CreateInBox(BoundBox *pBox)
{
	//
	// Create the prefab object. It will either be a single object
	// or a group containing the prefab objects.
	//
	CMapClass *pObject = Create();

	if (pObject != NULL)
	{
		//
		// Scale the prefab to match the box bounds.
		//
		Vector NewSize;
		pBox->GetBoundsSize(NewSize);

		Vector CurSize;
		pObject->GetBoundsSize(CurSize);

		Vector scale;
		for (int i = 0; i < 3; i++)
		{
			scale[i] = NewSize[i] / CurSize[i];
		}

		Vector zero(0, 0, 0);
		pObject->TransScale(zero, scale);

		//
		// Move the prefab center to match the box center.
		//
		Vector move;
		pBox->GetBoundsCenter(move);

		Vector center;
		pObject->GetBoundsCenter(center);
		for (int i = 0; i < 3; i++)
		{
			move[i] -= center[i];
		}

		BOOL bOldLock = Options.SetLockingTextures(TRUE);
		pObject->TransMove(move);
		Options.SetLockingTextures(bOldLock);
	}

	return(pObject);
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CPrefab3D::CenterOnZero()
{
	Vector ptCenter;
	m_pWorld->GetBoundsCenter(ptCenter);
	ptCenter[0] = -ptCenter[0];
	ptCenter[1] = -ptCenter[1];
	ptCenter[2] = -ptCenter[2];

	BOOL bOldLock = Options.SetLockingTextures(TRUE);
	m_pWorld->TransMove(ptCenter);
	Options.SetLockingTextures(bOldLock);
}


//-----------------------------------------------------------------------------
// Purpose: Returns true if the prefab data has been loaded from disk, false if not.
//-----------------------------------------------------------------------------
bool CPrefab3D::IsLoaded(void)
{
	return (m_pWorld != NULL);
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CPrefabRMF::CPrefabRMF()
{
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CPrefabRMF::~CPrefabRMF()
{
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : file - 
//			dwFlags - 
// Output : int
//-----------------------------------------------------------------------------
int CPrefabRMF::DoLoad(std::fstream& file, DWORD dwFlags)
{
	int iRvl;

	GetHistory()->Pause();

	AddMRU(this);

	if(m_pWorld)
		delete m_pWorld;
	m_pWorld = new CMapWorld( NULL );

	// read data
	if(dwFlags & lsMAP)
		iRvl = m_pWorld->SerializeMAP(file, FALSE);
	else
		iRvl = m_pWorld->SerializeRMF(file, FALSE);

	// error?
	if(iRvl == -1)
	{
		GetHistory()->Resume();
		return iRvl;
	}

	m_pWorld->CalcBounds(TRUE);

	GetHistory()->Resume();

	return 1;
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : file - 
//			dwFlags - 
// Output : int
//-----------------------------------------------------------------------------
int CPrefabRMF::DoSave(std::fstream& file, DWORD dwFlags)
{
	// save world
	if(dwFlags & lsMAP)
		return m_pWorld->SerializeMAP(file, TRUE);

	return m_pWorld->SerializeRMF(file, TRUE);
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : dwFlags - 
// Output : int
//-----------------------------------------------------------------------------
int CPrefabRMF::Load(DWORD dwFlags)
{
	//
	// Get parent library's file handle.
	//
	CPrefabLibraryRMF *pLibrary = dynamic_cast <CPrefabLibraryRMF *>(CPrefabLibrary::FindID(dwLibID));
	if (!pLibrary)
	{
		return -1;
	}

	std::fstream &file = pLibrary->m_file;
	file.seekg(dwFileOffset);

	return(DoLoad(file, dwFlags));
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : pszFilename - 
//			bLoadNow - 
//			dwFlags - 
// Output : int
//-----------------------------------------------------------------------------
int CPrefabRMF::Init(LPCTSTR pszFilename, BOOL bLoadNow, DWORD dwFlags)
{
	std::fstream file(pszFilename, std::ios::in | std::ios::binary);

	// ensure we're named
	memset(szName, 0, sizeof szName);
	strncpy(szName, pszFilename, sizeof szName - 1);
	return Init(file, bLoadNow, dwFlags);
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : file - 
//			bLoadNow - 
//			dwFlags - 
// Output : int
//-----------------------------------------------------------------------------
int CPrefabRMF::Init(std::fstream &file, BOOL bLoadNow, DWORD dwFlags)
{
	int iRvl = 1;	// start off ok
	
	if(bLoadNow)
	{
		// do load now
		iRvl = DoLoad(file, dwFlags);
	}

	if(!szName[0])
	{
		// ensure we're named
		strcpy(szName, "Prefab");
	}

	return iRvl;
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : pszFilename - 
//			dwFlags - 
// Output : int
//-----------------------------------------------------------------------------
int CPrefabRMF::Save(LPCTSTR pszFilename, DWORD dwFlags)
{
	std::fstream file(pszFilename, std::ios::out | std::ios::binary);
	return Save(file, dwFlags);
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : file - 
//			dwFlags - 
// Output : int
//-----------------------------------------------------------------------------
int CPrefabRMF::Save(std::fstream& file, DWORD dwFlags)
{
	if (!IsLoaded() && (Load() == -1))
	{
		AfxMessageBox("Couldn't Load prefab to Save it.");
		return -1;
	}

	return DoSave(file, dwFlags);
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CPrefabVMF::CPrefabVMF()
{
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CPrefabVMF::~CPrefabVMF()
{
}


//-----------------------------------------------------------------------------
// Purpose: Returns true if the prefab data has been loaded from disk, false if not.
//-----------------------------------------------------------------------------
bool CPrefabVMF::IsLoaded(void)
{
	if (m_pWorld == NULL)
	{
		return false;
	}

	//
	// We have loaded this prefab at least once this session. Check the file date/time
	// against our cached date/time to see if we need to reload it.
	//
	struct _stat info;
	if (_stat(m_szFilename, &info) == 0)
	{
		if (info.st_mtime > m_nFileTime)
		{
			return false;
		}
	}

	return true;
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : dwFlags - 
// Output : int
//-----------------------------------------------------------------------------
int CPrefabVMF::Load(DWORD dwFlags)
{
	//
	// Create a new world to hold the loaded objects.
	//
	if (m_pWorld != NULL)
	{
		delete m_pWorld;
	}

	m_pWorld = new CMapWorld( NULL );

	//
	// Open the file.
	//
	CChunkFile File;
	ChunkFileResult_t eResult = File.Open(m_szFilename, ChunkFile_Read);

	//
	// Read the file.
	//
	if (eResult == ChunkFile_Ok)
	{
		//
		// Set up handlers for the subchunks that we are interested in.
		//
		CChunkHandlerMap Handlers;
		Handlers.AddHandler("world", (ChunkHandler_t)CPrefabVMF::LoadWorldCallback, this);
		Handlers.AddHandler("entity", (ChunkHandler_t)CPrefabVMF::LoadEntityCallback, this);
		// dvs: Handlers.SetErrorHandler((ChunkErrorHandler_t)CPrefabVMF::HandleLoadError, this);

		File.PushHandlers(&Handlers);

		//CMapDoc::SetLoadingMapDoc( this );  dvs: fix - without this, no displacements in prefabs

		//
		// Read the sub-chunks. We ignore keys in the root of the file, so we don't pass a
		// key value callback to ReadChunk.
		//
		while (eResult == ChunkFile_Ok)
		{
			eResult = File.ReadChunk();
		}

		if (eResult == ChunkFile_EOF)
		{
			eResult = ChunkFile_Ok;
		}

		//CMapDoc::SetLoadingMapDoc( NULL );

		File.PopHandlers();
	}

	if (eResult == ChunkFile_Ok)
	{
		m_pWorld->PostloadWorld();
		m_pWorld->CalcBounds();

		File.Close();

		//
		// Store the file modification time to use as a cache check.
		//
		struct _stat info;
		if (_stat(m_szFilename, &info) == 0)
		{
			m_nFileTime = info.st_mtime;
		}
	}
	else
	{
		//GetMainWnd()->MessageBox(File.GetErrorText(eResult), "Error loading prefab", MB_OK | MB_ICONEXCLAMATION);
	}

	return(eResult == ChunkFile_Ok);
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : pFile - 
//			pData - 
// Output : ChunkFileResult_t
//-----------------------------------------------------------------------------
ChunkFileResult_t CPrefabVMF::LoadEntityCallback(CChunkFile *pFile, CPrefabVMF *pPrefab)
{
	CMapEntity *pEntity = new CMapEntity;

	ChunkFileResult_t eResult = pEntity->LoadVMF(pFile);

	if (eResult == ChunkFile_Ok)
	{
		CMapWorld *pWorld = pPrefab->GetWorld();
		pWorld->AddChild(pEntity);
	}

	return(ChunkFile_Ok);
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : pFile - 
//			pData - 
// Output : ChunkFileResult_t
//-----------------------------------------------------------------------------
ChunkFileResult_t CPrefabVMF::LoadWorldCallback(CChunkFile *pFile, CPrefabVMF *pPrefab)
{
	CMapWorld *pWorld = pPrefab->GetWorld();
	ChunkFileResult_t eResult = pWorld->LoadVMF(pFile);
	return(eResult);
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : pszFilename - 
//			dwFlags - 
// Output : int
//-----------------------------------------------------------------------------
int CPrefabVMF::Save(LPCTSTR pszFilename, DWORD dwFlags)
{
	return 1;
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CPrefabVMF::SetFilename(const char *szFilename)
{
	//
	// Extract the file name without the path or extension as the prefab name.
	//
	_splitpath(szFilename, NULL, NULL, szName, NULL);

	strcpy(m_szFilename, szFilename);
}