/*
**  NSString+Extensions.m
**
**  Copyright (c) 2001, 2002, 2003
**
**  Author: Ludovic Marcotte <ludovic@Sophos.ca>
**          Jonathan B. Leffert <jonathan@leffert.net>
**
**  This library 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.1 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
**  Lesser General Public License for more details.
**  
**  You should have received a copy of the GNU Lesser General Public
**  License along with this library; if not, write to the Free Software
**  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/

#include <Pantomime/NSString+Extensions.h>

#include <Pantomime/Constants.h>
#include <Pantomime/NSData+Extensions.h>


#ifdef MACOSX
#import <CoreFoundation/CoreFoundation.h>
#else
#include <GNUstepBase/GSCategories.h>
#endif

#include <ctype.h>

#define IS_PRINTABLE(c) (isascii(c) && isprint(c))

//
//
//
@implementation NSString (PantomimeStringExtensions)

- (NSString *) stringByTrimmingWhiteSpaces
{
#ifndef MACOSX
  return [self stringByTrimmingSpaces];
#else
  NSMutableString *aMutableString;

  aMutableString = [[NSMutableString alloc] initWithString: self];
  CFStringTrimWhitespace((CFMutableStringRef)aMutableString);
  
  return AUTORELEASE(aMutableString);
#endif
}


//
//
//
- (int) indexOfCharacter: (unichar) theCharacter
{
  int i, len;
  
  len = [self length];
  
  for (i = 0; i < len; i++)
    {
      if ([self characterAtIndex: i] == theCharacter)
	{
	  return i;
	}
    }
  
  return -1;
}


//
//
//
- (BOOL) hasCaseInsensitivePrefix: (NSString *) thePrefix
{
  if ( thePrefix )
    {
      return [[self uppercaseString] hasPrefix: [thePrefix uppercaseString]];
    }
  
  return NO;
}


//
//
//
- (BOOL) hasCaseInsensitiveSuffix: (NSString *) theSuffix
{
  if ( theSuffix )
    {
      return [[self uppercaseString] hasSuffix: [theSuffix uppercaseString]];
    }
  
  return NO;
}


//
//
//
- (NSString *) stringFromQuotedString
{
  int len;

  len = [self length];
  
  if ( len > 1 &&
       [self characterAtIndex: 0] == '"' &&
       [self characterAtIndex: (len-1)] == '"' )
    {
      return [self substringWithRange: NSMakeRange(1, len-2)];
    }
  
  return self;
}


//
//
//
- (NSString *) modifiedUTF7String
{
#ifndef MACOSX
  NSMutableData *aMutableData, *modifiedData;
  NSString *aString;

  const char *b;
  BOOL escaped;
  unichar ch;
  int i, len;

  //
  // We UTF-7 encode _only_ the non-ASCII parts.
  //
  aMutableData = [[NSMutableData alloc] init];
  AUTORELEASE(aMutableData);
  len = [self length];
  
  for (i = 0; i < len; i++)
    {
      ch = [self characterAtIndex: i];
      
      if ( IS_PRINTABLE(ch) )
	{
	  [aMutableData appendCFormat: @"%c", ch];
	}
      else
	{
	  int j;

	  j = i+1;
	  // We got a non-ASCII character, let's get the substring and encode it using UTF-7.
	  while (j < len && !IS_PRINTABLE([self characterAtIndex: j]))
	    {
	      j++;
	    }
	  
	  // Get the substring.
	  [aMutableData appendData: [[self substringWithRange: NSMakeRange(i,j-i)] dataUsingEncoding: NSUTF7StringEncoding]];
	  i = j-1;
	}
    }

  b = [aMutableData bytes];
  len = [aMutableData length];
  escaped = NO;

  //
  // We replace:
  //
  // &   ->  &-
  // +   ->  &
  // +-  ->  +
  // /   ->  ,
  //
  // in order to produce our modified UTF-7 string.
  //
  modifiedData = [[NSMutableData alloc] init];
  AUTORELEASE(modifiedData);

  for (i = 0; i < len; i++, b++)
    {
      if (!escaped && *b == '&')
	{
	  [modifiedData appendCString: "&-"];
	}
      else if (!escaped && *b == '+')
	{
	  if (*(b+1) == '-')
	    {
	      [modifiedData appendCString: "+"];
	    }
	  else
	    {
	      [modifiedData appendCString: "&"];

	      // We enter the escaped mode.
	      escaped = YES;
	    }
	}
      else if (escaped && *b == '/')
	{
	  [modifiedData appendCString: ","];
	}
      else if (escaped && *b == '-')
	{
	  [modifiedData appendCString: "-"];

	  // We leave the escaped mode.
	  escaped = NO;
	}
      else
	{
	  [modifiedData appendCFormat: @"%c", *b];
	}
    }
  
  // If we're still in the escaped mode we haven't added our trailing -,
  // let's add it right now.
  if ( escaped )
    {
      [modifiedData appendCString: "-"];
    }

  aString = AUTORELEASE([[NSString alloc] initWithData: modifiedData  encoding: NSASCIIStringEncoding]);

  return (aString != nil ? aString : self);
#else
  return self;
#endif
}


//
// This method returns nil if the conversion from modified UTF-7 to Unicode failed.
//
- (NSString *) stringFromModifiedUTF7
{
#ifndef MACOSX
  NSMutableData *aMutableData;

  BOOL escaped;
  unichar ch;
  int i, len;

  aMutableData = [[NSMutableData alloc] init];
  AUTORELEASE(aMutableData);

  len = [self length];
  escaped = NO;

  //
  // We replace:
  //
  // &   ->  +
  // &-  ->  &
  // ,   ->  /
  //
  // If we are in escaped mode. That is, between a &....-
  //
  for (i = 0; i < len; i++)
    {
      ch = [self characterAtIndex: i];
      
      if (!escaped && ch == '&')
	{
	  if ( (i+1) < len && [self characterAtIndex: (i+1)] != '-' )
	    {
	      [aMutableData appendCString: "+"];
	      
	      // We enter the escaped mode.
	      escaped = YES;
	    }
	  else
	    {
	      // We replace &- by &
	      [aMutableData appendCString: "&"];
	      i++;
	    }
	}
      else if (escaped && ch == ',')
	{
	  [aMutableData appendCString: "/"];
	}
      else if (escaped && ch == '-')
	{
	  [aMutableData appendCString: "-"];

	  // We leave the escaped mode.
	  escaped = NO;
	}
      else
	{
	  [aMutableData appendCFormat: @"%c", ch];
	}
    }

  return AUTORELEASE([[NSString alloc] initWithData: aMutableData  encoding: NSUTF7StringEncoding]);
#else
  return nil;
#endif
}


//
//
//
- (BOOL) hasREPrefix
{
  if ( [self hasCaseInsensitivePrefix: @"re:"] || [self hasCaseInsensitivePrefix: @"re :"] )
    {
      return YES;
    }
  
  return NO;
}


//
//
//
- (NSString *) stringByReplacingOccurrencesOfString: (NSString *) theTarget
					 withString: (NSString *) theReplacement
{
  if ( !theTarget || !theReplacement || [theTarget isEqualToString: theReplacement] )
    {
      return self;
    }
  else
    {
      NSMutableString *aMutableString;

      aMutableString = [NSMutableString stringWithString: self];
      
      [aMutableString replaceOccurrencesOfString: theTarget
		      withString: theReplacement
		      options: 0
		      range: NSMakeRange(0, [self length])];

      return aMutableString;
    }
}
@end
