//
//  DeVercruesseProcessManager.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 "DeVercruesseProcessManager.h"


@implementation DeVercruesseProcessManager

static DeVercruesseProcessManager *defaultManager = nil;

+ (id)defaultManager
{
    return ( defaultManager ? defaultManager : [[self alloc] init] );
}

- (id)init
{
    if( defaultManager )
    {
	[self dealloc];
    }
    else if( [super init] )
    {
        processes = [[NSMutableSet alloc] initWithCapacity:30];
        defaultManager = self;
    }
    
    return defaultManager;
}

- (void)dealloc
{
    [processes release];
    
    if( self == defaultManager )
        defaultManager = nil;
	[super dealloc];
}


- (DeVercruesseProcess*)currentProcess
{
    ProcessSerialNumber	sn;
    DeVercruesseProcess		*p, *q;
    
    
    CPSGetCurrentProcess( (CPSProcessSerNum*) &sn);
    
    if( p = [DeVercruesseProcess processWithPSN:&sn] )
    {
        if( q = [processes member:p] )
            p = q;
//        else
//            [processes addObject:p];
    }
    
    return p;
}

- (DeVercruesseProcess*)frontProcess
{
    ProcessSerialNumber	sn;
    DeVercruesseProcess		*p, *q;
    
    
    if( CPSGetFrontProcess( (CPSProcessSerNum*) &sn) != noErr )
        CPSGetCurrentProcess( (CPSProcessSerNum*) &sn);
    
    if( p = [DeVercruesseProcess processWithPSN:&sn] )
    {
        if( q = [processes member:p] )
            p = q;
//        else
//            [processes addObject:p];
    }
    
    return p;
}

- (DeVercruesseProcess*)nextToFrontProcess
{
    ProcessSerialNumber	sn;
    DeVercruesseProcess		*p, *q;
    
    
    if( CPSGetNextToFrontProcess( (CPSProcessSerNum*) &sn) != noErr )
        CPSGetCurrentProcess( (CPSProcessSerNum*) &sn);
    
    if( p = [DeVercruesseProcess processWithPSN:&sn] )
    {
        if( q = [processes member:p] )
            p = q;
//        else
//            [processes addObject:p];
    }
    
    return p;
}

- (DeVercruesseProcess*)anyNoBGOnlyProcess
{
    ProcessSerialNumber	sn;
    DeVercruesseProcess		*p, *q;
    
    
    sn.highLongOfPSN = 0;
    sn.lowLongOfPSN = kNoProcess;
    
    while( CPSGetNextProcess( (CPSProcessSerNum*) &sn) == noErr )
    {
        if( (p = [DeVercruesseProcess processWithPSN:&sn]) && ![p isBackgroundOnly] )
        {
            if( q = [processes member:p] )
                p = q;
//            else
//                [processes addObject:p];
            
            return p;
        }
    }
    
    return nil;
}


- (NSArray*)processes
{
    [self update];
    
    return [NSArray arrayWithArray:[processes allObjects]];
}


- (void)update
{
    ProcessSerialNumber	sn;
    NSMutableSet	*temp;
    DeVercruesseProcess		*process, *existingProcess;
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    
    sn.highLongOfPSN = 0;
    sn.lowLongOfPSN = kNoProcess;

    temp = [[NSMutableSet alloc] initWithCapacity: 30];
    
    while( CPSGetNextProcess( (CPSProcessSerNum*) &sn) == noErr )
    {
        // njr removed background-only check because we still get process launch notifications for background-only apps...
        if ( (process = [[DeVercruesseProcess alloc] initWithPSN:&sn]) != nil) {
            if ( (existingProcess = [processes member: process]) != nil ) {
                [temp addObject: existingProcess];
            } else {
                [temp addObject: process];
            }
            [process release];
        }
    }
    
    [processes setSet:[temp autorelease]];
    [pool release];
}


- (void)showAll
{
    ProcessSerialNumber	*psn;
    
    
    psn = [[self frontProcess] psn];
    
    CPSPostShowAllReq( (CPSProcessSerNum*) psn );
    CPSPostShowReq( (CPSProcessSerNum*) psn );
}

- (void)hideOthers
{
    [self hideOthers:YES];
}

- (void)hideOthers:(BOOL)ignoreRules
{
    NSArray		*p;
    NSEnumerator	*enumerator;
    DeVercruesseProcess	*process;
    
    
    p = [[ProcessMgr processes] retain];
    
    enumerator = [p objectEnumerator];
    
    while( process = [enumerator nextObject] )
    {
        if( ![process isHidden] && ![process isEqualToFront] &&
            (ignoreRules ||
              ![self checkRule:@"optDontHide" withProcess:process]) )
        {
            [process hide];
        }
    }
    
    [p release];
}

- (void)hideFront
{
    [[self frontProcess] hide];
}

- (void)setFront:(DeVercruesseProcess*)process
{
    [process makeFront];
}

// rules stuff

- (void)setRules:(NSArray*)r
{
    NSEnumerator *enumerator;
    NSDictionary *dict;
    NSMutableSet *hasRules, *keys;
    NSMutableDictionary *newRules;
    
    
    newRules = [[NSMutableDictionary alloc] initWithCapacity:10];
    
    hasRules = [[NSMutableSet alloc] initWithCapacity:10];
    [newRules setObject:hasRules forKey:@"_hasRules"];
    [hasRules release];
    
    keys = [[NSMutableSet alloc] initWithCapacity:10];
    [newRules setObject:keys forKey:@"_keys"];
    [keys release];
    
    enumerator = [r objectEnumerator];
    while( dict = [enumerator nextObject] )
    {
        NSString	*identifier, *key;
        NSEnumerator 	*keyEnumerator;
        NSDictionary	*d2;
        
        identifier = [[NSString alloc] initWithFormat:@"%@%.8X%.8X", [dict objectForKey:@"identifier"], [[dict objectForKey:@"type"] unsignedIntValue], [[dict objectForKey:@"creator"] unsignedIntValue]];
        [hasRules addObject:identifier];
        [identifier release];
        
        d2 = [dict objectForKey:@"rules"];
	keyEnumerator = [d2 keyEnumerator];
        while( key = [keyEnumerator nextObject] )
        {
            NSMutableDictionary	*d3;
            
            if( !(d3 = [newRules objectForKey:key]) )
            {
                [keys addObject:key];
                
                d3 = [[NSMutableDictionary alloc] initWithCapacity:10];
                [newRules setObject:d3 forKey:key];
                [d3 release];
            }
            
            [d3 setObject:[d2 objectForKey:key] forKey:identifier];
        }
    }
    
    [rules release];
    rules = [[NSDictionary alloc] initWithDictionary:newRules];
    
    [newRules release];
}

- (BOOL)anyRulesForProcess:(DeVercruesseProcess*)process
{
    if( !process || !rules )
        return NO;
    
    return [[rules objectForKey:@"_hasRules"] containsObject:[process identifier]];
}

- (BOOL)checkRule:(NSString*)ruleKey withProcess:(DeVercruesseProcess*)process
{
    if( !ruleKey || !process || !rules )
        return NO;
    
    return [[rules objectForKey:ruleKey] containsObject:[process identifier]];
}

- (NSSet*)processIdentifiersForRule:(NSString*)ruleKey
{
    return [rules objectForKey:ruleKey];
}

- (int)optionsForRule:(NSString*)ruleKey withProcess:(DeVercruesseProcess*)process
{
    NSDictionary *rule;
    NSNumber *options;
    
    
    if( !(rule = [rules objectForKey:ruleKey]) )
        return 2;	// use global setting
    
    if( !(options = [rule objectForKey:[process identifier]]) )
        return 2;	// use global setting
    
    return [options intValue];
}

@end
