#import "NSPropertyListSerialization+MullePlistWriter.h"


NSString *MullePlistWriterException = @"MullePlistWriterException";


@interface NSObject ( MullePlistWriter)

- (void) mullePlistWriteIntoData:(NSMutableData *) data
                          indent:(NSData *) indent;

@end


@implementation NSObject ( MullePlistWriter)

- (void) mullePlistWriteIntoData:(NSMutableData *) data
                          indent:(NSData *) indent
{
   [NSException raise:MullePlistWriterException
               format:@"Objects of class %@ can't be saved in a plist", isa];
}

@end


/*
 *
 *
 */

@interface NSData ( MullePlistWriter)

- (void) mullePlistWriteIntoData:(NSMutableData *) data
                          indent:(NSData *) indent;
@end


static inline char	hexit( unsigned char c)
{
   return( c > 10 ? 'A' - 10 + c : '0' + c);
}


@implementation NSData ( MullePlistWriter)

- (void) mullePlistWriteIntoData:(NSMutableData *) data
                          indent:(NSData *) indent
{
   NSMutableData   *buffer;
   char            *hex;
   unsigned char   *s;
   NSUInteger      len;
   NSUInteger      i;
   
   len    = [self length];
   buffer = [NSMutableData dataWithLength:len * 3 + 3];
   
   hex = (char *) [buffer bytes];
   s   = (unsigned char *) [self bytes];

   *hex++ = '<';
   i = 0;
   while( len)
   {
      *hex++ = hexit( *s >> 4);
      *hex++ = hexit( *s & 0xF);
      if( ! (++i & 0x7))
         *hex++ = ' ';
      s++;
   }
   *hex++ = '>';
   
   len = (char *) hex - (char *) [buffer bytes];
   [buffer setLength:len];
   [data appendBytes:buffer
              length:len];
}

@end

/*
 *
 *
 */

@interface NSNumber ( MullePlistWriter)

- (void) mullePlistWriteIntoData:(NSMutableData *) data
                          indent:(NSData *) indent;
@end


@implementation NSNumber ( MullePlistWriter)

- (void) mullePlistWriteIntoData:(NSMutableData *) data
                          indent:(NSData *) indent
{
   char  *s;

   s = (char *) [[self descriptionWithLocale:nil] UTF8String];
   [data appendBytes:s
              length:strlen( s)];
}

@end

/*
 *
 *
 */


@interface NSString ( MullePlistWriter)

- (void) mullePlistWriteIntoData:(NSMutableData *) data
                          indent:(NSData *) indent;

@end


@implementation NSString ( MullePlistWriter)

- (void) mullePlistWriteIntoData:(NSMutableData *) data
                          indent:(NSData *) indent
{
   char      *s;
   BOOL      quoted;
   NSArray   *components;
   NSString  *quotedSelf;
   
   quoted  = [self rangeOfCharacterFromSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]].length;
   quoted |= [self rangeOfCharacterFromSet:[NSCharacterSet punctuationCharacterSet]].length;
   quoted |= [self length] == 0;
      
   components = [self componentsSeparatedByString:@"\""];
   quotedSelf = [components componentsJoinedByString:@"\\\""];
   quoted |= [components count] > 1;
      
   if( quoted)
      [data appendBytes:"\""
                 length:1];


   s = (char *) [quotedSelf UTF8String];
   [data appendBytes:s
              length:strlen( s)];
   if( quoted)
      [data appendBytes:"\""
                 length:1];
}

@end

/*
 *
 *
 */


@interface NSArray ( MullePlistWriter)

- (void) mullePlistWriteIntoData:(NSMutableData *) data
                          indent:(NSData *) indent;

@end


@implementation NSArray ( MullePlistWriter)

- (void) mullePlistWriteIntoData:(NSMutableData *) data
                          indent:(NSData *) indent
{
   NSMutableData   *indent2;
   unsigned int    i, n;

   [data appendBytes:"("
              length:1];
   n = [self count];
   if( n)
   {
      [data appendBytes:"\n"
                 length:1];
      indent2 = [indent mutableCopy];
      [indent2 appendBytes:"   "
                    length:3];
      for( i = 0; i < n; i++)
      {
         if( i > 0)
            [data appendBytes:",\n"
                       length:2];
         [data appendData:indent2];
         [[self objectAtIndex:i] mullePlistWriteIntoData:data
                                                  indent:indent2];
      }
      [data appendBytes:"\n"
                 length:1];
      [data appendData:indent];
   }
   [data appendBytes:")"
              length:1];
}


@end

/*
 *
 *
 */


@interface NSDictionary ( MullePlistWriter)

- (void) mullePlistWriteIntoData:(NSMutableData *) data
                          indent:(NSData *) indent;

@end


@implementation NSDictionary ( MullePlistWriter)

- (void) mullePlistWriteIntoData:(NSMutableData *) data
                          indent:(NSData *) indent
{
   NSArray         *keys;
   id              key;
   id              value;
   NSMutableData   *indent2;
   unsigned int    i, n;

   [data appendBytes:"{"
              length:1];
   keys = [self allKeys];  // sort'em ?
   n    = [keys count];

   if( n)
   {
      [data appendBytes:"\n"
                 length:1];

//      keys    = [keys sortedArrayUsingSelector:@selector( compare:)];
      indent2 = [indent mutableCopy];
      [indent2 appendBytes:"   "
                    length:3];

      for( i = 0; i < n; i++)
      {
         key   = [keys objectAtIndex:i];
         value = [self objectForKey:key];

         [data appendData:indent2];
         [key mullePlistWriteIntoData:data
                               indent:nil];
         [data appendBytes:" = "
                    length:3];
         [value mullePlistWriteIntoData:data
                                 indent:indent2];
         [data appendBytes:";\n"
                    length:2];

      }
      [data appendData:indent];
   }
   [data appendBytes:"}"
              length:1];
}

@end



@implementation NSPropertyListSerialization ( MullePlistWriter)


+ (NSData *) plistDataFromObject:(id) obj
                errorDescription:(NSString **) error
{
   NSMutableData   *data;
   
   if( ! obj)
   {
      *error = @"plist is nil";
      return( nil);
   }
   
   data = [NSMutableData dataWithCapacity:0x1000];
   [obj mullePlistWriteIntoData:data
                         indent:[NSData data]];
   if( ! [data length])
   {
      *error = @"Some objects were not plist compatible";
      return( nil);
   }

#if 0   
   [data appendBytes:""  // trailin zero
              length:1];
#endif   
   return( data);
}


+ (id) mulleDataFromPropertyList:(id) plist
                          format:(NSPropertyListFormat) format
                errorDescription:(NSString **) error
{
   if( format != NSPropertyListOpenStepFormat)
      return( [self dataFromPropertyList:plist
                                  format:format
                         errorDescription:error]);
   
   return( [self plistDataFromObject:plist
                    errorDescription:error]);
}


@end