/*
  Copyright (C) 2000-2005 SKYRIX Software AG

  This file is part of SOPE.

  SOPE 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, or (at your option) any
  later version.

  SOPE 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 SOPE; see the file COPYING.  If not, write to the
  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
  02111-1307, USA.
*/

#include "NGMimeMessageGenerator.h"
#include "NGMimeMessage.h"
#include <NGMime/NGMimeFileData.h>
#include "common.h"

/* 
   Defaults
     Mail_Use_8bit_Encoding_For_Text[BOOL] --
       Use 8bit content-transfer-encoding for
       text messages
*/

NSData *
_base64Encoding(NGMimeBodyGenerator *self,
                NSData *_data_,
                id<NGMimePart>_part,
                NGMutableHashMap *_addHeaders)
{
  NSString   *transEnc = nil;  
  const char *bytes    = NULL;
  unsigned   length    = 0;

  if ([_data_ isKindOfClass:[NGMimeFileData class]])
    return _data_;
  
  bytes  = [_data_ bytes];
  length = [_data_ length];

  while (length > 0) {
    if ((unsigned char)*bytes > 127) {
      break;
    }
    bytes++;
    length--;
  }
  if (length > 0) { // should be encoded
    NGMimeType *type;

    type = [_part contentType];
    
    if ([[type type] isEqualToString:@"text"]) {
      NSUserDefaults *ud;
      BOOL use8bit;

      ud      = [NSUserDefaults standardUserDefaults];
      use8bit = [ud boolForKey:@"Mail_Use_8bit_Encoding_For_Text"];
      
      if (use8bit)
        transEnc = @"8bit";
      else {
        _data_   = [_data_ dataByEncodingQuotedPrintable];
        transEnc = @"quoted-printable";
      }
    }
    else {
      NGMimeType *appOctet;
      
      _data_   = [_data_ dataByEncodingBase64];
      transEnc = @"base64";
      
      appOctet = [NGMimeType mimeType:@"application" subType:@"octet-stream"];
      if (type == nil)
        [_addHeaders setObject:appOctet forKey:@"content-type"];
    }
  }
  else /* no encoding */
    transEnc = @"7bit";
  
  [_addHeaders setObject:transEnc forKey:@"content-transfer-encoding"];
  [_addHeaders setObject:[NSNumber numberWithInt:[_data_ length]]
                      forKey:@"content-length"];
  return _data_;
}

@implementation NGMimeMessageGenerator

+ (int)version {
  return 2;
}
+ (void)initialize {
  NSAssert2([super version] == 2,
            @"invalid superclass (%@) version %i !",
            NSStringFromClass([self superclass]), [super version]);
}

/* header field specifics */

- (NSData *)generateDataForHeaderField:(NSString *)_headerField
  value:(id)_value
{
  NSData *data = nil;

  data = [super generateDataForHeaderField:_headerField
                value:_value];
  {
    const char   *bytes  = NULL;
    unsigned int length  = 0;
    unsigned int desLen  = 0;
    char         *des    = NULL;
    unsigned int cnt     = 0;
    BOOL         doEnc   = NO;
    NSString     *str;

#if APPLE_Foundation_LIBRARY || NeXT_Foundation_LIBRARY
    str = [[NSString alloc] initWithData:data
                            encoding:NSISOLatin1StringEncoding];
#else
    str = [[NSString alloc] initWithData:data
                            encoding:NSISOLatin9StringEncoding];
#endif
    str = [str autorelease];
    
    bytes  = [str cString];
    length = [str cStringLength];

    while (cnt < length) {
      if ((unsigned char)bytes[cnt] > 127) {
        doEnc = YES;
        break;
      }
      cnt++;
    }
    if (doEnc) {
      char        iso[]     = "=?iso-8859-15?q?";
      unsigned    isoLen    = 16;
      char        isoEnd[]  = "?=";
      unsigned    isoEndLen = 2;
      
      desLen = length * 3 + 20;
      
      des = calloc(desLen + 2, sizeof(char));
      
      // memcpy(des, bytes, cnt);
      memcpy(des, iso, isoLen);
      desLen = NGEncodeQuotedPrintableMime(bytes, length,
                                           des + isoLen, desLen - isoLen);
      if ((int)desLen != -1) {
        memcpy(des + isoLen + desLen, isoEnd, isoEndLen);
        
        data = [NSData dataWithBytesNoCopy:des
		       length:(isoLen + desLen + isoEndLen)];
      }
      else {
        NSLog(@"WARNING: An error occour during quoted-printable decoding");
        if (des != NULL) free(des);
      }
    }
  }
  return data;
}

- (BOOL)isMultiValueCommaHeaderField:(NSString *)_headerField {
  // currently checks for: to, cc, bcc
  unsigned len;
  unichar  c0, c1;

  if ((len = [_headerField length]) < 2)
    return 0;
  c0 = [_headerField characterAtIndex:0];
  c1 = [_headerField characterAtIndex:1];
  
  switch (len) {
  case 2:
    if ((c0 == 't' || c0 == 'T') && ((c1 == 'o' || c1 == 'O')))
      return YES;
    if ((c0 == 'c' || c0 == 'C') && ((c1 == 'c' || c1 == 'C')))
      return YES;
    break;
  case 3:
    if ((c0 == 'b' || c0 == 'B') && ((c1 == 'c' || c1 == 'C'))) {
      c0 = [_headerField characterAtIndex:2];
      if (c0 == 'c' || c0 == 'C')
	return YES;
    }
    break;
  }
  return NO;
}

- (NSData *)generateDataWithHeaderField:(NSString *)_headerField
  values:(NSEnumerator *)_values
{
  NSMutableData *res   = nil;
  NSData        *data  = nil;
  id            value  = nil;
  const char    *bytes = NULL;
  unsigned      len    = 0;
  
  if (![self isMultiValueCommaHeaderField:_headerField])
    return [super generateDataWithHeaderField:_headerField values:_values];

  /* generate values separated by "," */
  
  bytes = [_headerField cString];
  len   = [_headerField length];
  while (len > 0) { // skip leading spaces
    if (*bytes != ' ')
      break;
    bytes++;
    len--;
  }
  
  res = nil;
  while ((value = [_values nextObject]) != nil) {
    if (res == nil) {
      res = [NSMutableData dataWithCapacity:128];
      [res appendBytes:bytes length:len];
      [res appendBytes:": " length:2];
    }
    else
      [res appendBytes:", " length:2];
    
    data = [self generateDataForHeaderField:_headerField
                 value:value];
    [res appendData:data];
  }
  if (res != nil) [res appendBytes:"\r\n" length:2];
  return res;
}

/* content-transfer-encoding */

- (id<NGMimeBodyGenerator>)defaultBodyGenerator {
  NGMimeMessageBodyGenerator *gen;
  
  gen  = [[NGMimeMessageBodyGenerator alloc] init];
  [gen setUseMimeData:self->useMimeData];
  return gen;
}

- (id<NGMimeBodyGenerator>)generatorForBodyOfPart:(id<NGMimePart>)_part {
  id<NGMimeBodyGenerator> bodyGen;
  NGMimeType              *contentType;
  NSString                *type;
  Class generatorClass;
  
  if (self->delegateRespondsTo.generatorGeneratorForBodyOfPart) {
    bodyGen = [self->delegate mimePartGenerator:self
                              generatorForBodyOfPart:self->part];
  
    if (bodyGen != nil)
      return bodyGen;
  }
  
  if ((contentType = [_part contentType]) == nil)
    contentType = [self defaultContentTypeForPart:_part];
  
  if (contentType == nil) {
    NSLog(@"WARNING(%s): missing content-type in part 0x%08X.", _part);
    return nil;
  }
  
  type = [contentType type];

  generatorClass = Nil;
  if ([type isEqualToString:NGMimeTypeMultipart])
    generatorClass = [NGMimeMessageMultipartBodyGenerator class];
  else if ([type isEqualToString:NGMimeTypeText])
    generatorClass = [NGMimeMessageTextBodyGenerator class];
  else if (([type isEqualToString:NGMimeTypeMessage]) &&
           [[contentType subType] isEqualToString:@"rfc822"]) {
    generatorClass = [NGMimeMessageRfc822BodyGenerator class];
  }
  
  bodyGen = [[[generatorClass alloc] init] autorelease];
  [(id)bodyGen setUseMimeData:self->useMimeData];
  return bodyGen;
}

@end /* NGMimeMessageGenerator */
