/* Implementation of GNU Objective-C class for streaming C types and indentatn
   Copyright (C) 1994, 1995, 1996, 1997 Free Software Foundation, Inc.
   
   Written by:  Andrew Kachites McCallum <mccallum@gnu.ai.mit.edu>
   Date: July 1994
   
   This file is part of the GNUstep Base Library.

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.
   
   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.
   
   You should have received a copy of the GNU Library General Public
   License along with this library; if not, write to the Free
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
   */ 

#include <config.h>
#include <base/preface.h>
#include <base/CStream.h>
#include <base/NSString.h>
#include <base/StdioStream.h>
#include <base/CoderPrivate.h> /* for SIGNATURE_FORMAT_STRING */
#include <Foundation/NSException.h>

id CStreamSignatureMalformedException = @"CStreamSignatureMalformedException";
id CStreamSignatureMismatchException  = @"CStreamSignatureMismatchException";

@implementation CStream


/* Encoding/decoding C values */

- (void) encodeValueOfCType: (const char*) type 
         at: (const void*) d 
         withName: (NSString*) name;
{
  [self subclassResponsibility:_cmd];
}

- (void) decodeValueOfCType: (const char*) type 
         at: (void*) d 
         withName: (NSString* *) namePtr;
{
  [self subclassResponsibility:_cmd];
}


/* Signature methods. */

- (void) writeSignature
{
  /* Careful: the string should not contain newlines. */
  [stream writeFormat: SIGNATURE_FORMAT_STRING,
	  STRINGIFY(GNUSTEP_BASE_PACKAGE_NAME),
	  GNUSTEP_BASE_MAJOR_VERSION,
	  GNUSTEP_BASE_MINOR_VERSION,
	  GNUSTEP_BASE_SUBMINOR_VERSION,
	  object_get_class_name(self),
	  format_version];
}

+ (void) readSignatureFromStream: s
		    getClassname: (char *) name
                   formatVersion: (int*) version
{
  int got;
  char package_name[64];
  int major_version;
  int minor_version;
  int subminor_version;

  got = [s readFormat: SIGNATURE_FORMAT_STRING,
	   &(package_name[0]), 
	   &major_version,
	   &minor_version,
	   &subminor_version,
	   name, 
	   version];
  if (got != 6)
    [NSException raise:CStreamSignatureMalformedException
      format: @"CStream found a malformed signature"];
}


/* Initialization methods */

/* This is the hidden designated initializer.  Do not call it yourself. */
- _initWithStream: (id <Streaming>) s
    formatVersion: (int)version
{
  [super init];
  [s retain];
  stream = s;
  format_version = version;
  indentation = 0;
  return self;
}

/* This is the designated initializer for reading. */
- _initForReadingFromPostSignatureStream: (id <Streaming>)s
		       withFormatVersion: (int)version
{
  [self _initWithStream: s
	formatVersion: version];
  return self;
}

- initForReadingFromStream: (id <Streaming>) s
	     withFormatVersion: (int)version
{
  [self notImplemented: _cmd];
  /* xxx Why this condition? -mccallum */
  if ([stream streamPosition] != 0)
    {
      char name[128];		/* max class name length. */
      int version;
      [[self class] readSignatureFromStream: stream
		    getClassname: name
		    formatVersion: &version];
      if (!strcmp(name, object_get_class_name(self))
	  || version != format_version)
	{
	  [NSException raise: CStreamSignatureMismatchException
		       format: @"CStream found a mismatched signature"];
	}
      [self _initForReadingFromPostSignatureStream: s
	    withFormatVersion: version];
    }
  return self;
}

+ cStreamReadingFromStream: (id <Streaming>) s
{
  char name[128];		/* Maximum class name length. */
  int version;
  id new_cstream;

  [self readSignatureFromStream: s
	getClassname: name
	formatVersion: &version];
  new_cstream = [[objc_lookup_class(name) alloc] 
		  _initForReadingFromPostSignatureStream: s
		  withFormatVersion: version];
  return [new_cstream autorelease];
}

+ cStreamReadingFromFile: (NSString*) filename
{
  return [self cStreamReadingFromStream:
		 [StdioStream streamWithFilename: filename fmode: "r"]];
}

/* This is a designated initializer for writing. */
- initForWritingToStream: (id <Streaming>) s
       withFormatVersion: (int)version
{
  [self _initWithStream: s
	formatVersion: version];
  [self writeSignature];
  return self;
}

- initForWritingToStream: (id <Streaming>) s
{
  return [self initForWritingToStream: s
	       withFormatVersion: [[self class] defaultFormatVersion]];
}

- initForWritingToFile: (NSString*) file
{
  return [self initForWritingToStream: 
		 [StdioStream streamWithFilename: file fmode: "w"]];
}

+ cStreamWritingToStream: (id <Streaming>) s
{
  return [[[self alloc] initForWritingToStream: s]
	   autorelease];
}

+ cStreamWritingToFile: (NSString*) filename;
{
  return [[[self alloc] initForWritingToFile: filename]
	   autorelease];
}


/* Encoding/decoding indentation */

- (void) encodeWithName: (NSString*) name
	 valuesOfCTypes: (const char *) types, ...
{
  va_list ap;

  [self encodeName: name];
  va_start (ap, types);
  while (*types)
    {
      [self encodeValueOfCType: types
	    at: va_arg(ap, void*)
	    withName: NULL];
      types = objc_skip_typespec (types);
    }
  va_end (ap);
}

- (void) decodeWithName: (NSString* *)name
	 valuesOfCTypes: (const char *)types, ...
{
  va_list ap;

  [self decodeName: name];
  va_start (ap, types);
  while (*types)
    {
      [self decodeValueOfCType: types
	    at: va_arg (ap, void*)
	    withName: NULL];
      types = objc_skip_typespec (types);
    }
  va_end (ap);
}

- (void) encodeIndent
{
  /* Do nothing */
}

- (void) encodeUnindent
{
  /* Do nothing */
}

- (void) decodeIndent
{
  /* Do nothing */
}

- (void) decodeUnindent
{
  /* Do nothing */
}

- (void) encodeName: (NSString*) n
{
  /* Do nothing */
}

- (void) decodeName: (NSString* *) name
{
  /* Do nothing */
}


/* Access to the underlying stream. */

- (id <Streaming>) stream
{
  return stream;
}


/* Deallocation. */

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


/* Returning default format version. */

+ (int) defaultFormatVersion
{
  [self subclassResponsibility:_cmd];
  return 0;
}

@end
