#pragma .h #import <Foundation/NSString.h>
#pragma .h #import <IconKit/iconkit.h>

#import <Foundation/NSDictionary.h>
#import <Foundation/NSPathUtilities.h>
#import <Foundation/NSProcessInfo.h>

#import <AppKit/NSWorkspace.h>

#import "File.h"
#import "FileList.h"

#pragma .h typedef struct _FileFlags {
#pragma .h 	unsigned int	imageLoaded:1;
#pragma .h 	unsigned int	acceptingDragImageLoaded:1;
#pragma .h 	unsigned int	parentLoaded:1;
#pragma .h 	unsigned int	childrenLoaded:1;
#pragma .h } FileFlags;

@implementation File: IKFolder

static NSImage				*draggedImage = nil;
static NSMutableDictionary	*fileMap = nil;

+ (void)initialize
{
    if (self == [File  class]) {
        fileMap = [[NSMutableDictionary  alloc]  init];
    }
    return;
}

+ multipleSelectionClass
{
	return [FileList class];
}

+ (NSArray *) pasteTypes
{
    static NSMutableArray *pasteTypes = nil;
    if(pasteTypes == nil) {
        pasteTypes = [[NSMutableArray alloc] init];
        [pasteTypes addObject:NSFilenamesPboardType];
        [pasteTypes addObject:IKidPboardType()];
    }
    return pasteTypes;
}

+ fileForPath: (NSString *) thePath
{
    // <<HACK>> This is really bad since files which we fetched once will never go away !
    // But right now we have bigger fish to fry...so ignor this ugly piece of code.

    id file = [fileMap objectForKey: thePath];

    if( !file )
    {
        file = [[File alloc] initPath: thePath];
        [fileMap  setObject:file forKey:thePath];
    }
    else
    {
        // Just retain it once more so taht we can release it again 

        [file retain];
    }
    return [file autorelease];
}

- initPath: (NSString *) thePath
{
    // <<HACK>> Method name should be changed !  initWithPath  or initWithFile etc...

    NSString *		fileType;
    NSString * 		title = [thePath lastPathComponent];
    NSDictionary * 	dict;

    if( !title )
        return nil;

    // <<HACK>> This does not work properly with WindowsNT !!! Too hardcoded !

    else if( [title isEqual:@""] ||
             [title isEqual:@"/"] )
        title = [[NSProcessInfo processInfo] hostName];

    // Now we can be sure that the title is what we expect it to be...

    self = [super init:title];
    if( !self ) return nil;

    path = [thePath retain];

    // This could be wrapped in "isDictionray" on the MiscKit..

    dict = [[NSFileManager defaultManager] fileAttributesAtPath:path traverseLink:NO];
    flags.dragAccepting = [[dict fileType] isEqual:NSFileTypeDirectory];
    flags.leaf = !flags.dragAccepting;
    flags.hidden = NO;
    fileFlags.imageLoaded = NO;
    fileFlags.acceptingDragImageLoaded = NO;
    fileFlags.parentLoaded = NO;
    fileFlags.childrenLoaded = !flags.dragAccepting;

    // Set some other minor detaiils.
    // <<HACK>> This is not really very cleaer..esp the file wrapper part.

    isApplication = NO;
    isFileWrapper = NO;
    fileType = [path pathExtension];
    if( [fileType isEqual:@"app"] )
    {
        isApplication = YES;
        isFileWrapper = YES;
        flags.dragAccepting = YES;
    }
    else if( [fileType isEqual:@"rftd"] ||
             [fileType isEqual:@"nib"] ||
             [fileType isEqual:@"eomodeld"] )
    {
        isFileWrapper = YES;
        flags.dragAccepting = NO;
    }
    return self;
}

- copyWithZone: (NSZone *) zone;
{
    File *copy = [super copyWithZone: zone];

    copy->path = [path copyWithZone:zone];
    copy->fileFlags = fileFlags;
    return copy;
}

- (void)dealloc
{
    [path release];
    [super dealloc];
}

- image
{
    if (!fileFlags.imageLoaded)
        image = draggedImage
            ? [draggedImage retain]
            : [[[NSWorkspace sharedWorkspace] iconForFile:path] retain];
    fileFlags.imageLoaded = 1;
    return image;
}

- acceptingDragImage
{
    if( isFileWrapper )
        return [self image];
    else
        return [super acceptingDragImage];
}

- (NSString *) path
{
	return path;
}

- parents
{
    NSString *directory;

    if (!fileFlags.parentLoaded) {
        fileFlags.parentLoaded = 1;
        directory = [path stringByDeletingLastPathComponent];
        if([directory isEqual:@""]) {
            [[File fileForPath:@"/"] addChild: self];
        } else {
            [[File fileForPath:directory] addChild: self];
        }
    }	
    return parents;
}

- (void)addParent: parent
{
    fileFlags.parentLoaded = 1;
    [super addParent: parent];
}

- (void)removeChild:child withAnnouncement:(BOOL)yesNo
{
    NSEnumerator *enumerator = [fileMap keyEnumerator];
    NSString     *key, *p = [child path];
    fileFlags.childrenLoaded = 0;
    while(key = [enumerator nextObject]) {
        if([key hasPrefix:p]) {
            [fileMap removeObjectForKey:key];
        }
    }
    [fileMap removeObjectForKey:p];
    [super removeChild:child withAnnouncement:yesNo];
}

- children
{
    int i, count;

    if (!fileFlags.childrenLoaded) {
        NSArray *files = [[NSFileManager defaultManager] directoryContentsAtPath:path];
        fileFlags.childrenLoaded = 1;
        [users setSendAnnouncements: NO];
        for(i = 0, count = [files count]; i < count; i++) {
            NSString *s = [files objectAtIndex:i];
            [self  addChild: [File fileForPath:[path stringByAppendingPathComponent:s]]];
        }
        [users  setSendAnnouncements: YES];
    }
    return children;
}

- (void)copyToPasteboard: (NSPasteboard *) pboard
{
    [super  copyToPasteboard: pboard];
    [pboard addTypes:[NSArray arrayWithObject:NSFilenamesPboardType] owner:nil];
    [pboard setPropertyList:[NSArray arrayWithObject:path] forType:NSFilenamesPboardType];
}

+ readFromPasteboard: (NSPasteboard *) pboard
{
    id file = nil;

    if( (file = [super readFromPasteboard: pboard]) == nil &&
        [pboard availableTypeFromArray:[NSArray arrayWithObject:NSFilenamesPboardType]] != NULL) {
        id a = [pboard propertyListForType:NSFilenamesPboardType];
        if([a isKindOfClass:[NSString class]]) {
            file = [File fileForPath:a];
        }
        else if( [a isKindOfClass:[NSArray class]] )
        {
            if([a count] > 1)
                file = [FileList readFromPasteboard:pboard];
            else
                file = [File fileForPath:[a objectAtIndex:0]];
       }
    }
    return file;
}

- (unsigned int) draggingEntered: (id <NSDraggingInfo>) sender
{
    int n = [sender draggingSequenceNumber];

    if (draggingSession != n)
    {
        // We are an application _and_ a fileWrapper, then we should only support "move" operations
        // if we really support the filepath of the dragged objects.
        // IF the "command" key is pressed we will actept all dragged files.

        if( isApplication && isFileWrapper )
        {
            dragging = [[[self class] readFromPasteboard : [sender  draggingPasteboard]] retain];

            draggingSession = n;
            if( !dragging )
            {
                operationMask = NSDragOperationNone;
            }
            else
            {
                // if command key

                // if( ![self _applicationSupportsFile:[draggedFile path]] )
                 //   return NO;

                operationMask = NSDragOperationGeneric;
                // else check agaisnt supported filetypes...
            }
        }
        // otherwise we will leave the work to our superclass
        // <<NOTE>> This code is really evil since it heavily lelies on the implementation of the super method.
        // We might not have been able to do this if the super code was not known ..

        else [super draggingEntered:sender];
    }
    return [self draggingOperation: sender];
}


- (unsigned int) draggingOperation: (id <NSDraggingInfo>) sender
{
    unsigned int
    choices = operationMask & [sender draggingSourceOperationMask] & ~NSDragOperationPrivate;
    while (choices & (choices - 1))
        choices &= choices - 1;
    return choices;
}


/* shelf listener methods */

+ (void)shelf:sender dragWillEnter: (id <NSDraggingInfo>) source
{
    draggedImage = [[source draggedImage] copy];
}

+ (void)shelf:sender dragWillExit: (id <NSDraggingInfo>) source
{
    [draggedImage release];
    draggedImage = nil;
}

+ (void)shelf:sender dragWillComplete: (id <NSDraggingInfo>) source
{
    [draggedImage release];
    draggedImage = nil;
}

- (BOOL)performDragOperation:(id <NSDraggingInfo>) source
{
    id		draggedFile;
    id		pasteboard = [source draggingPasteboard];
    int		i;
    id		infoDict;
    id		pathArray;

    draggedFile = [[self class] readFromPasteboard:pasteboard];

    // If we are an application the we propably should open that file.
    // If the command key is pressed we will open if no matter 

    if( isApplication && isFileWrapper )
    {
        if( [draggedFile isKindOfClass:[File class]] )
        {
            NSLog( @"Should perform drop... if we are an app...open the file.. %@", [draggedFile path] );

            infoDict = [NSMutableDictionary new];
            [infoDict setObject:[self path] forKey:@"App"];
            [infoDict setObject:[NSArray arrayWithObject:[draggedFile path]] forKey:@"Files"];

            if( [[NSUserDefaults standardUserDefaults] boolForKey:@"UseWorkspaceThreads"] )
                [NSThread detachNewThreadSelector:@selector(_detachedAppBasedFileOpen:)
                                         toTarget:self
                                       withObject:infoDict];
            else
                [self performSelector:@selector(_detachedAppBasedFileOpen:)
                           withObject:infoDict
                           afterDelay:0.5];
        }
        else if( [draggedFile isKindOfClass:[FileList class]] )
        {
            // count, objectAtIndex:  etc.

            infoDict = [NSMutableDictionary new];
            [infoDict setObject:[self path] forKey:@"App"];
            pathArray = [NSMutableArray new];

            for( i=0; i<[draggedFile count]; i++ )
                [pathArray addObject:[[draggedFile objectAtIndex:i] path]];

            [infoDict setObject:pathArray forKey:@"Files"];

            if( [[NSUserDefaults standardUserDefaults] boolForKey:@"UseWorkspaceThreads"] )
                [NSThread detachNewThreadSelector:@selector(_detachedAppBasedFileOpen:)
                                         toTarget:self
                                       withObject:infoDict];
            else
                [self performSelector:@selector(_detachedAppBasedFileOpen:)
                           withObject:infoDict
                           afterDelay:0.5];
       }
    }
    else if( flags.dragAccepting )
    {
        // Ok...seems like we are a folder and propably should trigger a copy or move operation based on the
        // drag modifier that we have... or disable the drag if it violates the file system rules (e.g permission etc)
    }
    return YES;
}

- (BOOL)_applicationSupportsFile:(id)path
{
    // We currently accept every file..no restriction what so ever... hmm.. thats definitly ugly.
    // this will change at least for OpenStep applications where the necessary info should be in the Info.dict !

    // <<HACK>> It definitly is a hack that we don't have a subclass ExecutableFile : File or that like which would
    // handle this properly without all the ifs etc.

    return YES;
}

- (void)_detachedAppBasedFileOpen:(id)infoDict
{
    // Subppol needed if threaded...

    id		threadPool = [NSAutoreleasePool new];
    id		pathEnum = [[infoDict objectForKey:@"Files"] objectEnumerator];
    id		nextFile;
    id		appPath = [infoDict objectForKey:@"App"];

    NSLog( @"Opening with info... %@", infoDict );

    while( nextFile = [pathEnum nextObject] )
        [[NSWorkspace sharedWorkspace] openFile:nextFile
                                withApplication:appPath
                                  andDeactivate:YES];
    [threadPool release];
}


@end
