
#include "CEgFileSpec.h"

#include "CEgIOFile.h"

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

#define DIRSEP '/'

#include "EgOSUtils.h"


CEgFileSpec::CEgFileSpec() {
	
	Assign( NULL, 0 );
}

CEgFileSpec::CEgFileSpec( const char* inFileName, long inType  ) {

	Assign( NULL, 0 );
	AssignPathName( inFileName );
	SetType( inType );
}

CEgFileSpec::CEgFileSpec( const CEgFileSpec& inSpec ) {
	
	Assign( inSpec );
}

void CEgFileSpec::Assign( const CEgFileSpec& inSpec ) {

	Assign( inSpec.OSSpec(), inSpec.GetType() );
}

void CEgFileSpec::Assign( const void* inOSSpecPtr, long inType ) {

	mFileType = inType;
	
	mSpecData.Assign( (char*) inOSSpecPtr );
}

void CEgFileSpec::AssignPathName( const char* inPathName ) {

	if ( inPathName ) {
		Assign( inPathName, 0 );
	}	
}


void CEgFileSpec::AssignFolder( const char* inFolderName ) {
	
	mSpecData.Assign( (char*) EgOSUtils::sAppSpec.OSSpec() );
#if 0
	/* FIXME why the DIRSEP at the start */
	if ( inFolderName[ 0 ] != DIRSEP && mSpecData.getChar( mSpecData.length() ) != DIRSEP )
		mSpecData.Append( DIRSEP );
#endif
	mSpecData.Append( inFolderName );
	if ( mSpecData.getChar( mSpecData.length() ) != DIRSEP )
		mSpecData.Append( DIRSEP );
}
	
long CEgFileSpec::GetType() const {

	unsigned long i, j, len, type = 0;

	len = mSpecData.length();
	i = mSpecData.FindLastInstanceOf( '.' );
	j = mSpecData.FindLastInstanceOf( DIRSEP );

	if ( i > 0 && len - i <= 3 && j < i ) {
		for ( j = i; j <= len; j++ )
			type = ( type << 8 ) | mSpecData.getChar( j );
	}
	
	return type;
}

void CEgFileSpec::TypeToExt( UtilStr& ioStr, long inType ) {

	unsigned long c, i, pos = ioStr.length();
	
	for ( i = 0; i < 4; i++ ) {
		c = (inType >> i * 8) & 0xFF;
		if ( c > 32 )
			ioStr.Insert( pos, c, 1 );
	}
	if ( ioStr.length() > pos ) {
		if ( ioStr.getChar( pos + 1 ) != '.' )
			ioStr.Insert( pos, '.', 1 );
	}
}

void CEgFileSpec::SetType( long inType ) {
	mFileType = inType;
}

void CEgFileSpec::Delete() const {
	
	if ( OSSpec() ) {
		unlink( mSpecData.getCStr() );
	}
}

void CEgFileSpec::Rename( const UtilStr& inNewName ) {

	int pos;

	pos = mSpecData.FindLastInstanceOf( DIRSEP );
	mSpecData.Trunc( mSpecData.length() - pos );
	mSpecData.Append( inNewName );
}

void CEgFileSpec::MakeUnique() {

	UtilStr origName, newName;
	long i;

	// Make a copy of the original file name	
	GetFileName( origName );

	origName.Append( " " );

	// Once this spec isn't taken/used, then we're done	
	for ( i = 1; i < 10000 && Exists(); i++ ) {
		newName.Assign( origName );
		newName.Append( i );
		Rename( newName );
	}
}

void CEgFileSpec::GetFileName( UtilStr& outFileName ) const {

	int 	pos;
	
	outFileName.Wipe();

	if ( OSSpec() ) {
		// If it's a folder, temporarily remove the end '\'
		long len = mSpecData.length();
		bool isFolder = mSpecData.getChar( len ) == DIRSEP;
		if ( isFolder ) {
			pos = mSpecData.FindPrevInstanceOf( len - 1, DIRSEP ); 
			outFileName.Assign( mSpecData.getCStr() + pos, len - pos - 1 );
			}
		else {
			pos = mSpecData.FindLastInstanceOf( DIRSEP );
			outFileName.Assign( mSpecData.getCStr() + pos );
		}
	}

	pos = outFileName.FindLastInstanceOf( '.' );	// Find where ext begins

	if ( pos > 0 )
		outFileName.Keep( pos - 1 );		// Strip extension
}

void CEgFileSpec::SaveAs( const CEgFileSpec& inDestSpec ) const {
	
	if ( OSSpec() && inDestSpec.OSSpec() ) {
		inDestSpec.Delete();
		rename( (char*) OSSpec(), (char*) inDestSpec.OSSpec() );
	}
}

int CEgFileSpec::Exists() const {

	CEgIFile	iFile;
	struct		stat statdata;

	if (stat((char *) OSSpec(), &statdata) != 0)
		return 0;
	if (S_ISDIR(statdata.st_mode))
		return 2;
	else if (S_ISREG(statdata.st_mode))
		return 1;

	return 0;
}

CEgErr CEgFileSpec::Duplicate( const CEgFileSpec& inDestSpec ) const {

	CEgIOFile	oFile;
	CEgIFile	iFile;

	// Open the source and destination files
	iFile.open( this );
	iFile.seek( 0 );
	if ( ! iFile.noErr() ) 
		oFile.open( &inDestSpec );
	
	long		pos		= 0, numBytes;
	long		size	= iFile.size();
	CEgErr		err;
	
	// Copy the file in 50k chunks
	while ( pos < size && oFile.noErr() && iFile.noErr() ) {
		numBytes = 50000;
		if ( numBytes + pos > size )
			numBytes = size - pos;
		oFile.CEgOStream::PutBlock( iFile, numBytes );
		pos += numBytes;
	}
	
	if ( iFile.noErr() )
		err = oFile;
	else
		err = iFile;
		
	return err;
}

void CEgFileSpec::ChangeExt( const char* inExt ) {

	UtilStr	fileName;
	long		len;
	
	GetFileName( fileName );
	fileName.Append( '.' );
	len = fileName.length();
	fileName.Append( inExt );
	fileName.Keep( len + 3 );
	Rename( fileName );
}

const void* CEgFileSpec::OSSpec() const {

	if ( mSpecData.length() > 0 )
		return mSpecData.getCStr();
	else
		return NULL;
}
