/*
 *  NDResourceFork.m
 *
 *  Created by nathan on Wed Dec 05 2001.
 *  Copyright (c) 2001 Nathan Day. All rights reserved.
 *
 *	Currently ResourceFork will not add resource forks to files
 *	or create new files with resource forks
 *
 */

#import "TKResourceFile.h"
#import <CoreServices/CoreServices.h>

OSErr createResourceFork( NSURL * aURL );

@implementation TKResourceFile

/*
 * resourceForkForReadingAtURL:
 */
+ (id)resourceForkForReadingAtURL:(NSURL *)aURL
{
	return [[[self alloc] initForReadingAtURL:aURL] autorelease];
}

/*
 * resourceForkForWritingAtURL:
 */
+ (id)resourceForkForWritingAtURL:(NSURL *)aURL
{
	return [[[self alloc] initForWritingAtURL:aURL] autorelease];
}

/*
 * resourceForkForReadingAtPath:
 */
+ (id)resourceForkForReadingAtPath:(NSString *)aPath
{
	return [[[self alloc] initForReadingAtPath:aPath] autorelease];
}

/*
 * resourceForkForWritingAtPath:
 */
+ (id)resourceForkForWritingAtPath:(NSString *)aPath
{
	return [[[self alloc] initForWritingAtPath:aPath] autorelease];
}

/*
 * initForReadingAtURL:
 */
- (id)initForReadingAtURL:(NSURL *)aURL
{
	return [self initForPermission:fsRdPerm AtURL:aURL];
}

/*
 * initForWritingAtURL:
 */
- (id)initForWritingAtURL:(NSURL *)aURL
{
	return [self initForPermission:fsWrPerm AtURL:aURL];
}

/*
 * initForPermission:AtURL:
 */
- (id)initForPermission:(char)aPermission AtURL:(NSURL *)aURL
{
	return [self initForPermission:aPermission AtPath:[aURL path]];
}

- (id)initForPermission:(char)aPermission AtPath:(NSString *)aPath
{
	
	NSParameterAssert( aPath != nil );
	NSParameterAssert( [aPath length] > 0 );
	
	if (self = [super init]) {
		CFURLRef urlRef;
		bool fileExists, createdFSRef;
		FSRef fsRef;
		OSErr theErr;
		HFSUniStr255 dForkName;
		
		// Create a CFURL with the specified POSIX path.
		urlRef = CFURLCreateWithFileSystemPath(
											   kCFAllocatorDefault,
											   (CFStringRef) aPath,
											   kCFURLPOSIXPathStyle,
											   FALSE
											   );
		NSParameterAssert( urlRef != NULL );
		
		// Check whether the file exists already.
		fileExists = [[NSFileManager defaultManager] fileExistsAtPath: aPath];
		if (!fileExists) {
			CFRelease( urlRef );
			NSParameterAssert( fileExists );
		}
		
		// Try to create an FSRef from the URL.  (If the specified file doesn't exist, this
		// function will return false, but if we've reached this code we've already insured
		// that the file exists.)
		createdFSRef = CFURLGetFSRef( urlRef, &fsRef );
		CFRelease( urlRef );
		NSParameterAssert( createdFSRef == YES );
		
		// get the name of the OS's data fork
		theErr = FSGetDataForkName ( &dForkName );
		NSParameterAssert( theErr == noErr );
		
		// finally, open our resource fork
		theErr = FSOpenResourceFile ( &fsRef, dForkName.length, dForkName.unicode, aPermission, &fileReference);
		//NSParameterAssert( theErr == noErr );
	}
	return self;
}

/*
 * initForReadingAtPath:
 */
- (id)initForReadingAtPath:(NSString *)aPath
{
    if( [[NSFileManager defaultManager] fileExistsAtPath:aPath] ){
		return [self initForPermission:fsRdPerm AtPath:aPath];
	}else{
		NSLog(@"Error Opening file, does not exist");
		return nil;
	}
}

/*
 * initForWritingAtPath:
 */
- (id)initForWritingAtPath:(NSString *)aPath
{
	return [self initForPermission:fsWrPerm AtPath:aPath];
}

/*
 * dealloc
 */
- (void)dealloc
{
    //CloseResFile( fileReference );
	[super dealloc];
}

- (OSErr)changeResource:(short)anID forData:(NSData *)aData forType:(ResType)aType
{
	Handle		theResHandle;
	Handle		resToCopyToHandle;
	OSErr theErr;
	short			thePreviousRefNum;
	
	thePreviousRefNum = CurResFile();
	UseResFile( fileReference );
	
	if (!aData || !anID)
		return paramErr;
	
	if (noErr != (theErr = ResError()) )
		return theErr;
	
	resToCopyToHandle = Get1Resource( aType, anID );
	if( noErr == (theErr = ResError()) ) {
		// copy NSData's bytes to a handle
		if ( noErr == (theErr = PtrToHand ( [aData bytes], &theResHandle, [aData length] )) ) {
			int x = GetHandleSize(theResHandle);
			//NSLog(@"%d", x);
			//NSLog(@"%d", );
			if(GetHandleSize(resToCopyToHandle)!=0){
				SetHandleSize(resToCopyToHandle, x);
				HLock(resToCopyToHandle);
				HLock( theResHandle );
				memmove(*theResHandle, *resToCopyToHandle, x);
				//AddResource( theResHandle, aType, anID, thePName );
				HUnlock( theResHandle );
				HUnlock(resToCopyToHandle);
				ChangedResource(resToCopyToHandle);
			}
		}
		ReleaseResource(theResHandle);
	}
	ReleaseResource(resToCopyToHandle);
	UseResFile( thePreviousRefNum );
	
	return theErr;
}

//- (OSErr)setResourceFileEndiannessMarker:

- (OSErr)addData:(NSData *)aData type:(ResType)aType Id:(short)anID name:(NSString *)aName attributes: (ResAttributes)attributes
{
	Handle		theResHandle;
	OSErr theErr;
	
	if (!aData || !aName)
		return paramErr;
	
	if( noErr == (theErr = [self removeType:aType Id:anID]) ) {
		short			thePreviousRefNum;
		
		thePreviousRefNum = CurResFile();
		if (noErr != (theErr = ResError()) )
			return theErr;
		UseResFile( fileReference );
		if (noErr != (theErr = ResError()) )
			return theErr;
		
		// copy NSData's bytes to a handle
		if ( noErr == (theErr = PtrToHand ( [aData bytes], &theResHandle, [aData length] )) ) {
			//NSLog(@"running function");
			Str255 thePName;
			
			//CopyCStringToPascal ( [aName lossyCString], thePName );
			ConstStringPtr thePNamePtr = (ConstStringPtr) &thePName;
			thePNamePtr = CFStringGetPascalStringPtr((CFStringRef) aName, kCFStringEncodingASCII );
			
			HLock( theResHandle );
			AddResource( theResHandle, aType, anID, thePName );
			HUnlock( theResHandle );
			if (noErr == (theErr = ResError()) ) {
				
				SetResAttrs(theResHandle, attributes);
				if (noErr == (theErr = ResError()) ) {
					
					ChangedResource(theResHandle);
					if (noErr == (theErr = ResError()) ) {
						
						ReleaseResource( theResHandle );
					}
				}
			}
		}
		UseResFile( thePreviousRefNum );
	}
	
	return theErr;
}

/*
 * dataForType:Id:
 */
- (NSData *)dataForType:(ResType)aType Index:(short)anIndex
{
	NSData *theData;
	Handle theResHandle;
	short thePreviousRefNum;
	
	thePreviousRefNum = CurResFile();
	if (noErr != ResError() )
		return nil;
	UseResFile( fileReference );
	if (noErr != ResError() )
		return nil;
	
	theResHandle = Get1IndResource( aType, anIndex );
	if( NULL == theResHandle || noErr != ResError()  ) {
		UseResFile( thePreviousRefNum );
		return nil;
	}
	
	HLock(theResHandle);
	theData = [NSData dataWithBytes:*theResHandle length:GetHandleSize( theResHandle )];
	HUnlock(theResHandle);
	
	ReleaseResource( theResHandle );
	
	UseResFile( thePreviousRefNum );
	return theData;
}

- (NSData *)dataForType:(ResType)aType ID:(short)anID
{
    NSData *theData;
    Handle theResHandle;
    short thePreviousRefNum;
	
    thePreviousRefNum = CurResFile();
    if (noErr != ResError() )
        return nil;
    UseResFile( fileReference );
    if (noErr != ResError() )
        return nil;
	
    theResHandle = Get1Resource( aType, anID );
    if( NULL == theResHandle || noErr != ResError()  ) {
        UseResFile( thePreviousRefNum );
        return nil;
    }
	
    HLock(theResHandle);
    theData = [NSData dataWithBytes:*theResHandle length:GetHandleSize( theResHandle )];
    HUnlock(theResHandle);
	
    ReleaseResource( theResHandle );
	
    UseResFile( thePreviousRefNum );
    return theData;
}

/*
 * -addString:type:Id:name:
 *		adds a string to the resource fork as a pascal string
 */

- (OSErr)updateRes{
	UpdateResFile( fileReference );
	return ResError();
}

/*
 * removeType: Id:
 */
- (OSErr)removeType:(ResType)aType Id:(short)anID
{
    short		thePreviousRefNum;
	Handle		theResHandle;
	OSErr		theErr;
	
	thePreviousRefNum = CurResFile();
	if (noErr != (theErr = ResError()) )
		return theErr;
	UseResFile( fileReference );
	if (noErr != (theErr = ResError()) )
		return theErr;
	
	theResHandle = Get1Resource( aType, anID );
	theErr = ResError( );
	if( theResHandle && theErr == noErr )
	{
		RemoveResource( theResHandle );		// Disposed of in current resource file
		if (noErr == (theErr = ResError()) )
			DisposeHandle(theResHandle);
	}
	else theErr = noErr; // this fails silently if the resource isn't present
	UseResFile( thePreviousRefNum );  
	return theErr;
}

- (short)count:(ResType)aType
{
	short resCount;
    short thePreviousRefNum;
	
	thePreviousRefNum = CurResFile();
	if (noErr != ResError() )
		return 0;
	UseResFile( fileReference );
	if (noErr != ResError() )
		return 0;
	
	resCount = Count1Resources(aType);
	
    UseResFile( thePreviousRefNum );
    return resCount;
}

-(OSErr)closeFile{
	CloseResFile( fileReference );
	return ResError();
}

- (short)resId:(ResType)aType Index:(short)anIndex
{
    short thePreviousRefNum;
    short truID;
    ResType theType;
    Str255 pName;
	Handle resHandle;
	
    thePreviousRefNum = CurResFile();
	NSParameterAssert( ResError() == noErr );
    UseResFile( fileReference );
	NSParameterAssert( ResError() == noErr );
    SetResLoad(FALSE);
	NSParameterAssert( ResError() == noErr );
	
	resHandle = Get1IndResource( aType, anIndex );
	NSParameterAssert( resHandle != NULL );
	NSParameterAssert( ResError() == noErr );
	
	GetResInfo(resHandle, &truID, &theType, pName);
	NSParameterAssert( ResError() == noErr );
	ReleaseResource( resHandle );
	NSParameterAssert( ResError() == noErr );
	
	SetResLoad(TRUE);
	NSParameterAssert( ResError() == noErr );
    UseResFile( thePreviousRefNum );     		// reset back to resource previously set
	NSParameterAssert( ResError() == noErr );
    return truID;
}

- (id)resName:(ResType)aType Index:(short)anIndex
{
    short thePreviousRefNum;
    short truID;
    ResType theType;
    Str255 pName;
    //char cName[255];
	Handle resHandle;
	
    thePreviousRefNum = CurResFile();
	NSParameterAssert( ResError() == noErr );
    UseResFile( fileReference );
	NSParameterAssert( ResError() == noErr );
    SetResLoad(FALSE);
	NSParameterAssert( ResError() == noErr );
	
	resHandle = Get1IndResource( aType, anIndex );
	NSParameterAssert( resHandle != NULL );
	
	GetResInfo(resHandle, &truID, &theType, pName);
	NSParameterAssert( ResError() == noErr );
	ReleaseResource( resHandle );
	NSParameterAssert( ResError() == noErr );
	
	SetResLoad(TRUE);
	NSParameterAssert( ResError() == noErr );
    UseResFile( thePreviousRefNum );
	NSParameterAssert( ResError() == noErr );
	//CopyPascalStringToC(pName, cName);
    //return [NSString stringWithCString:cName];
    return (NSString *)CFStringCreateWithPascalString (NULL, pName, kCFStringEncodingASCII);
}


-(TKEndianness)endiannessOfResourceFile
{
	TKEndianness resourceFileFormat;
	ThemeDataFormatForResourceFile(fileReference,&resourceFileFormat);
	return resourceFileFormat;
}

-(void)setEndiannessOfResourceFile:(TKEndianness)endianness
{
	SetThemeDataFormatForResourceFile(fileReference,endianness);
}

@end
