/* 
   NSPosixFileDescriptor.m

   Copyright (C) 1995, 1996 Ovidiu Predescu and Mircea Oancea.
   All rights reserved.

   Author: Mircea Oancea <mircea@jupiter.elcom.pub.ro>

   This file is part of libFoundation.

   Permission to use, copy, modify, and distribute this software and its
   documentation for any purpose and without fee is hereby granted, provided
   that the above copyright notice appear in all copies and that both that
   copyright notice and this permission notice appear in supporting
   documentation.

   We disclaim all warranties with regard to this software, including all
   implied warranties of merchantability and fitness, in no event shall
   we be liable for any special, indirect or consequential damages or any
   damages whatsoever resulting from loss of use, data or profits, whether in
   an action of contract, negligence or other tortious action, arising out of
   or in connection with the use or performance of this software.
*/

#include <errno.h>

#include <Foundation/common.h>
#include <Foundation/NSString.h>
#include <Foundation/NSArray.h>
#include <Foundation/NSData.h>
#include <Foundation/NSException.h>
#include <Foundation/NSPosixFileDescriptor.h>
#include <Foundation/exceptions/GeneralExceptions.h>

#include "NSMappedData.h"

@implementation NSPosixFileDescriptor

// Getting a standard NSPosixFileDescriptor

static NSPosixFileDescriptor* descriptorForStandardInput = nil;
static NSPosixFileDescriptor* descriptorForStandardOutput = nil;
static NSPosixFileDescriptor* descriptorForStandardError = nil;

+ (void)initialize
{
    if (!descriptorForStandardInput)
	descriptorForStandardInput = [[self alloc] 
	    initWithFileDescriptor:fileno(stdin)];
    if (!descriptorForStandardOutput)
	descriptorForStandardOutput = [[self alloc] 
	    initWithFileDescriptor:fileno(stdout)];
    if (!descriptorForStandardError)
	descriptorForStandardError = [[self alloc] 
	    initWithFileDescriptor:fileno(stderr)];
}

+ descriptorWithStandardInput
{
    return descriptorForStandardInput;
}

+ descriptorWithStandardOutput
{
    return descriptorForStandardOutput;
}

+ descriptorWithStandardError
{
    return descriptorForStandardError;
}

// Initialize

- initWithFileDescriptor:(int)fileDescriptor
{
    fd = fileDescriptor;
    return self;
}

- initWithCStringPath:(const char*)aPath flags:(int)someFlags 
  createMode:(int)someMode
{
    fd = open(aPath, someFlags, someMode);
    if (fd == -1) {
	[self autorelease];
	return nil;
    }
    return self;
}

- initWithPath:(NSString*)aPath
{
    return [self initWithCStringPath:[aPath fileSystemRepresentation] 
	    flags:O_RDONLY createMode:0];
}

- initWithPath:(NSString*)aPath flags:(int)someFlags
{
    return [self initWithCStringPath:[aPath fileSystemRepresentation] 
	    flags:someFlags createMode:0];
}

- initWithPath:(NSString*)aPath flags:(int)someFlags 
  createMode:(int)someMode
{
    return [self initWithCStringPath:[aPath fileSystemRepresentation] 
	    flags:someFlags createMode:someMode];
}

- (void)dealloc
{
    close(fd);
    [delegate release];
    [super dealloc];
}

// Get FD

- (int)fileDescriptor
{
    return fd;
}

// Read

- (NSData*)readEntireFile
{
    NSRange range = {0, lseek(fd, 0, SEEK_END)};
    return [self readFileRange:range]; 
}

- (NSData*)readRestOfFile
{
    NSRange range;

    range.location = lseek(fd, 0, SEEK_CUR);
    range.length = lseek(fd, 0, SEEK_END) - range.location;
    return [self readFileRange:range];
}

- (NSData*)readFileRange:(NSRange)range
{	
    void* bytes;

    bytes = Malloc(range.length);
    lseek(fd, range.location, SEEK_SET);
    if (read(fd, bytes, range.length) != range.length) {
	Free(bytes);
	THROW([[PosixFileOperationException alloc]
	    initWithFormat:@"could not read %d bytes", range.length]);
    }
    
    return [[[NSData alloc] initWithBytesNoCopy:bytes length:range.length]
	    autorelease];
}

- (void)readBytes:(void*)bytes range:(NSRange)range
{
    lseek(fd, range.location, SEEK_SET);
    if (read(fd, bytes, range.length) != range.length)
	THROW([[PosixFileOperationException alloc]
	    initWithFormat:@"could not read %d bytes", range.length]);
}

- (NSData*)readFileLength:(long)length
{
    NSRange range = {lseek(fd, 0, SEEK_CUR), length};
    
    return [self readFileRange:range];
}

// Write

- (void)writeData:(NSData*)aData
{
    NSRange range = {0, [aData length]};    
    [self writeData:aData range:range];
}

- (void)writeData:(NSData*)aData range:(NSRange)range
{
    char* bytes;
    
    if (range.location + range.length > [aData length])
	THROW([[RangeException alloc]
		initWithReason:@"invalid range in NSData" size:[aData length] 
		    index:range.location+range.length]);
    
    bytes = (char*)[aData bytes] + range.location;
    if (write(fd, bytes, range.length) != range.length)
	THROW([[PosixFileOperationException alloc]
	    initWithFormat:@"could not write %d bytes", range.length]);
}

- (void)writeString:(NSString*)string
{
    NSRange range = {0, [string cStringLength]};    
    [self writeString:string range:range];
}

- (void)writeString:(NSString*)string range:(NSRange)range
{
    char* bytes;
    
    if (range.location + range.length > [string cStringLength])
	THROW([[RangeException alloc]
		initWithReason:@"invalid range in NSString"
		    size:[string cStringLength] 
		    index:range.location + range.length]);
    
    bytes = (char*)[string cString] + range.location;
    if (write(fd, bytes, range.length) != range.length)
	THROW([[PosixFileOperationException alloc]
	    initWithFormat:@"could not write %d bytes", range.length]);
}

// Seek

- (int unsigned)fileLength
{
    long cur = lseek(fd, 0, SEEK_CUR);
    long len = lseek(fd, 0, SEEK_END);
    lseek(fd, cur, SEEK_SET);
    return len;
}

- (int unsigned)filePosition
{
    return lseek(fd, 0, SEEK_CUR);
}

- (void)seekToEnd
{
    lseek(fd, 0, SEEK_END);
}

- (void)seekToPosition:(long)aPosition
{
    lseek(fd, aPosition, SEEK_SET);
}

- (void)truncateAtPosition:(long)aPosition
{
    lseek(fd, aPosition, SEEK_SET);
    if (ftruncate(fd, aPosition) != 0)
	THROW([[PosixFileOperationException alloc]
	    initWithFormat:@"could not truncate file"]);
}

// Mapping files to memory

- (NSData *)mapFileRange:(NSRange)range
{
    return [[[NSMappedData alloc]
	initWithPosixFileDescriptor:self range:range]
	autorelease];
}

- (void)synchronizeFile
{
    if (fsync(fd) != 0)
	THROW([[PosixFileOperationException alloc]
	    initWithFormat:@"could not sync file"]);
}

// Monitoring file descriptors

- (void)ceaseMonitoringFileActivity
{
    // TODO
}

- (NSPosixFileActivity)fileActivity
{
    // TODO
    return NSPosixNoActivity;
}

- (void)monitorFileActivity:(NSPosixFileActivity)activity
{
    [self monitorActivity:activity delegate:delegate];
}

- (void)monitorActivity:(NSPosixFileActivity)activity delegate:(id)delegate
{
    // TODO
}

// File descriptor delegate

- delegate
{
    return delegate;
}

- (void)setDelegate:(id)aDelegate
{
    [delegate autorelease];
    delegate = [aDelegate retain];
}

@end /* NSPosixFileDescriptor */
