#import "NSPropertyListSerialization+MullePlistReader.h"
#include <ctype.h>


#define TRY_VALIANTLY_TO_CONVERT  0


@implementation NSPropertyListSerialization ( MullePlistReader)


static inline int   iswhite( int c)
{
   return( c == ' ' || c == '\n' || c == '\r' || c == '\t');
}


static id  _parse_object( char **s, unsigned int *len);

static int   skip_white( char **s, unsigned int *len)
{
    while( *len)
    {
        if( ! iswhite( **s))
            return( 0);
        ++*s;
        --*len;
    }
    return( -1);
}

/*
static NSString  *slurp_identifier( char **s, unsigned int *len)
{
    int    c;
    char   *memo;

    memo = *s;
    while( *len)
    {
        c = **s;
        if( ! (isalnum( c) || c == '_'))
           break;
        ++*s;
        --*len;
    }
    return( [NSString stringWithCString:memo
                                 length:*s - memo]);
}
*/

static id   _parse_value( char **s, unsigned int *len)
{
    int        c;
    char       *memo;
    int        quoted;
    NSString   *string;
    
    quoted = **s == '"';
    if( quoted)
    {
        ++*s;
        --*len;
    }
    
    memo = *s;
    while( *len)
    {
        c = **s;
        if( quoted)
        {
            if( c == '"')
                break;
        }
        else
        {
            if( iswhite( c) || c == ',' || c == ';')
                break;
        }
        ++*s;
        --*len;
    }
    

    string = [NSString stringWithCString:memo
                                  length:*s - memo];
    if( quoted)
    {
        ++*s;
        --*len;
    }

// brute force, try NSCalendarDate first    
// then NSDecimalNumber Number 
// finally stay as NSString
#if TRY_VALIANTLY_TO_CONVERT
    if( isdigit( *memo))
    {
        if( value = [NSDecimalNumber decimalNumberWithString:string])
            return( value);
    }
    
    if( value = [NSCalendarDate dateWithString:string])
        return( value);
#endif
        
    return( string);
}


static int   _parse_key_value_into_dictionary( char **s, unsigned int *len, NSMutableDictionary *dictionary)
{
    id   key;
    id   value;
    
    
    key = _parse_value( s, len);
    if( ! key)
        return( -1);
    
    if( skip_white( s, len))
    {
       NSLog( @"Malformed key value pair, only key %@ present", key);
       return( -2);
    }

    if( **s != '=')
    {
       NSLog( @"Malformed key value pair, = missing after key %@", key);
       return( -3);
    }
    
    ++*s;
    --*len;
    
    if( skip_white( s, len))
    {
       NSLog( @"Malformed key value pair, missing value after key %@", key);
       return( -4);
    }
   
    value = _parse_object( s, len);
    if( ! value)
    {
       NSLog( @"Malformed key value pair, missing value after key %@", key);
       return( -4);
    }
    [dictionary setObject:value
                   forKey:key];
                   
    return( 0);
}


static id   _parse_array( char **s, unsigned int *len)
{
    NSMutableArray   *array;
    id               parsed;
    
    array = [NSMutableArray array];
    while( *len)
    {
        if( skip_white( s, len))
            return( nil);
    
        if( **s == ',')
        {
           ++*s;
           --*len;
           continue;
        }

        if( **s == ')')
        {
           ++*s;
           --*len;
           return( array);
        }

        parsed = _parse_object( s, len);
        if( ! parsed)
            break;
        [array addObject:parsed];
    }
    NSLog( @"Array truncated");
    return( nil);
}


static id   _parse_dictionary( char **s, unsigned int *len)
{
    NSMutableDictionary   *dictionary;
    
    dictionary = [NSMutableDictionary dictionary];
    while( *len)
    {
        if( skip_white( s, len))
            break;
                
        if( **s == ';')
        {
           ++*s;
           --*len;
           continue;
        }
        
        if( **s == '}')
        {
           ++*s;
           --*len;
           return( dictionary);
        }
        
        if( _parse_key_value_into_dictionary( s, len, dictionary) < 0)
            return( nil);
    }
    NSLog( @"Dictionary truncated");
    return( nil);
}


static id  _parse_object( char **s, unsigned int *len)
{
    if( skip_white( s, len))
        return( nil);
        
    switch( **s)
    {
    case '{' :
        ++*s;
        --*len;
        return( _parse_dictionary( s, len));

    case '(' :
        ++*s;
        --*len;
        return( _parse_array( s, len));
    }
    return( _parse_value( s, len));
}


static id  parse_old_plist( NSData *data)
{
   char         *s;
   NSUInteger   len;
   
   s   = (char *) [data bytes];
   len = [data length];
   return( _parse_object( &s, &len));
}


+ (id) mullePropertyListFromData:(NSData *) data
                mutabilityOption:(NSPropertyListMutabilityOptions) option 
                          format:(NSPropertyListFormat *) format
                errorDescription:(NSString **) error
{
   id   list;
   
   list = [NSPropertyListSerialization propertyListFromData:data
                                           mutabilityOption:option 
                                                     format:format 
                                           errorDescription:error];
   if( ! list)
   {
      // can only do immutable containers
      if( option == NSPropertyListImmutable)
      {
         list = parse_old_plist( data);
         if( list)
            *format = NSPropertyListOpenStepFormat;
      }
      // fall through, if this is nil then Foundation
      // will provide the error 
   }
   return( list);
}

@end