/*
  Copyright (C) 2000-2005 SKYRIX Software AG

  This file is part of SOPE.

  SOPE is free software; you can redistribute it and/or modify it under
  the terms of the GNU Lesser General Public License as published by the
  Free Software Foundation; either version 2, or (at your option) any
  later version.

  SOPE 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 Lesser General Public
  License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with SOPE; see the file COPYING.  If not, write to the
  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
  02111-1307, USA.
*/

#include "NGTextStream.h"
#include "common.h"

@implementation NGTextStream

- (void)dealloc {
  [self->lastException release];
  [super dealloc];
}

/* NGTextInputStream */

- (NSException *)lastException {
  return nil;
}
- (void)setLastException:(NSException *)_exception {
  ASSIGN(self->lastException, _exception);
}
- (void)resetLastException {
  [self->lastException release];
  self->lastException = nil;
}

- (unichar)readCharacter {
  [self subclassResponsibility:_cmd];
  return 0;
}

- (BOOL)isOpen {
  return YES;
}

/* NGExtendedTextInputStream */

- (NSString *)readLineAsString {
  NSMutableString *str;
  unichar c;

  *(&str) = (id)[NSMutableString string];

  NS_DURING {
    while ((c = [self readCharacter]) != '\n')
      [str appendString:[NSString stringWithCharacters:&c length:1]];
  }
  NS_HANDLER {
    if ([localException isKindOfClass:[NGEndOfStreamException class]]) {
      if ([str length] == 0) str = nil;
    }
    else
      [localException raise];
  }
  NS_ENDHANDLER;
  
  return str;
}

- (unsigned)readCharacters:(unichar *)_chars count:(unsigned)_count {
  /*
    Read up to _count characters, but one at the minimum.
  */
  volatile unsigned pos;

  NS_DURING {
    for (pos = 0; pos < _count; pos++)
      _chars[pos] = [self readCharacter];
  }
  NS_HANDLER {
    if ([localException isKindOfClass:[NGEndOfStreamException class]]) {
      if (pos == 0)
        [localException raise];
    }
    else
      [localException raise];
  }
  NS_ENDHANDLER;

  NSAssert1(pos > 0, @"invalid character count to be returned: %i", pos);
  return pos;
}

- (BOOL)safeReadCharacters:(unichar *)_chars count:(unsigned)_count {
  volatile unsigned pos;
  
  for (pos = 0; pos < _count; pos++)
    _chars[pos] = [self readCharacter];
  
  return YES;
}

/* NGTextOutputStream */

- (BOOL)writeCharacter:(unichar)_character {
  [self subclassResponsibility:_cmd];
  return NO;
}

- (BOOL)writeString:(NSString *)_string {
  unsigned length = [_string length], cnt = 0;
  unichar  buffer[length];
  void     (*writeChar)(id, SEL, unichar);

  writeChar = (void (*)(id,SEL,unichar))
    [self methodForSelector:@selector(writeCharacter:)];
  
  [_string getCharacters:buffer];
  for (cnt = 0; cnt < length; cnt++)
    writeChar(self, @selector(writeCharacter:), buffer[cnt]);
  
  return YES;
}

- (BOOL)flush {
  return YES;
}

/* NGExtendedTextOutputStream */

- (BOOL)writeFormat:(NSString *)_format arguments:(va_list)_ap {
  NSString *tmp;
  
  tmp = [[[NSString alloc] initWithFormat:_format arguments:_ap] autorelease];
  [self writeString:tmp];
  return YES;
}
- (BOOL)writeFormat:(NSString *)_format, ... {
  va_list ap;
  BOOL res = NO;

  va_start(ap, _format);

#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050
  /* As soon as we add an exception handler on Leopard compilation
   * breaks. Probably some GCC bug.
   */
  res = [self writeFormat:_format arguments:ap];
#else
  NS_DURING {
    res = [self writeFormat:_format arguments:ap];
  }
  NS_HANDLER {
    va_end(ap);
    [localException raise];
  }
  NS_ENDHANDLER;
#endif
  
  va_end(ap);

  return res;
}

- (BOOL)writeNewline {
  if (![self writeString:@"\n"]) return NO;
  return [self flush];
}

- (unsigned)writeCharacters:(const unichar *)_chars count:(unsigned)_count {
  /*
    Write up to _count characters, but one at the minimum.
  */
  volatile unsigned pos;

  NS_DURING {
    for (pos = 0; pos < _count; pos++)
      [self writeCharacter:_chars[pos]];
  }
  NS_HANDLER {
    if ([localException isKindOfClass:[NGEndOfStreamException class]]) {
      if (pos == 0)
        [localException raise];
    }
    else
      [localException raise];
  }
  NS_ENDHANDLER;

  NSAssert1(pos > 0, @"invalid character count to be returned: %i", pos);
  return pos;
}

- (BOOL)safeWriteCharacters:(const unichar *)_chars count:(unsigned)_count {
  unsigned pos;

  for (pos = 0; pos < _count; pos++) {
    if (![self writeCharacter:_chars[pos]])
      return NO;
  }
  
  return YES;
}

@end /* NGTextStream */
