///////////////////////////////////////////////////////////////////////////////
/// @file deFileVirtual.cpp
///
/// @brief virtual file interface
///
/// @author Lightning
///
/// This file is the intellectual property of Novus Delta, LLC.. Usage of the
/// contents of this file is subject to the Destiny3D Member License which
/// can be found at http://www.destiny3d.com.  Any other usage is prohibited.
///
/// This file is distributed "AS IS" without warranty of any kind.  Novus
/// Delta, LLC. does not guarantee the fitness of the contents of this file
/// for any particular purpose.
///
/// Copyright (C) 2001-2003 Novus Delta, LLC. All Rights Reserved.
///
/// <hr>
///                                 Change History
/// <hr>
///
/// @date May 2003
/// @author Lightning
/// @remarks Creation
///
///////////////////////////////////////////////////////////////////////////////

#ifdef _WIN32
//#include <windows.h>
#endif

#include "deFile_priv.hpp"
#include "deFileSystem_priv.hpp"

//the size of the buffer used while moving data around (1MB)
#define MoveDataSize	(1024 * 1024)

deFileVirtual::deFileVirtual()
{
	//set everything to the defaults
	priv_BaseFS = NULL;
	priv_CurrentFile = NULL;
	priv_CurrentPosition = 0;
}

deFileVirtual::~deFileVirtual()
{
	//close any file that may exist
	this->Close();
}

void* deFileVirtual::GetInterface(IdeFile::interface_t i)
{
	if (i == IdeFile::iface_IdeFile)
		return (IdeFile*)(this);

	if (i == IdeFile::iface_IdeFileVirtual)
		return (IdeFileVirtual*)(this);

	return NULL;
}

int deFileVirtual::Release()
{
	delete this;
	return 0;
}

deBoolean deFileVirtual::Open(IdeFileSystem *BaseFS, char *Filename, long OpenFlags)
{
	deFSVirtual *	VirtualFS;
	
	//if a file is already open, fail
	if(priv_CurrentFile)
		return deFALSE;

	//make sure the basefs is valid
	if(!BaseFS)
		return deFALSE;

	//get a virtual fs interface
	VirtualFS = (deFSVirtual *)BaseFS->GetInterface(IdeFileSystem::iface_IdeFSVirtual);
	if(!VirtualFS)
		return deFALSE;

	//open up a virtual file
	if(!VirtualFS->Open(this, Filename, OpenFlags))
		return deFALSE;

	//set our base file
	priv_BaseFS = VirtualFS;

	//all doen
	return deTRUE;
}

deBoolean deFileVirtual::Close()
{
	deBoolean Ret;

	//close an opened file

	//if no current file, fail
	if(!priv_CurrentFile)
		return deFALSE;

	//tell the VFS to close the file
	Ret = priv_BaseFS->Close(this);

	//reset any data about a file
	priv_CurrentFile = NULL;
	priv_FileFlags = 0;
	priv_CurrentPosition = 0;

	return Ret;
}

long deFileVirtual::Read(void *Buffer, long Length)
{
	//read data from an opened file
	return priv_BaseFS->Read(this, Buffer, Length);
}

long deFileVirtual::Write(void *Buffer, long Length)
{
	//write to an open file
	return priv_BaseFS->Write(this, Buffer, Length);
}

long deFileVirtual::GetPosition()
{
	//return the current position in a file

	//if no file, return -1 for failure
	if(!priv_CurrentFile)
		return -1;

	//return the current position
	return priv_CurrentPosition;
}

long deFileVirtual::SetPosition(long NewPosition, Seek SeekType)
{
	//set the new position in the file

	//if no file, fail
	if(!priv_CurrentFile)
		return -1;

	//set the position based on the seek type
	switch(SeekType)
	{
		case deFile_Seek_Start:
			//set CurrentPosition to the new position

			//make sure NewPosition is not invalid
			if(NewPosition < 0)
				return -1;

			//set it
			priv_CurrentPosition = NewPosition;
			break;

		case deFile_Seek_Current:
			//add to the current position the new position

			//make sure it isn't invalid
			if((priv_CurrentPosition + NewPosition) < 0)
				return -1;

			//set it
			priv_CurrentPosition = priv_CurrentPosition + NewPosition;
			break;

		case deFile_Seek_End:
			//take the length of the file and subtract the new position from it

			//make sure it isn't invalid
			if((priv_CurrentFile->Size - NewPosition) < 0)
				return -1;

			//set it
			priv_CurrentPosition = priv_CurrentFile->Size - NewPosition;
			break;

		default:
			//invalid seek
			return -1;
	};

	//return the new position
	return priv_CurrentPosition;
}

long deFileVirtual::GetSize()
{
	//get the size of a file

	//if no file, return a -1
	if(!priv_CurrentFile)
		return -1;

	//return the file's size
	return priv_CurrentFile->Size;
}

deBoolean deFileVirtual::SetSize(long NewSize)
{
	//a function to set the size of a file

	//call the internal SetSize function that allows for a callback
	return (priv_BaseFS->SetSize(this, NewSize, NULL) == deFSPROGRESS_CONTINUE);
}

//internal function for setting the size due to needing to pass along the callback
DWORD deFileVirtual::SetSize(long NewSize, deFSPROGRESS *StatusCallback)
{
	//change the size of a file

	//if no file, fail
	if(!priv_CurrentFile)
		return deFSPROGRESS_CANCEL;

	//return what the base does
	return priv_BaseFS->SetSize(this, NewSize, StatusCallback);
}

s64 deFileVirtual::GetTime()
{
	//get the time of a file

	//if no file, return a time of 0
	if(!priv_CurrentFile)
		return 0;

	//return the file's time
	return priv_CurrentFile->Time;
}

deBoolean deFileVirtual::SetTime(s64 Time)
{
	//if no file, fail
	if(!priv_CurrentFile)
		return deFALSE;

	//set the time and return true
	priv_CurrentFile->Time = Time;
	return deTRUE;
}

deBoolean deFileVirtual::GetProperties(deFileProperties *Properties)
{
	//return the properties to a file

	//if no current file, fail
	if(!priv_CurrentFile)
		return deFALSE;

	//fill in the properties
	Properties->Filename = priv_CurrentFile->FileName;	//note, the user is responsible for making a copy of the name
	Properties->Flags = 0;					//no flags for the file
	Properties->Size = priv_CurrentFile->Size;	//the size of the file
	Properties->Time = priv_CurrentFile->Time;	//the time of the file
	return deTRUE;
}

long deFileVirtual::GetOpenFlags()
{
	return priv_FileFlags;
}
