//
//  DeVercruesseProcess.m
//
//  Created by Frank Vercruesse on Wed Apr 04 2001.
//  Copyright (c) 2001 Frank Vercruesse.
//
//
//  This program is free software; you can redistribute it and/or
//  modify it under the terms of the GNU General Public License
//  as published by the Free Software Foundation; either version 2
//  of the License, or (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
//  02111-1307, USA.

#import "DeVercruesseProcess.h"
#import "DeVercruesseProcessManager.h"


@implementation DeVercruesseProcess

+ (id)processWithPSN:(ProcessSerialNumber*)sn
{
    return [[[self alloc] initWithPSN:sn] autorelease];
}

- (id)init
{
    return [self initWithPSN:nil];
}

- (id)initWithPSN:(ProcessSerialNumber*)sn
{
    OSErr		err;
    CPSProcessInfoRec	pInfo;
    int			dummy;
    
    
    if( sn )
    {
        err = CPSGetProcessInfo( (CPSProcessSerNum*) sn, &pInfo, nil, 0, &dummy, nil, 0);
        
        if( !err )
        {
            self = [super init];
            
            if( self )
            {
                psn.highLongOfPSN = sn->highLongOfPSN;
                psn.lowLongOfPSN = sn->lowLongOfPSN;
                
                pid = pInfo.UnixPID;
                flavour = pInfo.Flavour;
                attributes = pInfo.Attributes;
                type = pInfo.ExecFileType;
                creator = pInfo.ExecFileCreator;
                
                bundleIdentifier = nil;
                identifier = nil;
                
                name = nil;
                loc = nil;
                img = nil;
            }
        }
        else
        {
            [self dealloc];
            return nil;
        }
    }
    else
    {
        [self dealloc];
        return nil;
    }
    
    return self;
}

- (void)dealloc
{
    [bundleIdentifier release];
    [identifier release];
    [name release];
    [img release];
    [cachedImage release];
    [loc release];
    
    [super dealloc];
}

- (NSString *)executableLoc;
{
    CFStringRef bundleLoc = (CFStringRef) [self loc];
    CFURLRef bundleURL;
    CFBundleRef bundleRef;

    if (bundleLoc == NULL) {
        // GetProcessBundleLocation returns NULL on 10.1.5 for certain CFM Carbon applications (BBEdit 6.5.3) - njr
        return nil; // XXX should really handle this better, but for the purposes of F-Script Anywhere, we donÕt care if itÕs not a Carbon app, and this works fine going forward.
    }
    
    bundleURL = CFURLCreateWithFileSystemPath(NULL, bundleLoc, kCFURLPOSIXPathStyle, NO);
    bundleRef = CFBundleCreate(NULL, bundleURL);

    if (bundleRef == NULL) {
        BOOL isDir;
        CFRelease(bundleURL);
        if ([[NSFileManager defaultManager] fileExistsAtPath: [self loc] isDirectory: &isDir] && !isDir)
            return [self loc];
        return nil; // not a bundle or file
    } else {
        CFURLRef execURL = CFBundleCopyExecutableURL(bundleRef);
        CFURLRef absURL = CFURLCopyAbsoluteURL(execURL);
        CFStringRef execPath = CFURLCopyFileSystemPath(absURL, kCFURLPOSIXPathStyle);

        if (absURL != NULL) CFRelease(absURL);
        if (execURL != NULL) CFRelease(execURL);
        CFRelease(bundleRef);
        return [(NSString *)execPath autorelease];
    } 
}


- (NSString*)loc
{
    char		buffer[1025];
    FSRef		location;
    
    
    if( !loc )
    {
        if( !GetProcessBundleLocation( &psn, &location) )
        {
            FSRefMakePath( &location, buffer, 1024);
            loc = [NSString stringWithUTF8String:buffer];
            [loc retain];
        }
    }
    
    return loc;
}

- (NSString*)name;
{
    char		buffer[1025];
    CFStringRef		pName;
    
    
    if( !name )
    {
        if( !name && (CopyProcessName( &psn, &pName) == noErr) )    // with thanks to Dylan Ashe
        {
            CFStringGetCString( pName, buffer, 256, CFStringConvertNSStringEncodingToEncoding( [NSString defaultCStringEncoding]));
            
            name = [NSString stringWithCString:buffer];
            [name retain];
            
            CFRelease( pName);
        }
        // CFBundleName code demoted to alternate method because it was causing plist processing issues in Jaguar, yet CopyProcessName doesn't work for everything, e.g. Help Viewer - njr
        else if( [self loc] )
        {
            NSBundle *b = [NSBundle bundleWithPath:loc];
            if( b )
            {
                name = [b localizedStringForKey:@"CFBundleName" value:@"_AsM_nO_nAmE_" table:@"InfoPlist"];

                if( [name isEqualToString:@"_AsM_nO_nAmE_"] )
                    name = [[b infoDictionary] objectForKey:@"CFBundleName"];

                if( name )
                    [name retain];
            }
        }
        
   }

    return name ? name : @"Çunknown nameÈ";
}

- (NSImage*)img
{
    if( !img )
    {
        if( [self loc] )
        {
            img = [[NSWorkspace sharedWorkspace] iconForFile:loc];
            
            if( img )
            {
                [img setDataRetained:YES];
//                [img setCachedSeparately:YES]; // added 8/26/01 by Frank
                [img retain];
            }
        }
    }
    
    return img;
}

- (NSImage *)cachedImage;
{
    return cachedImage;
}

- (void)setCachedImage:(NSImage *)anImage;
{
    if (anImage != cachedImage) {
        [anImage retain];
        [cachedImage release];
        cachedImage = anImage;
    }
}


- (ProcessSerialNumber*)psn
{
    return &psn;
}

- (UInt32)pid
{
    return pid;
}

- (UInt32)flavour
{
    return flavour;
}

- (UInt32)attributes
{
    CPSProcessInfoRec	pInfo;
    int			dummy;
    
    
    if( CPSGetProcessInfo( (CPSProcessSerNum*) &psn, &pInfo, nil, 0, &dummy, nil, 0) == noErr )
        attributes = pInfo.Attributes;
    
    return attributes;
}

- (UInt32)type
{
    return type;
}

- (UInt32)creator
{
    return creator;
}

- (NSString*)identifier
{
    if( !identifier )
    {
        identifier = [[NSString alloc] initWithFormat:@"%@%.8X%.8X",
            [self bundleIdentifier], [self type] ? type : 'APPL', [self creator] ? creator : '????'];
    }
    
    return identifier;
}

- (NSString*)bundleIdentifier
{
    NSBundle	*b;
    
    
    if( !bundleIdentifier && [self loc] && (b = [NSBundle bundleWithPath:loc]) )
        bundleIdentifier = [[b bundleIdentifier] retain];
    
    return bundleIdentifier ? bundleIdentifier : @"";
}


- (unsigned)hash
{
    return psn.lowLongOfPSN;
//    return pid;
}

- (BOOL)isEqual:(id)anObject
{
    if( anObject )
        return CPSEqualProcess( (CPSProcessSerNum*) &psn, (CPSProcessSerNum*) [anObject psn]);
    else
        return NO;
}

- (BOOL)isEqualToCurrent
{
    return [self isEqual:[ProcessMgr currentProcess]];
}

- (BOOL)isEqualToFront
{
    return [self isEqual:[ProcessMgr frontProcess]];
}

- (BOOL)isStillRunning
{
    CPSProcessInfoRec	pInfo;
    int			dummy;
    
    
    if( CPSGetProcessInfo( (CPSProcessSerNum*) &psn, &pInfo, nil, 0, &dummy, nil, 0) == noErr )
        return YES;
    else
        return NO;
}

- (BOOL)isBackgroundOnly
{
    return (((attributes & kCPSBGOnlyAttr) | (attributes & kCPSUIElementAttr)) != 0);
}

- (BOOL)isHidden
{
    return (([self attributes] & kCPSHiddenAttr) != 0);
}


- (BOOL)isClassic
{
    return (flavour == kCPSBlueApp);
}

- (BOOL)isCarbon
{
    return (flavour == kCPSCarbonApp);
}

- (BOOL)isCocoa
{
    return (flavour == kCPSYellowApp);
}


- (void)show
{
    if( [self isStillRunning] )
        CPSPostShowReq( (CPSProcessSerNum*) &psn);
}

- (void)hide
{
    if( [self isStillRunning] )
        CPSPostHideReq( (CPSProcessSerNum*) &psn);
}

- (void)makeFront
{
    if( [self isStillRunning] )
    {
        CPSSetFrontProcess( (CPSProcessSerNum*) &psn);
        CPSPostShowReq( (CPSProcessSerNum*) &psn);
    }
}

- (void)kill
{
    [self kill:NO];
}

- (void)kill:(BOOL)hard
{
    if( [self isStillRunning] )
        CPSPostKillRequest( (CPSProcessSerNum*) &psn, hard ? bfCPSKillHard : 0 );
}

- (NSString *)description;
{
    return [NSString stringWithFormat: @"%@ pid %d: '%@' (%@)", [super description], [self pid], [self name], [self identifier]];
}

@end
