/*
  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 "NGMimePartGenerator.h"
#include "NGMimeHeaderFieldGenerator.h"
#include "NGMimeBodyGenerator.h"
#include "NGMimeJoinedData.h"
#include <NGMime/NGMimeType.h>
#include "common.h"

@implementation NGMimePartGenerator

+ (int)version {
  return 2;
}

+ (id)mimePartGenerator {
  return [[[self alloc] init] autorelease];
}

- (void)dealloc {
  [self->result release];
  [self->part   release];
  [super dealloc];
}

/* setting the delegate */

- (void)setDelegate:(id)_delegate {
  self->delegate = _delegate;

  self->delegateRespondsTo.generatorGenerateDataForHeaderField =
    [self->delegate respondsToSelector:
         @selector(mimePartGenerator:generateDataForHeaderField:value:)];
  
  self->delegateRespondsTo.generatorGeneratorForBodyOfPart =
    [self->delegate respondsToSelector:
         @selector(mimePartGenerator:generatorForBodyOfPart:)];
  
  self->delegateRespondsTo.generatorGenerateDataForBodyOfPart =
    [self->delegate respondsToSelector:
         @selector(mimePartGenerator:generateDataForBodyOfPart:
		   additionalHeaders:)];
}

- (id)delegate {
  return self->delegate;
}

- (BOOL)prepareForGenerationOfPart:(id<NGMimePart>)_part {
  {
    id tmp = self->part;

    self->part = _part;
    
    [self->part retain];
    [tmp release]; tmp = nil;
  }
  if (self->result) {
    [self->result release];
    self->result = nil;
  }
  self->result = (self->useMimeData)
    ? [[NGMimeJoinedData alloc] init]
    : [[NSMutableData alloc] initWithCapacity:4096];
    
  
  if ([self->result respondsToSelector:@selector(methodForSelector:)])
    self->appendBytes = (void(*)(id,SEL,const void *, unsigned))
                        [self->result methodForSelector:
                                          @selector(appendBytes:length:)];
  else
    self->appendBytes = NULL;
  return YES;
}

- (BOOL)generatePrefix {
  return YES;
}

- (void)generateSuffix {
}

- (id<NGMimeHeaderFieldGenerator>)generatorForHeaderField:(NSString *)_name {
  return [NGMimeHeaderFieldGeneratorSet defaultRfc822HeaderFieldGeneratorSet];
}

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

  if (self->delegateRespondsTo.generatorGenerateDataForHeaderField)
    data = [self->delegate mimePartGenerator:self
                           generateDataForHeaderField:_headerField
                           value:_value];
  else {
    data = [[self generatorForHeaderField:_headerField]
                  generateDataForHeaderFieldNamed:_headerField
                  value:_value];
  }
  return data;
}

- (NSData *)generateDataWithHeaderField:(NSString *)_headerField
  values:(NSEnumerator *)_values
{
  NSMutableData *res   = nil;
  NSData        *data  = nil;
  id            value  = nil;
  const char    *bytes = NULL;
  unsigned      len    = 0;

  res = [NSMutableData dataWithCapacity:64];
  bytes = [_headerField cString];
  len   = [_headerField length];
  while (len > 0) {
    if (*bytes != ' ')
      break;
    bytes++;
    len--;
  }
  while ((value = [_values nextObject])) {
    data = [self generateDataForHeaderField:(NSString *)_headerField
                 value:value];
    [res appendBytes:bytes length:len];
    [res appendBytes:": " length:2];
    [res appendData:data];
    [res appendBytes:"\r\n" length:2];
  }
  return res;
}


- (NSData *)generateHeaderData:(NGHashMap *)_additionalHeaders {
  NSEnumerator     *headerFieldNames = nil;
  NSString         *headerFieldName  = nil;
  NGMutableHashMap *addHeaders       = nil;
  NSMutableData    *data             = nil;

  data = (self->useMimeData)
    ? [[[NGMimeJoinedData alloc] init] autorelease]
    : [NSMutableData dataWithCapacity:2048];
  
  headerFieldNames = [self->part headerFieldNames];
  addHeaders       = [_additionalHeaders mutableCopy];

  while ((headerFieldName = [headerFieldNames nextObject])) {
    NSData       *headerFieldData = nil;
    NSEnumerator *enumerator      = nil;
    BOOL         reset;
      
    if ([[_additionalHeaders objectsForKey:headerFieldName] count] > 0) {
      enumerator = [addHeaders objectEnumeratorForKey:headerFieldName];
      reset = YES;
    }
    else {
      reset = NO;
      enumerator = [self->part valuesOfHeaderFieldWithName:headerFieldName];
    }
    headerFieldData = [self generateDataWithHeaderField:headerFieldName
                            values:enumerator];
    if (reset)
      [addHeaders removeAllObjectsForKey:headerFieldName];
    
    if (headerFieldData)
      [data appendData:headerFieldData];
  }
  headerFieldNames = [addHeaders keyEnumerator];
  while ((headerFieldName = [headerFieldNames nextObject])) {
    NSData *headerFieldData = nil;
    headerFieldData = [self generateDataWithHeaderField:headerFieldName
                            values:[addHeaders objectEnumeratorForKey:
                                              headerFieldName]];
    if (headerFieldData)
      [data appendData:headerFieldData];
  }
  [addHeaders release]; addHeaders = nil;
  return data;
}

- (NGMimeType *)defaultContentTypeForPart:(id<NGMimePart>)_part {
  return [NGMimeType mimeType:@"application/octet"];
}

- (id<NGMimeBodyGenerator>)defaultBodyGenerator {
  id<NGMimeBodyGenerator> gen;

  gen = [[[NGMimeBodyGenerator alloc] init] autorelease];
  [(id)gen setUseMimeData:self->useMimeData];
  return gen;
}

- (id<NGMimeBodyGenerator>)generatorForBodyOfPart:(id<NGMimePart>)_part {
  id<NGMimeBodyGenerator> bodyGen      = nil;
  NGMimeType              *contentType = nil;
  NSString                *type        = nil;
  
  if (self->delegateRespondsTo.generatorGeneratorForBodyOfPart)
    bodyGen = [self->delegate mimePartGenerator:self
                   generatorForBodyOfPart:self->part];

  if (!bodyGen) {
    contentType = [_part contentType];
    if (contentType == nil) {
      contentType = [self defaultContentTypeForPart:_part];
    }
    if (contentType == nil) {
      NSLog(@"WARNING(%s): no content-type", __PRETTY_FUNCTION__);
      return nil;
    }
    type = [contentType type];
  
    if ([type isEqualToString:NGMimeTypeMultipart]) {
      bodyGen = [[[NGMimeMultipartBodyGenerator alloc] init] autorelease];
    }
    else if ([type isEqualToString:NGMimeTypeText]) {
      bodyGen = [[[NGMimeTextBodyGenerator alloc] init] autorelease];
    }
    else if (([type isEqualToString:NGMimeTypeMessage]) &&
             [[contentType subType] isEqualToString:@"rfc822"]) {
      bodyGen = [[[NGMimeRfc822BodyGenerator alloc] init] autorelease];
    }
  }
  [(id)bodyGen setUseMimeData:self->useMimeData];
  return bodyGen;
}

- (NSData *)generateBodyData:(NGMutableHashMap *)_additionalHeaders {
  NSData                  *data   = nil;
  id<NGMimeBodyGenerator> bodyGen = nil;

  if (self->delegateRespondsTo.generatorGenerateDataForBodyOfPart) {
    data = [self->delegate mimePartGenerator:self
                           generateDataForBodyOfPart:self->part
                           additionalHeaders:_additionalHeaders];
  }
  else {
    bodyGen = [self generatorForBodyOfPart:self->part];

    if (bodyGen == nil) // no generator for body
      bodyGen = [self defaultBodyGenerator];

    [(id)bodyGen setUseMimeData:self->useMimeData];
    
    if (bodyGen == nil) {
      id body = [self->part body];        
      NSLog(@"WARNING(%s): no defaultBodyGenerator", __PRETTY_FUNCTION__);
      if ([body isKindOfClass:[NSData class]]) {
        data = body;
      }
      else if ([body isKindOfClass:[NSString class]]) {
        data = [body dataUsingEncoding:[NSString defaultCStringEncoding]];
      }
    }
    else {
      data = [bodyGen generateBodyOfPart:self->part
                      additionalHeaders:_additionalHeaders
                      delegate:self->delegate];
    }
  }
  return data;
}

- (void)generateData {
  NGMutableHashMap *additionalHeaders = nil;
  NSData           *bodyData          = nil;
  NSData           *headerData        = nil;
  
  additionalHeaders = [[NGMutableHashMap alloc] initWithCapacity:16];
  
  bodyData   = [self generateBodyData:additionalHeaders];
  headerData = [self generateHeaderData:additionalHeaders];
  
  if (headerData != nil) {
    [self->result appendData:headerData];
    [self->result appendBytes:"\r\n" length:2];
    if (bodyData != nil)
      [self->result appendData:bodyData];
  }
  [additionalHeaders release];
}

- (NSData *)generateMimeFromPart:(id<NGMimePart>)_part {
  [self prepareForGenerationOfPart:_part];
  if ([self generatePrefix]) {
    NSData *data;
    
    [self generateData];
    [self generateSuffix];
    data = self->result;
    self->result = nil;
    return [data autorelease];
  }
  return nil;
}

- (NSString *)generateMimeFromPartToFile:(id<NGMimePart>)_part {
  NSString *filename = nil;

  static NSString      *TmpPath = nil;
  static NSProcessInfo *Pi      = nil;
  
  if (TmpPath == nil) {
    NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
    
    TmpPath = [ud stringForKey:@"NGMimeBuildMimeTempDirectory"];
    if (TmpPath == nil)
      TmpPath = @"/tmp/";
    TmpPath = [[TmpPath stringByAppendingPathComponent:@"OGo"] copy];
  }
  if (Pi == nil)
    Pi = [[NSProcessInfo processInfo] retain];

  filename = [Pi temporaryFileName:TmpPath];

  [self setUseMimeData:YES];

  if (![[self generateMimeFromPart:_part]
              writeToFile:filename atomically:YES]) {
    NSLog(@"ERROR[%s] couldn`t write data to temorary file %@",
          __PRETTY_FUNCTION__, filename);
    return nil;
  }
  return filename;
}

- (id<NGMimePart>)part {
  return self->part;
}

- (BOOL)useMimeData {
  return self->useMimeData;
}

- (void)setUseMimeData:(BOOL)_b {
  self->useMimeData = _b;
}

@end /* NGMimePartGenerator */
  
