/*
**  NSData+Extensions.m
**
**  Copyright (c) 2001, 2002, 2003
**
**  Author: Alexander Malmberg <alexander@malmberg.org>
**          Ludovic Marcotte <ludovic@Sophos.ca>
**
**  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/NSData+Extensions.h"

#include <Foundation/NSArray.h>
#include <Foundation/NSString.h>

#include "Pantomime/Constants.h"

#include <string.h>

// TODO:
// add an NSData cluster member NSSubrangeData that retaind its parent and
// used its data. Would make almost all of these operations work without
// copying.

@implementation NSData (PantomimeExtensions)


+ (id) dataWithCString: (const char *) theCString
{
  return [self dataWithBytes: theCString 
	       length: strlen(theCString)];
}



- (NSRange) rangeOfData: (NSData *) theData
{
  const char *b, *bytes, *str;
  int i, len, slen;
  
  bytes = [self bytes];
  len = [self length];

  if ( !theData )
    {
      return NSMakeRange(NSNotFound,0);
    }
  
  slen = [theData length];
  str = [theData bytes];
  
  b = bytes;
  
  // TODO: this could be optimized
  i = 0;
  b += i;
  for (; i<= len-slen; i++, b++)
    {
      if ( !memcmp(str,b,slen) )
	{
	  return NSMakeRange(i,slen);
	}
    }
  
  return NSMakeRange(NSNotFound,0);
}


- (NSRange) rangeOfCString: (const char *) theCString
{
  return [self rangeOfCString: theCString
	       options: 0
	       range: NSMakeRange(0,[self length])];
}

-(NSRange) rangeOfCString: (const char *) theCString
		  options: (unsigned int) options
{
  return [self rangeOfCString: theCString 
	       options: options 
	       range:NSMakeRange(0,[self length])];
}

-(NSRange) rangeOfCString: (const char *) theCString
		  options: (unsigned int) options
		    range: (NSRange) theRange
{
  const char *b, *bytes;
  int i, len, slen;
  
  if ( !theCString)
    {
      return NSMakeRange(NSNotFound,0);
    }
  
  bytes = [self bytes];
  len = [self length];
  slen = strlen(theCString);
  
  b = bytes;
  
  if ( len > theRange.location + theRange.length)
    {
      len = theRange.location + theRange.length;
    }

  // TODO: this could be optimized
  if (options == NSCaseInsensitiveSearch)
    {
      i = theRange.location;
      b +=i ;
      
      for (; i <= len-slen; i++, b++)
	{
	  if ( !strncasecmp(theCString,b,slen) )
	    {
	      return NSMakeRange(i,slen);
	    }
	}
    }
  else
    {
      i = theRange.location;
      b += i;
      
      for (; i <= len-slen; i++, b++)
	{
	  if ( !memcmp(theCString,b,slen) )
	    {
	      return NSMakeRange(i,slen);
	    }
	}
    }
  
  return NSMakeRange(NSNotFound,0);
}


- (NSData *) subdataFromIndex: (int) theIndex
{
  return [self subdataWithRange: NSMakeRange(theIndex, [self length] - theIndex)];
}

- (NSData *) subdataToIndex: (int) theIndex
{
  return [self subdataWithRange: NSMakeRange(0, theIndex)];
}

- (NSData *) dataByTrimmingWhiteSpaces
{
  const char *bytes;
  int i, j, len;
  
  bytes = [self bytes];
  len = [self length];

  for ( i = 0; i < len && bytes[i] == ' '; i++) ;
  for ( j = len-1; j >= 0 && bytes[j] == ' '; j--) ;
  
  if ( j <= i )
    {
      return AUTORELEASE(RETAIN(self));
    }
      
  return [self subdataWithRange: NSMakeRange(i, j-i+1)];
}

- (NSData *) dataByRemovingLineFeedCharacters
{
  const char *bytes;
  int i, j, len;
  char *dest;

  NSMutableData *aMutableData;
  
  bytes = [self bytes];
  len = [self length];
  
  aMutableData = [[NSMutableData alloc] init];
  [aMutableData setLength: len];
  
  dest = [aMutableData mutableBytes];
  
  for (i = j = 0; i < len; i++)
    {
      if ( bytes[i] != '\n')
	{
	  dest[j++] = bytes[i];
	}
    }
  
  [aMutableData setLength: j];
  
  return AUTORELEASE(aMutableData);
}

- (NSData *) dataFromQuotedData
{
  const char *bytes;
  int len;
  
  bytes = [self bytes];
  len = [self length];

  if ( len < 2 )
    {
      return AUTORELEASE(RETAIN(self));
    }
  
  if ( bytes[0] == '"' && bytes[len-1] == '"')
    {
      return [self subdataWithRange: NSMakeRange(1, len-2)];
    }
  
  return AUTORELEASE(RETAIN(self));
}

- (int) indexOfCharacter: (char) theCharacter
{
  const char *b;
  int i, len;
 
  b = [self bytes];
  len = [self length];

  for ( i = 0; i < len; i++, b++)
    if (*b == theCharacter)
      {
	return i;
      }
  
  return -1;
}


- (BOOL) hasCPrefix: (const char *) theCString
{
  const char *bytes;
  int len, slen;
  
  if ( !theCString )
    {
      return NO;
    }
  
  bytes = [self bytes];
  len = [self length];

  slen = strlen(theCString);
  
  if ( slen > len)
    {
      return NO;
    }

  if ( !strncmp(bytes,theCString,slen) )
    {
      return YES;
    }
  
  return NO;
}

- (BOOL) hasCSuffix: (const char *) theCString
{
  const char *bytes;
  int len, slen;
  
  if ( !theCString ) 
    {
      return NO;
    }

  bytes = [self bytes];
  len = [self length];

  slen = strlen(theCString);

  if ( slen > len) 
    {
      return NO;
    }

  if ( !strncmp(&bytes[len-slen],theCString,slen) )
    {
      return YES;
    }
  
  return NO;
}

- (BOOL) hasCaseInsensitiveCPrefix: (const char *) theCString
{
  const char *bytes;
  int len, slen;
  
  if ( !theCString ) 
    {
      return NO;
    }

  bytes = [self bytes];
  len = [self length];
  slen = strlen(theCString);
  
  if ( slen > len)
    {
      return NO;
    }
      
  if ( !strncasecmp(bytes,theCString,slen) )
    {
      return YES;
    }

  return NO;
}

- (BOOL) hasCaseInsensitiveCSuffix: (const char *) theCString
{
  const char *bytes;
  int len, slen;
  
  if ( !theCString )
    {
      return NO;
    }
  
  bytes = [self bytes];
  len = [self length];
  slen = strlen(theCString);

  if ( slen > len ) 
    {
      return NO;
    }  

  if ( !strncasecmp(&bytes[len-slen],theCString,slen) )
    {
      return YES;
    }
  
  return NO;
}



- (NSComparisonResult) caseInsensitiveCCompare: (const char *) theCString
{
  int slen, len, clen, i;
  const char *bytes;
  
  // Is this ok?
  if ( ! theCString )
    {
      return NSOrderedDescending;
    }
      
  bytes = [self bytes];
  len = [self length];
  slen = strlen(theCString);
  
  if ( slen > len )
    {
      clen = len;
    }
  else
    {
      clen = slen;
    }

  i = strncasecmp(bytes,theCString,clen);
  
  if ( i < 0 )
    {
      return NSOrderedAscending;
    }
  
  if ( i > 0 )
    {
      return NSOrderedDescending;
    }
  
  if ( slen == len )
    {
      return NSOrderedSame;
    }

  if ( slen < len )
    {
      return NSOrderedAscending;
    }
  
  return NSOrderedDescending;
}


- (NSArray *) componentsSeparatedByCString: (const char *) theCString
{
  NSMutableArray *aMutableArray;
  NSRange r1, r2;
  int len;
  
  aMutableArray = [[NSMutableArray alloc] init];
  len = [self length];
  r1 = NSMakeRange(0,len);
  
  r2 = [self rangeOfCString: theCString
	     options: 0 
	     range: r1];
  
  while ( r2.length )
    {
      [aMutableArray addObject: [self subdataWithRange: NSMakeRange(r1.location, r2.location - r1.location)]];
      r1.location = r2.location + r2.length;
      r1.length = len - r1.location;
      
      r2 = [self rangeOfCString: theCString
		 options: 0
		 range: r1];
    }

  [aMutableArray addObject: [self subdataWithRange: NSMakeRange(r1.location, len - r1.location)]];
  
  return AUTORELEASE(aMutableArray);
}


- (NSString *) asciiString
{
  return AUTORELEASE([[NSString alloc] initWithData: self 
				       encoding: NSASCIIStringEncoding]);
}


- (const char *) cString
 {
   NSMutableData *aMutableData;

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

   [aMutableData appendData: self];
   [aMutableData appendBytes: "\0"
		 length: 1];
   
   return [aMutableData mutableBytes];
}

@end


@implementation NSMutableData (PantomimeExtensions)

- (void) appendCFormat: (NSString *) format, ...
{
  NSString *aString;
  va_list args;
  
  va_start(args,format);
  aString = [[NSString alloc] initWithFormat: format
			      arguments: args];
  va_end(args);
  
  // We allow lossy conversion to not lose any information
  [self appendData:
	  [aString dataUsingEncoding: NSASCIIStringEncoding
		   allowLossyConversion: YES]];
  
  DESTROY(aString);
}


//
//
//
- (void) appendCString: (const char *) theCString
{
  [self appendBytes: theCString
	length: strlen(theCString)];
}


//
//
//
- (void) insertCString: (const char *) theCString
	       atIndex: (int) theIndex
{
  int s_length, length;

  if ( !theCString )
    {
      return;
    }
  
  s_length = strlen(theCString);

  if ( s_length == 0 )
    {
      return;
    }

  length = [self length];
  
  // We insert at the beginning of the data
  if ( theIndex == 0 )
    {
      NSMutableData *data;

      data = [NSMutableData dataWithBytes: theCString
			    length: s_length];

      [data appendData: self];

      [self setData: data];
    }
  // We insert at the end of the data
  else if ( theIndex >= length )
    {
      [self appendCString: theCString];
    }
  // We insert somewhere in the middle
  else
    {
      NSMutableData *data;

      data = [NSMutableData dataWithBytes: [self subdataWithRange: NSMakeRange(0, theIndex)]
			    length: theIndex];
      
      [data appendCString: theCString];
      [data appendData: [self subdataWithRange: NSMakeRange(theIndex, length - theIndex)]];
      
      [self setData: data];
    }
}


//
// This method destructively elides all \r in the mutable data.
//
- (void) replaceCRLFWithLF
{
  unsigned char *bytes, *bi, *bo;
  int delta, i,length;
  
  bytes = [self mutableBytes];
  length = [self length];
  bi = bo = bytes;
  
  for (i = delta = 0; i < length; i++, bi++)
    {
      if ( i+1 < length && bi[0] == '\r' && bi[1] == '\n')
	{
	  i++;
	  bi++;
	  delta++;
	}
      
      *bo = *bi;
      bo++;
    }
  
  [self setLength: length-delta];
}


//
//
//
- (NSMutableData *) replaceLFWithCRLF
{
  NSMutableData *aMutableData;
  unsigned char *bytes, *bi, *bo;
  int delta, i, length;
  
  bi = bytes = [self mutableBytes];
  length = [self length];
  delta = 0;
  
  if ( bi[0] == '\n' )
    {
      delta++;
    }
  
  bi++;
  
  for (i = 1; i < length; i++, bi++)
    {
      if ( (bi[0] == '\n') && (bi[-1] != '\r') )
	{
	  delta++;
	}
    }
  
  bi = bytes;
  aMutableData = [[NSMutableData alloc] initWithLength: (length+delta)];
  bo = [aMutableData mutableBytes];
  
  for (i = 0; i < length; i++, bi++, bo++)
    {
      if ( (i+1 < length) && (bi[0] == '\r') && (bi[1] == '\n') )
	{
	  *bo = *bi;
	  bo++;
	  bi++;
	  i++;
	}
      else if ( *bi == '\n' )
	{
	  *bo = '\r';
	  bo++;
	}
      
      *bo = *bi;
    }

  return AUTORELEASE(aMutableData);
}

@end

