/* Copyright (c) 1996 by Lele Gaifax.  All Rights Reserved
 *
 * This software may be used and distributed freely for any purpose
 * provided that this notice is included unchanged on any and all
 * copies. The author does not warrant or guarantee this software in
 * any way.
 *
 * This file is part of the PyObjC package.
 *
 * $RCSfile: OC_Stream.m,v $
 * $Revision: 1.1.1.3 $
 * $Date: 1996/10/23 17:19:50 $
 *
 * Created Fri Oct  4 13:38:20 1996.
 */

#include "ObjC.h"
#include "OC_Stream.h"

#ifndef WITH_FOUNDATION

#include <libc.h>
#include <streams/streams.h>

#define CLASS_VERSION 0

#define ASSERT_OPEN() if (!stream) NX_RAISE(OC_closedStreamError, NULL, NULL)
#define ASSERT_SEEKABLE() ASSERT_OPEN(); else if (! stream->flags & NX_CANSEEK) NX_RAISE(OC_nonSeekableStreamError, NULL, NULL)
#define ASSERT_READABLE() ASSERT_OPEN(); else if (! stream->flags & NX_CANREAD) NX_RAISE(OC_nonReadableStreamError, NULL, NULL)
#define ASSERT_WRITEABLE() ASSERT_OPEN(); else if (! stream->flags & NX_CANWRITE) NX_RAISE(OC_nonWriteableStreamError, NULL, NULL)

@interface OC_MemoryStream : OC_Stream
{
}

+ newFromMemory:(void *) mem length:(unsigned int) len;
+ newFromMemory:(void *) mem length:(unsigned int) len withMode:(int) m;

- (void) close;

@end /* OC_MemoryStream class interface */

@interface OC_MappedFileStream : OC_MemoryStream
{
}

+ newFromFilename:(const char *) name;
+ newFromFilename:(const char *) name withMode:(int) m;

@end /* OC_MappedFileStream class interface */

@implementation OC_Stream

+ (void) initialize
{
  if (self == [OC_Stream class])
    {
      [OC_Stream setVersion:CLASS_VERSION];
    }
}

+ newFromStream:(NXStream *) s
{
  id instance = [[self alloc] initFromStream:s];

  return instance;
}

+ newFromFilename:(const char *) name
{
  return [OC_MappedFileStream newFromFilename:name];
}

+ newFromFilename:(const char *) name withMode:(int) m
{
  return [OC_MappedFileStream newFromFilename:name withMode:m];
}

+ newFromMemory:(void *) mem length:(unsigned int) len
{
  return [OC_MemoryStream newFromMemory:mem length:len withMode:NX_READONLY];
}

+ newFromMemory:(void *) mem length:(unsigned int) len withMode:(int) m
{
  return [OC_MemoryStream newFromMemory:mem length:len withMode:m];
}

- initFromStream:(NXStream *) s
{
  if (s == NULL)
    {
      PyErr_SetString (ObjCStreams_Error, "invalid stream");
      return nil;
    }

  [super init];
  stream = s;
  return self;
}

- (void) freeWhenDone:(BOOL) yn
{
  freeWhenDone = yn;
}

- free
{
  if (freeWhenDone && stream)
    [self close];
  return [super free];
}

- (BOOL) saveToFilename:(const char *) name
{
  ASSERT_OPEN();
  if (NXSaveToFile (stream, name) == -1)
    return NO;
  else
    return YES;
}

- (NXStream *) stream
{
  return stream;
}

- (int) readByte:(char *) cp
{
  char c;

  ASSERT_READABLE();
  c = NXGetc (stream);
  if (c != EOF && cp)
    *cp = c;

  return c;
}

- (int) readBytes:(void *) buf length:(unsigned int) len
{
  ASSERT_READABLE();
  return NXRead (stream, buf, len);
}

- (int) writeByte:(char) c
{
  ASSERT_WRITEABLE();
  return NXPutc (stream, c);
}

- (int) writeBytes:(void *) buf length:(unsigned int) len
{
  ASSERT_WRITEABLE();
  return NXWrite (stream, buf, len);
}

- (void) setStreamPosition:(long) pos seekMode:(int) mode
{
  ASSERT_SEEKABLE();
  NXSeek (stream, pos, mode);
}

- (long) streamPosition
{
  ASSERT_OPEN();
  return NXTell (stream);
}

- (BOOL) isAtEof
{
  ASSERT_OPEN();
  return NXAtEOS (stream);
}

- (void) flushStream
{
  ASSERT_OPEN();
  NXFlush (stream);
}

- (void) close
{
  if (stream)
    {
      NXClose (stream);
      stream = NULL;
    }
}

- (BOOL) isClosed
{
  return stream == NULL;
}

- (BOOL) isWritable
{
  return (stream && stream->flags & NX_CANWRITE);
}

@end /* OC_Stream class implementation */

@implementation OC_MemoryStream

+ newFromMemory:(void *) p length:(unsigned int) l
{
  return [self newFromMemory:p length:l withMode:NX_READONLY];
}

+ newFromMemory:(void *) p length:(unsigned int) l withMode:(int) m
{
  BOOL append;

  if (m == OCS_APPEND)
    {
      m = NX_READWRITE;
      append = YES;
    }
  else
    append = NO;
  
  if (m != NX_READONLY && (p || l))
    {
      PyErr_SetString (ObjCStreams_Error, "Writable memory stream must be initialized with NULL");
      return nil;
    }
  else
    {
      NXStream *s = NXOpenMemory (p, l, m);
      OC_MemoryStream *instance;
      
      if (s == 0)
	{
	  PyErr_SetString (ObjCStreams_Error, "cannot open memory stream");
	  return nil;
	}

      if (append)
	NXSeek (s, 0, NX_FROMEND);
      
      instance = [[self alloc] initFromStream:s];
      [instance freeWhenDone:YES];
      return instance;
    }
}

- (void) close
{
  [super close];		// XXX should free the buffer
				// with vm_deallocate().
}

@end /* OC_MemoryStream class implementation */

@implementation OC_MappedFileStream

+ newFromFilename:(const char *) name
{
  return [self newFromFilename:name withMode:NX_READONLY];
}

+ newFromFilename:(const char *) name withMode:(int) m
{
  OC_MappedFileStream *instance;
  NXStream *s;
  BOOL append;
  
  if (m == OCS_APPEND)
    {
      m = NX_READWRITE;
      append = YES;
    }
  else
    append = NO;
  
  s = NXMapFile (name, m);

  if (s == 0)
    {
      PyErr_SetString (ObjCStreams_Error, "unable to open file");
      return nil;
    }

  if (append)
    NXSeek (s, 0, NX_FROMEND);
  
  instance = [[self alloc] initFromStream:s];
  [instance freeWhenDone:YES];

  return instance;
}

@end /* OC_MappedFileStream implementation */

#else /* WITH_FOUNDATION */

@implementation OC_Stream

+ newFromMemory:(void *) p length:(unsigned int) l
{
  return [self dataWithBytes:p length:l];
}

+ newFromMemory:(void *) p length:(unsigned int) l withMode:(int) m
{
  if (m == OCS_READONLY)
    return [self dataWithBytes:p length:l];
  else
    {
      [self notImplemented:_cmd];
      return nil;
    }
}

+ newFromFilename:(const char *) name
{
  [self newFromFilename:name withMode:OCS_READONLY];
}

+ newFromFilename:(const char *) name withMode:(int) m
{
  id data;
  id path = [NSString stringWithCString:name];
  
  if (m == OCS_READONLY)
    data = [self dataWithContentsOfMappedFile:path];
  else
    {
      [self notImplemented:_cmd];
      data = nil;
    }

  return data;
}

- (BOOL) saveToFilename:(const char *) name
{
  id path = [NSString stringWithCString:name];

  return [self writeToFile:path atomically:NO]; // XXX ???
}

- (int) readByte:(unsigned char *) cp
{
  char onebyte;
  NSRange range = (NSRange) { offset, 1 };

  if (offset >= [self length])
    return -1;
  
  [self getBytes:&onebyte range:range];
  if (cp)
    *cp = onebyte;
  offset++;
  
  return onebyte;
}

- (int) readBytes:(void *) buf length:(int) len
{
  NSRange range;

  if (offset >= [self length])
    return -1;

  if (offset+len >= [self length])
    len = [self length] - offset;
  range = (NSRange) { offset, len };
  
  [self getBytes:buf range:range];
  offset += len;

  return len;
}

- (int) writeByte:(unsigned char) c
{
  [self notImplemented:_cmd];
  return -1;
}

- (int) writeBytes:(const void *) buf length:(int) len
{
  [self notImplemented:_cmd];
  return -1;
}

- (void) setStreamPosition:(long) pos seekMode:(int) mode
{
  switch (mode)
    {
    case OCS_FROMCURRENT:
      pos += offset;
      /* FALLTHROUGH */
      
    case OCS_FROMSTART:
      if (pos >= [self length])
	offset = [self length];
      else
	offset = pos;
      break;

    case OCS_FROMEND:
      offset = [self length];
      if (-pos > offset)
	offset = 0;
      else
	offset += pos;
      break;
    }
}
      
- (unsigned int) streamPosition
{
  return offset;
}

- (BOOL) isAtEof
{
  return offset == [self length];
}

- (void) flushStream
{
}

- (void) close
{
}

- (BOOL) isClosed
{
  return NO;
}

- (BOOL) isWritable
{
  [self notImplemented:_cmd];
  return NO;
}

- (int) writeFormat: (id <String>)format, ...
{
  [self notImplemented:_cmd];
  return -1;
}

- (int) readFormat: (id <String>)format, ...
{
  [self notImplemented:_cmd];
  return -1;
}
  
- (int) writeFormat: (id <String>)format arguments: (va_list)arg
{
  [self notImplemented:_cmd];
  return -1;
}

- (int) readFormat: (id <String>)format arguments: (va_list)arg
{
  [self notImplemented:_cmd];
  return -1;
}

- (void) writeLine: (id <String>)l
{
  [self notImplemented:_cmd];
}

- (id <String>) readLine
{
  [self notImplemented:_cmd];
  return nil;
}

@end /* Stream OC_Stream category implementation */

#endif /* GNU_RUNTIME */

/*
** Local Variables:
** change-log-default-name:"../ChangeLog.PyObjC"
** End:
*/
