// Copyright 1997-2002 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 <OWF/OWFileProcessor.h>

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

#import <OWF/OWAddress.h>
#import <OWF/OWFileDataStream.h>
#import <OWF/OWFileInfo.h>
#import <OWF/OWObjectStream.h>
#import <OWF/OWPipeline.h>
#import <OWF/OWSourceProcessor.h>
#import <OWF/OWTimeStamp.h>
#import <OWF/OWURL.h>

RCS_ID("$Header: /Network/Source/CVS/OmniGroup/Frameworks/OWF/Processors.subproj/Protocols.subproj/OWFileProcessor.m,v 1.17 2002/03/09 01:53:52 kc Exp $")

@interface OWFileProcessor (Private)
- (void)_processDirectoryAtPath:(NSString *)filePath;
- (void)_fetchDirectory:(OFDirectory *)directory;
- (void)_fetchRegularFile:(NSString *)filePath;
- (BOOL)_redirectToFTP;
- (BOOL)_redirectHFSPathToPosixPath;
@end

@implementation OWFileProcessor

static NSString *directoryIndexFilename = nil;

+ (void)didLoad;
{
    [self registerProcessorClass:self fromContentType:[OWURL contentTypeForScheme:@"file"] toContentType:[OWSourceProcessor sourceContentType] cost:1.0];
    [[OFController sharedController] addObserver:self];
}

+ (void)controllerDidInitialize:(OFController *)controller;
{
    [self readDefaults];
}

+ (void)readDefaults;
{
    NSUserDefaults *userDefaults;

    userDefaults = [NSUserDefaults standardUserDefaults];
    directoryIndexFilename = [userDefaults stringForKey:@"OWDirectoryIndexFilename"];
}

- initWithPipeline:(OWPipeline *)aPipeline;
{
    if (![super initWithPipeline:aPipeline])
        return nil;

    fileAddress = [(OWAddress *)[pipeline lastContent] retain];

    return self;
}

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

- (void)process;
{
    NSString *filePath;
    OFFile *file;
    NSDate *lastChanged;

    filePath = [fileAddress localFilename];
    file = [OFUnixFile fileWithPath:filePath];

    NS_DURING {
	lastChanged = [file lastChanged];
    } NS_HANDLER {
        if ([self _redirectToFTP] || [self _redirectHFSPathToPosixPath])
            return;
	[localException raise];
    } NS_ENDHANDLER;

    [OWTimeStamp cacheDate:lastChanged forAddress:[pipeline lastAddress]];

    if (![file isDirectory]) {
	[self _fetchRegularFile:filePath];
    } else if ([filePath hasSuffix:@"/"]) {
	[self _fetchDirectory:[OFUnixDirectory directoryWithFile:file]];
    } else {
        [self _processDirectoryAtPath:filePath];
    }
}

@end

@implementation OWFileProcessor (Private)

- (void)_processDirectoryAtPath:(NSString *)filePath;
{
#warning Document wrappers (including RTFD) are not supported at present

    // Redirect from file:/.../x to file:/.../x/
    [pipeline addSourceContent:[OWAddress addressWithFilename:[filePath stringByAppendingString:@"/"]]];
    [pipeline cacheContent];
    [pipeline startProcessingContent];
}

- (void)_fetchDirectory:(OFDirectory *)directory;
{
    OWObjectStream *objectStream;
    NSArray *files;
    unsigned int fileIndex, fileCount;

    [self setStatusString:NSLocalizedStringFromTableInBundle(@"Reading directory", @"OWF", [OWFileProcessor bundle], fileprocessor status)];

    if (directoryIndexFilename && [directory containsFileNamed:directoryIndexFilename]) {
	[self _fetchRegularFile:[[directory path] stringByAppendingPathComponent:directoryIndexFilename]];
	return;
    }

    objectStream = [[OWObjectStream alloc] init];
    [objectStream setContentTypeString:@"ObjectStream/OWFileInfoList"];
    [pipeline addSourceContent:objectStream];
    [pipeline cacheContent];
    files = [directory sortedFiles];
    fileCount = [files count];
    for (fileIndex = 0; fileIndex < fileCount; fileIndex++) {
        OFFile *file;
        OWFileInfo *fileInfo;

        file = [files objectAtIndex:fileIndex];
        fileInfo = [[OWFileInfo alloc] initWithAddress:[OWAddress addressWithFilename:[file path]] size:[file size] isDirectory:[file isDirectory] isShortcut:[file isShortcut] lastChangeDate:[[file lastChanged] dateWithCalendarFormat:NSLocalizedStringFromTableInBundle(@"%d-%b-%Y %H:%M:%S %z", @"OWF", [OWFileProcessor bundle], fileprocessor lastChangeDate calendar format) timeZone:nil]];
        [objectStream writeObject:fileInfo];
        [fileInfo release];
    }
    [objectStream dataEnd];
    [objectStream release];
    [pipeline startProcessingContent];
}

- (void)_fetchRegularFile:(NSString *)filePath;
{
    OWDataStream *fileStream;

    [self setStatusString:NSLocalizedStringFromTableInBundle(@"Reading file", @"OWF", [OWFileProcessor bundle], fileprocessor status)];
    fileStream = [[OWFileDataStream alloc] initWithContentsOfMappedFile:filePath];
    [fileStream setContentTypeAndEncodingFromFilename:filePath isLocalFile:YES];
    [pipeline addSourceContent:fileStream];
    [fileStream release];
    [pipeline cacheContent];
    [self setStatusString:NSLocalizedStringFromTableInBundle(@"Finished reading", @"OWF", [OWFileProcessor bundle], fileprocessor status)];
    [pipeline startProcessingContent];
}

- (BOOL)_redirectToFTP;
{
    OWURL *url;
    NSString *netLocation;

    url = [fileAddress url];
    netLocation = [url netLocation];
    if (netLocation == nil || [netLocation length] == 0 || [netLocation isEqualToString:@"localhost"])
	return NO;

    [pipeline addSourceContent:[OWAddress addressWithURL:[OWURL urlWithScheme:@"ftp" netLocation:netLocation path:[url path] params:[url params] query:[url query] fragment:[url fragment]]]];
    [pipeline startProcessingContent];
    return YES;
}

- (NSString *)_pathForVolumeNamed:(NSString *)name;
{
    unsigned int volumeIndex;
    HFSUniStr255 volumeName;
    FSRef volumeFSRef;
    
    volumeIndex = 1;
    while (FSGetVolumeInfo(kFSInvalidVolumeRefNum, volumeIndex++, NULL, kFSVolInfoNone, NULL, &volumeName, &volumeFSRef) == noErr) {
        if ([[NSString stringWithCharacters:volumeName.unicode length:volumeName.length] isEqualToString:name]) {
            NSURL *url;

            url = [(NSURL *)CFURLCreateFromFSRef(NULL, &volumeFSRef) autorelease];
            return [url path];
        }
    }
    return nil;
}

- (BOOL)_redirectHFSPathToPosixPath;
{
    NSMutableArray *pathComponents;
    NSString *volumeName;
    NSString *volumePath;
    NSString *posixPath;

    pathComponents = [[[[[fileAddress url] path] componentsSeparatedByString:@"/"] mutableCopy] autorelease];
    volumeName = [pathComponents objectAtIndex:0];
    volumePath = [self _pathForVolumeNamed:volumeName];
#ifdef DEBUG_HFS_PATHS
    NSLog(@"volumeName = %@, volumePath = %@", volumeName, volumePath);
#endif
    if (volumePath == nil)
        return NO;
    if ([volumePath hasSuffix:@"/"])
        volumePath = [volumePath substringToIndex:[volumePath length] - 1];
    [pathComponents replaceObjectAtIndex:0 withObject:volumePath];
    posixPath = [pathComponents componentsJoinedByString:@"/"]; 
#ifdef DEBUG_HFS_PATHS
    NSLog(@"posixPath = %@", posixPath);
#endif
    [pipeline addSourceContent:[OWAddress addressWithFilename:posixPath]];
    [pipeline cacheContent];
    [pipeline startProcessingContent];
    return YES;
}

@end
