///////////////////////////////////////////////////////////////////////////////
/// @file deFileReal.cpp
///
/// @brief real 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>
#elif linux
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <utime.h>
#include <ctime>
#endif    

#include "deFile_priv.hpp"

deFileReal::deFileReal()
{
	//set the defaults
#ifdef _WIN32
  priv_FileHandle = (HANDLE)-1;
#elif linux
  priv_FileHandle = -1;
#else
#error "deFileReal Destruction not implemented for OS"
#endif
	priv_OpenFlags = 0;
	priv_Filename[0] = 0;
}

deFileReal::~deFileReal()
{
	//close any open file
	this->Close();
}

void* deFileReal::GetInterface(IdeFile::interface_t i)
{
	if (i == IdeFile::iface_IdeFile)
		return (IdeFile*)(this);
	if (i == IdeFile::iface_IdeFileReal)
		return (IdeFileReal*)(this);
	return NULL;
}

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

deBoolean deFileReal::Open(IdeFileSystem *BaseFS, char *Filename, long OpenFlags)
{
	deFSReal *	RealFS;

	//if a file is open, fail
     if((long)priv_FileHandle != -1)
		return deFALSE;

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

	//get the realfs interface
	RealFS = (deFSReal *)BaseFS->GetInterface(IdeFileSystem::iface_IdeFSReal);
	if(!RealFS)
		return deFALSE;

	//open up the file
	return RealFS->Open(this, Filename, OpenFlags);
}

deBoolean deFileReal::Close()
{
	deBoolean Ret;

	//make sure the handle is valid
	if((long)priv_FileHandle == -1)
		return deFALSE;

#ifdef _WIN32

	//close the file
	Ret = (deBoolean)CloseHandle(priv_FileHandle);

  	priv_FileHandle = (HANDLE)-1;
   
#elif linux
    close(priv_FileHandle);
	priv_FileHandle = -1;
#else
#error File Closing not written for OS
#endif

	//reset a few variables
	priv_OpenFlags = 0;
	priv_Filename[0] = 0;

	return Ret;
}

long deFileReal::Read(void *Buffer, long Length)
{
	long ReadLength;

	//make sure the handle is valid
	if((long)priv_FileHandle == -1)
		return -1;

	//make sure it can be read
	if(!(priv_OpenFlags & deFile_Open_Read))
		return -1;

#ifdef _WIN32

	//read the data
	ReadFile(priv_FileHandle, Buffer, Length, (DWORD *)&ReadLength, 0);

#elif linux
  ReadLength = read(priv_FileHandle,Buffer,Length);
#else
#error File Reading not written for OS
#endif

	//return the length read (incase there was an issue)
	return ReadLength;
}

long deFileReal::Write(void *Buffer, long Length)
{
	long WriteLength;
	
	//make sure the handle is valid
	if((long)priv_FileHandle == -1)
		return -1;

	//make sure it can be written to
	if(!(priv_OpenFlags & deFile_Open_Write))
		return -1;

#ifdef _WIN32

	//write the data
	WriteFile(priv_FileHandle, Buffer, Length, (DWORD *)&WriteLength, 0);

#elif linux
  WriteLength = write(priv_FileHandle,Buffer,Length);
#else
#error File writing not written for OS
#endif

	//return the length written (incase there was an issue)
	return WriteLength;
}

long deFileReal::GetPosition()
{
	//get the current position in the file

	//make sure the handle is valid
	if((long)priv_FileHandle == -1)
		return -1;

#ifdef _WIN32

	//return the current position
	return SetFilePointer(priv_FileHandle, 0, NULL, SEEK_CUR);

#elif linux
  return lseek(priv_FileHandle,0,SEEK_CUR);
#else
#error File position setting not written for OS
#endif
}

long deFileReal::SetPosition(long NewPosition, Seek SeekType)
{
	//set the current file position
	DWORD MoveMethod;

	//make sure the handle is valid
	if((long)priv_FileHandle == -1)
		return -1;

	//get the method to move
	switch(SeekType)
	{
	case deFile_Seek_Start:
		MoveMethod = SEEK_SET;
		break;

	case deFile_Seek_End:
		MoveMethod = SEEK_END;
		break;

	case deFile_Seek_Current:
		MoveMethod = SEEK_CUR;
		break;

	default:
		return -1;
	}
  
#ifdef _WIN32
	//set the pointer
	return SetFilePointer(priv_FileHandle, NewPosition, NULL, MoveMethod);

#elif linux
  return lseek(priv_FileHandle,NewPosition,MoveMethod);
#else
#error Setting file pointer not written for OS
#endif

}

long deFileReal::GetSize()
{
  //get the size of the current file

	//if no handle, fail
	if((long)priv_FileHandle == -1)
		return deFALSE;

#ifdef _WIN32

	//get the size
	return GetFileSize(priv_FileHandle, NULL);

#elif linux
  struct stat Info;
  if(fstat(priv_FileHandle,&Info) == -1)
    return deFALSE;

  return Info.st_size;
#else
#error Getting file size not written for OS
#endif
}

deBoolean deFileReal::SetSize(long NewSize)
{
#ifdef _WIN32
	//set the size of the file
	long	CurrentFilePos;

	//make sure the handle is valid
	if((long)priv_FileHandle == -1)
		return deFALSE;

	//make sure it can be written to
	if(!(priv_OpenFlags & deFile_Open_Write))
		return deFALSE;

	//get the current position
	CurrentFilePos = SetFilePointer(priv_FileHandle, 0, NULL, SEEK_CUR);

	//seek to where the end is
	SetFilePointer(priv_FileHandle, NewSize, NULL, SEEK_SET);

	//if the old position is larger than the new then move the old position back to
	//the end of the file
	if(CurrentFilePos > NewSize)
		CurrentFilePos = NewSize;

	//set the end of the file
	if(!SetEndOfFile(priv_FileHandle))
		return deFALSE;

	//put the pointer back
	SetFilePointer(priv_FileHandle, CurrentFilePos, NULL, SEEK_SET);

#elif linux
	//set the size of the file
	long	CurrentFilePos;

	//make sure the handle is valid
	if((long)priv_FileHandle == -1)
		return deFALSE;

	//make sure it can be written to
	if(!(priv_OpenFlags & deFile_Open_Write))
		return deFALSE;

   if(ftruncate(priv_FileHandle,NewSize) == -1)
    return deFALSE;
   return deTRUE;
#else
#error Setting file size not written for OS
#endif

	return deTRUE;
}

s64 deFileReal::GetTime()
{
#ifdef _WIN32
	//get the time the file was last written to
	s64	Ret;
  
	//if no handle, fail
	if((long)priv_FileHandle == -1)
		return 0;

	if(!GetFileTime(priv_FileHandle, NULL, NULL, (FILETIME *)&Ret))
		return 0;

    	return Ret;
#elif linux
  struct stat Info;
  if(fstat(priv_FileHandle,&Info) == -1)
    return deFALSE;      

  return Info.st_mtime;
#else
#error Getting file time not written for OS
#endif 
}

deBoolean deFileReal::SetTime(s64 Time)
{
	//make sure the handle is valid
	if((long)priv_FileHandle == -1)
		return deFALSE;

	//make sure it can be written to
	if(!(priv_OpenFlags & deFile_Open_Write))
		return deFALSE;
#ifdef _WIN32
	//set the time the file was last written to
	if(!SetFileTime(priv_FileHandle, (FILETIME *)&Time, (FILETIME *)&Time, (FILETIME *)&Time))
		return deFALSE;
#elif linux
  utimbuf NewTime;
  NewTime.actime = Time;
  NewTime.modtime = Time;
  utime(priv_FilePath,&NewTime);
#else
#error Setting file time not written for OS
#endif

	return deTRUE;
}

deBoolean deFileReal::GetProperties(deFileProperties *Properties)
{
	//get the properties for the current file

	//make sure the handle is valid
	if((long)priv_FileHandle == -1)
		return deFALSE;

	//make sure it can be written to
	if(!(priv_OpenFlags & deFile_Open_Write))
		return deFALSE;

	//get the properties
	Properties->Time = GetTime();
	Properties->Filename = priv_Filename;
	Properties->Size = GetSize();

	return deTRUE;
}

long deFileReal::GetOpenFlags()
{
	return priv_OpenFlags;
}
