//
//  GSNSDataExtensions.m
//  Genetic Crossing
//
//  Created by khammond on Mon Oct 29 2001.
//  Copyright (c) 2001 Kyle Hammond. All rights reserved.
//
// Original development comments by Dave Winer.
/*	C source code for Base 64 

       Here's the C source code for the Base 64 encoder/decoder.

        File:
                     base64.c
        Created:
                     Saturday, April 5, 1997; 1:30:13 PM
        Modified: 
                     Tuesday, April 8, 1997; 7:52:28 AM


       /*
       Dave Winer, dwiner@well.com, UserLand Software, 4/7/97
        
       I built this project using Symantec C++ 7.0.4 on a Mac 9500.
        
       We needed a handle-based Base 64 encoder/decoder. Looked around the
       net, found a bunch of code that couldn't easily be adapted to 
       in-memory stuff. Most of them work on files to conserve memory. This
       is inelegant in scripting environments such as Frontier.
        
       Anyway, so I wrote an encoder/decoder. Docs are being maintained 
       on the web, and updates at:
        
       http://www.scripting.com/midas/base64/
        
       If you port this code to another platform please put the result up
       on a website, and send me a pointer. Also send email if you think this
       isn't a compatible implementation of Base 64 encoding.
        
       BTW, I made it easy to port -- layering out the handle access routines.
       Of course there's a small performance penalty for this, and if you don't
       like it, change it. Thanks!
       */

#import "GSNSDataExtensions.h"

@implementation NSData (Base64Encoding)

static char gEncodingTable[ 64 ] = {
           'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
           'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
           'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
           'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'
            };

+ (NSData *)dataWithBase64EncodedString:(NSString *)inBase64String
{
    NSData	*result = nil;

    result = [ [ NSData alloc ] initWithBase64EncodedString:inBase64String ];

    return [ result autorelease ];
}

- (id)initWithBase64EncodedString:(NSString *)inBase64String
{
    NSMutableData	*mutableData = nil;

    if ( inBase64String )
    {
        unsigned long		ixtext;
        unsigned long		lentext;
        unsigned char		ch;
		unsigned char		inbuf [4], outbuf [3];

        short				i, ixinbuf;
        BOOL				flignore;
        BOOL				flendtext = NO;
        NSData				*base64Data;
        const unsigned char	*base64Bytes;

        // Convert the string to ASCII data.
        base64Data = [ inBase64String dataUsingEncoding:NSASCIIStringEncoding ];
				
        base64Bytes = [ base64Data bytes ];

        mutableData = [ NSMutableData dataWithCapacity:[ base64Data length ] ];

        ixtext = 0;

        lentext = [ base64Data length ];

        ixinbuf = 0;

        while (YES)
        {
            if (ixtext >= lentext)
                break;

            ch = base64Bytes[ ixtext++ ]; // gethandlechar (h64, ixtext++);
               
            flignore = NO;
               
            if ((ch >= 'A') && (ch <= 'Z'))
                ch = ch - 'A';
               
            else if ((ch >= 'a') && (ch <= 'z'))
                ch = ch - 'a' + 26;
                       
            else if ((ch >= '0') && (ch <= '9'))
                ch = ch - '0' + 52;
               
            else if (ch == '+')
                ch = 62;
                       
            else if (ch == '=') /*no op -- can't ignore this one*/
                flendtext = YES;
                       
            else if (ch == '/')
                ch = 63;
               
            else
                flignore = YES; 
               
            if (!flignore)
            {
                short	ctcharsinbuf = 3;
                BOOL	flbreak = NO;
                                
                if (flendtext)
                {
                    if (ixinbuf == 0)
                        break;
                                       
                    if ((ixinbuf == 1) || (ixinbuf == 2))
                        ctcharsinbuf = 1;
                    else
                        ctcharsinbuf = 2;
                        
                    ixinbuf = 3;
                               
                    flbreak = YES;
                }
                       
                inbuf [ixinbuf++] = ch;
                       
                if (ixinbuf == 4)
                {
                    ixinbuf = 0;
            
                    outbuf [0] = (inbuf [0] << 2) | ((inbuf [1] & 0x30) >> 4);
                               
                    outbuf [1] = ((inbuf [1] & 0x0F) << 4) | ((inbuf [2] & 0x3C) >> 2);
                               
                    outbuf [2] = ((inbuf [2] & 0x03) << 6) | (inbuf [3] & 0x3F);
               
                    for (i = 0; i < ctcharsinbuf; i++) 
                        [ mutableData appendBytes:&outbuf[ i ] length:1 ];
                }

                if (flbreak)
                    break;
            }
        } /*while*/
    }

    self = [ self initWithData:mutableData ];

    return self;
}

- (NSString *)base64EncodingWithLineLength:(unsigned int)inLineLength
{   /*
        Encode the NSData. Some funny stuff about linelength -- it only makes
        sense to make it a multiple of 4. If it's not a multiple of 4, we make it
        so (by only checking it every 4 characters). 

        Further, if it's 0, we don't add any line breaks at all.
    */
        
    const unsigned char	*bytes = [ self bytes ];
    NSMutableString		*result = nil;
    unsigned long		ixtext;
    unsigned long		lentext;
    long				ctremaining;
    unsigned char		inbuf [3], outbuf [4];
    short				i;
    short				charsonline = 0, ctcopy;
    unsigned long		ix;

    ixtext = 0;
        
    lentext = [ self length ];
    result = [ NSMutableString stringWithCapacity:lentext ];

    while ( YES )
    {
        ctremaining = lentext - ixtext;
               
        if (ctremaining <= 0)
            break;
                                       
        for (i = 0; i < 3; i++)
        {
            ix = ixtext + i;

            if (ix < lentext)
                inbuf [i] = bytes[ix];
            else
                inbuf [i] = 0;
        } /*for*/

        outbuf [0] = (inbuf [0] & 0xFC) >> 2;
           
        outbuf [1] = ((inbuf [0] & 0x03) << 4) | ((inbuf [1] & 0xF0) >> 4);
           
        outbuf [2] = ((inbuf [1] & 0x0F) << 2) | ((inbuf [2] & 0xC0) >> 6);
               
        outbuf [3] = inbuf [2] & 0x3F;

        ctcopy = 4;

        switch (ctremaining)
        {
            case 1: 
                ctcopy = 2; 

                break;
                       
            case 2: 
                ctcopy = 3; 
                               
                break;
        } /*switch*/
        
        for (i = 0; i < ctcopy; i++)
            [ result appendFormat:@"%c", gEncodingTable[outbuf [i]] ];

        for (i = ctcopy; i < 4; i++)
            [ result appendFormat:@"%c", '=' ];

        ixtext += 3;
               
        charsonline += 4;

        if (inLineLength > 0)
        { /*DW 4/8/97 -- 0 means no line breaks*/
            if (charsonline >= inLineLength)
            {
                charsonline = 0;

                [ result appendString:@"\n" ];
            }
        }
    } /*while*/
        
	return result;
}

@end
