// Copyright 1997-1999 Omni Development, Inc.  All rights reserved.
//
// This software may only be used and reproduced according to the
// terms in the file OmniSourceLicense.html, which should be
// distributed with this project and can also be found at
// http://www.omnigroup.com/DeveloperResources/OmniSourceLicense.html.

#import "OWTask.h"

#import <Foundation/Foundation.h>
#import <OmniBase/OmniBase.h>
#import <OmniFoundation/OmniFoundation.h>

#import "OWContentInfo.h"

RCS_ID("$Header: /Network/Source/CVS/OmniGroup/Frameworks/OWF/Pipelines.subproj/OWTask.m,v 1.12 1999/03/30 21:26:31 kc Exp $")

@implementation OWTask

static NSTimeZone *localTimeZone;

+ (void)initialize;
{
    static BOOL initialized = NO;

    [super initialize];
    if (initialized)
	return;
    initialized = YES;

    localTimeZone = [[NSTimeZone localTimeZone] retain];

}

+ (NSString *)HMSStringFromTimeInterval:(NSTimeInterval)interval;
{
    unsigned int seconds;
    
    seconds = interval + .5;
    if (seconds < 1)
        return nil;

    if (seconds < 60)
        return [NSString stringWithFormat:@"%d seconds", seconds];
    else if (seconds < 3600)
        return [NSString stringWithFormat:@"%d:%02d", seconds/60, seconds%60];
    else
        return [NSString stringWithFormat:@"%d:%02d:%02d", seconds/3600, (seconds/60)%60, seconds%60];
}



// Init and dealloc

- init;  // DESIGNATED INITIALIZER
{
    [super init];

    OFSimpleLockInit(&displayablesSimpleLock);
    parentContentInfoLock = [[NSLock alloc] init];
    taskFlags.wasActiveOnLastCheck = NO;

    state = PipelineDead;
    
    return self;
}

- initWithName:(NSString *)name contentInfo:(OWContentInfo *)aContentInfo parentContentInfo:(OWContentInfo *)aParentContentInfo;
{
    [self init];

    compositeTypeString = [name retain];
    contentInfo = [aContentInfo retain];
    [contentInfo addTask:self]; // Note retain cycle, requires nullify... to break.
    [self setParentContentInfo:aParentContentInfo];

    return self;
}

- (void)dealloc;
{
    OBPRECONDITION(parentContentInfo == nil);
    OBPRECONDITION(contentInfo == nil);
    OBPRECONDITION(inspector == nil);

    [lastActivationDate release];
    [compositeTypeString release];

    OFSimpleLockFree(&displayablesSimpleLock);
    [parentContentInfoLock release];
    [super dealloc];
}


// Task management

- (void)abortTask;
{
}


// Active tree

- (BOOL)treeHasActiveChildren;
{
    return contentInfo && [contentInfo treeHasActiveChildren];
}

- (void)treeActiveStatusMayHaveChanged;
{
    BOOL treeHasActiveChildren;

    treeHasActiveChildren = [self treeHasActiveChildren];
    if (treeHasActiveChildren == taskFlags.wasActiveOnLastCheck)
        return;
    taskFlags.wasActiveOnLastCheck = treeHasActiveChildren;
    if (treeHasActiveChildren)
        [self activateInTree];
    else
        [self deactivateInTree];
}

- (void)activateInTree;
{
    [parentContentInfoLock lock];
    [parentContentInfo addActiveChildTask:self];
    [parentContentInfoLock unlock];
    [lastActivationDate release];
    lastActivationDate = [[NSDate alloc] init];
}

- (void)deactivateInTree;
{
    [parentContentInfoLock lock];
    [parentContentInfo removeActiveChildTask:self];
    [parentContentInfoLock unlock];
}

- (void)abortTreeActivity;
{
    [self abortTask];
    [contentInfo abortActiveChildTasks];
}

// State

- (PipelineState)state;
{
    return state;
}

- (id <OWAddress>)lastAddress;
{
    return nil;
}

- (NSTimeInterval)timeSinceTreeActivationInterval;
{
    return -[lastActivationDate timeIntervalSinceNow];
}

- (NSTimeInterval)estimatedRemainingTimeInterval;
{
    return 0;
}

- (NSTimeInterval)estimatedRemainingTreeTimeInterval;
{
    return MAX([self estimatedRemainingTimeInterval], contentInfo ? [contentInfo estimatedRemainingTreeTimeIntervalForActiveChildTasks] : 0.0);
}

- (BOOL)hadError;
{
    return NO;
}

- (BOOL)isRunning;
{
    return NO;
}

- (BOOL)hasThread;
{
    return NO;
}

- (NSString *)errorNameString;
{
    return @"";
}

- (NSString *)errorReasonString;
{
    return @"";
}

- (NSString *)compositeTypeString;
{
    unsigned int activeChildCount;

    activeChildCount = [contentInfo activeChildTasksCount];
    if (!activeChildCount)
        return compositeTypeString;
    else
        return [NSString stringWithFormat:@"%@ (%d)", compositeTypeString, activeChildCount];
}

- (BOOL)hasChildrenInCompositeTypeStringsInArray:(NSMutableArray *)compositeTypeStrings andSet:(NSCountedSet *)compositeTypeStringsSet;
{
    BOOL hasActiveChildren;

    if (!taskFlags.wasActiveOnLastCheck)
        return NO;

    OFSimpleLock(&displayablesSimpleLock);
    
    if (compositeTypeString && [self isRunning]) {
        if ([compositeTypeStringsSet countForObject:compositeTypeString] == 0)
            [compositeTypeStrings addObject:compositeTypeString];
        [compositeTypeStringsSet addObject:compositeTypeString];
    }
    if (contentInfo) {
        unsigned int typeCount;

        // This really only works for the top level, but that's all we care about
        typeCount = [compositeTypeStrings count];
        [contentInfo putCompositeTypeStringsOfActiveChildTasksInArray:compositeTypeStrings andSet:compositeTypeStringsSet];

        hasActiveChildren = typeCount < [compositeTypeStrings count];
    } else
        hasActiveChildren = NO;
    
    OFSimpleUnlock(&displayablesSimpleLock);

    return hasActiveChildren;
}

- (NSString *)documentsInTreeString;
{
    NSMutableArray *compositeTypeStrings;
    NSCountedSet *compositeTypeStringsSet;
    BOOL hasActiveChildren;
    unsigned int typeCount;
    NSMutableString *string;

    compositeTypeStrings = [[NSMutableArray alloc] init];
    compositeTypeStringsSet = [[NSCountedSet alloc] init];

    hasActiveChildren = [self hasChildrenInCompositeTypeStringsInArray:compositeTypeStrings andSet:compositeTypeStringsSet];

    typeCount = [compositeTypeStrings count];
    if (typeCount && hasActiveChildren) {
        unsigned int typeIndex;

        string = [NSMutableString string];
        for (typeIndex = 0; typeIndex < typeCount; typeIndex++) {
            NSString *aCompositeTypeString;
            int count;

            aCompositeTypeString = [compositeTypeStrings objectAtIndex:typeIndex];
            count = [compositeTypeStringsSet countForObject:aCompositeTypeString];
            if (count == 1)
                [string appendString:aCompositeTypeString];
            else
                [string appendFormat:@"%d %@s", count, aCompositeTypeString];

            if (typeIndex < typeCount-1)
                [string appendString:@", "];
        }
    } else
        string = nil;

    [compositeTypeStrings release];
    [compositeTypeStringsSet release];
    return string;
}

- (unsigned int)workDone;
{
    return 0;
}

- (unsigned int)workToBeDone;
{
    return 0;
}

- (unsigned int)workDoneIfNotFinished;
{
    unsigned int workDone, workToBeDone;

    workDone = [self workDone];
    workToBeDone = [self workToBeDone] ;
    if (workDone == workToBeDone)
        return 0;
    return workDone;
}

- (unsigned int)workToBeDoneIfNotFinished;
{
    unsigned int workDone, workToBeDone;

    workDone = [self workDone];
    workToBeDone = [self workToBeDone] ;
    if (workDone == workToBeDone)
        return 0;
    return workToBeDone;
}

#warning WJS: We never call this with YES any more.  Check if we want to, then modify.
- (unsigned int)workDoneIncludingChildrenIncludingFinished:(BOOL)includeFinished;
{
    return (includeFinished ? [self workDone] : [self workDoneIfNotFinished]) + [contentInfo workDoneByChildTasksIncludingFinished:includeFinished];
}

#warning WJS: We never call this with YES any more.  Check if we want to, then modify.
- (unsigned int)workToBeDoneIncludingChildrenIncludingFinished:(BOOL)includeFinished;
{
    return (includeFinished ? [self workToBeDone] : [self workToBeDoneIfNotFinished]) + [contentInfo workToBeDoneByChildTasksIncludingFinished:includeFinished];
}


- (NSString *)statusString;
{
    if (taskFlags.wasActiveOnLastCheck)
        return @"Children are active";
    else
        return @"Activity finished";
}


// Inspecting

- (void)setInspector:(id <OWPipelineInspector>)newInspector;
{
    if (inspector == newInspector)
        return;

    [inspector release];
    inspector = [newInspector retain];
}

- (void)tellInspectorPipelineTreeDidChange;
{
    if (inspector)
        [inspector pipelineTreeDidChange];
    else {
        [parentContentInfoLock lock];
        [parentContentInfo tellInspectorPipelineTreeDidChange];
        [parentContentInfoLock unlock];
    }
}


// Parent contentInfo

- (void)setParentContentInfo:(OWContentInfo *)aParentContentInfo;
{
    [parentContentInfoLock lock];
    if (parentContentInfo != aParentContentInfo) {
        if (parentContentInfo) {
            [parentContentInfo removeChildTask:self];
            if (taskFlags.wasActiveOnLastCheck)
                [parentContentInfo removeActiveChildTask:self];
            [parentContentInfo release];
        }
        parentContentInfo = [aParentContentInfo retain];
        if (parentContentInfo) {
            [parentContentInfo addChildTask:self];
            if (taskFlags.wasActiveOnLastCheck)
                [parentContentInfo addActiveChildTask:self];
        }
    }
    [parentContentInfoLock unlock];
}

- (OWContentInfo *)parentContentInfo;
{
    OWContentInfo *info;

    [parentContentInfoLock lock];
    info = [[parentContentInfo retain] autorelease];
    [parentContentInfoLock unlock];

    return info;
}


// ContentInfo

- (OWContentInfo *)contentInfo;
{
    return [[contentInfo retain] autorelease];
}

- (void)nullifyContentInfo;
{
    [contentInfo release];
    contentInfo = nil;
}

- (void)deepFlushContentCache;
{
    [contentInfo deepFlushContentCache];
}


// OFMessageQueue protocol helpers

- (unsigned int)taskPriority;
{
    return [parentContentInfo baseTaskPriority];
}

- (unsigned int)baseTaskPriority; // Without modifiers
{
    return [parentContentInfo baseTaskPriority];
}

- (unsigned int)taskGroup;
{
    return [parentContentInfo taskGroup];
}

- (unsigned int)taskMaximumSimultaneousThreadsInGroup;
{
    return [parentContentInfo taskMaximumSimultaneousThreadsInGroup];
}

- (NSComparisonResult)comparePriority:(OWTask *)aTask;
{
    unsigned int ourPriority, otherPriority;

    ourPriority = contentInfo ? [contentInfo baseTaskPriority] : NSNotFound;
    otherPriority = aTask->contentInfo ? [aTask->contentInfo baseTaskPriority] : NSNotFound;
     if (ourPriority < otherPriority)
        return NSOrderedAscending;
    else if (ourPriority == otherPriority)
        return NSOrderedSame;
    else
        return NSOrderedDescending;
}

@end


@implementation OWTask (Private)

// Debugging

- (NSMutableDictionary *)debugDictionary;
{
    NSMutableDictionary *debugDictionary;

    debugDictionary = [super debugDictionary];

    if (contentInfo)
        [debugDictionary setObject:contentInfo forKey:@"contentInfo"];
    if (lastActivationDate)
        [debugDictionary setObject:lastActivationDate forKey:@"lastActivationDate"];
    if (compositeTypeString)
        [debugDictionary setObject:compositeTypeString forKey:@"compositeTypeString"];

    return debugDictionary;
}

@end
