/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
/*
 * email-parser.c
 *
 *  Created on: 04.08.2010
 *      Author: Umer Kayani <u.kayani@tarent.de>,
 *      		Andreas Grau <a.grau@tarent.de>
 */

/*
 * This program 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.1 of the License, or (at your option)
 * any later version.
 *
 * This program 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 this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor Boston, MA 02110-1301, USA
 */

/* FIXME remove the use of fileno() */
#define _POSIX_C_SOURCE 1
#define _XOPEN_SOURCE
#define _POSIX_SOURCE

#include <stdio.h>
#include <unistd.h>
#include <logging.h>
#include "email-parser.h"
#include "../../main/src/util.h"


/* ########################################INTERNAL METHODS######################################## */


typedef struct {
	GList *g_mime_objects; /* GMimeObject *body; */
} g_mime_objects_data;


static void
write_email_to_disk (GMimeMessage *message, const char *folder_name)
{
	GMimeStream *stream;

	FILE *fp = NULL;
	char email_file_name[50];

	strcpy (email_file_name, folder_name);
	/* strcat(emailFileName, "generatedEmail.txt"); */

	fp = fopen (email_file_name, "w");

	stream = g_mime_stream_fs_new (dup (fileno (fp)));

	/* write the message to the stream */
	g_mime_object_write_to_stream ((GMimeObject *) message, stream);

	/* flush the stream (kinda like fflush() in libc's stdio) */
	g_mime_stream_flush (stream);

	/* free the output stream */
	g_object_unref (stream);

	fclose(fp);
}

static void
find_email_attachments (GMimeObject *parent, GMimeObject *part, gpointer user_data)
{
	g_mime_objects_data *data = user_data;
	(void)parent;

	/* if (( parent->content_type->content type is text/plain or text/html) and	(it's not an attachment)) */
	data->g_mime_objects = g_list_append (data->g_mime_objects, (GMimeObject *) part);
}

static gchar *
decode_base64_image_string (gchar *str, ssize_t len, gint *image_length)
{
	gchar *image_data = g_new0(gchar, len + 1);
	gint state = 0;
	guint32 save;

	/* base64 decode the input string */
	*image_length = g_mime_encoding_base64_decode_step ((guchar *) str, len,
	                                                    (guchar *) image_data, &state, &save);

	return image_data;
}

static void
remove_extra_chars_after_xml_end_tag(gchar *xml_data, gchar *end_tag)
{
	gchar *ptr_to_last_tag = strstr(xml_data, end_tag); /* set pointer to first char of end tag */
	gint index_to_start_of_last_tag;

	/* if tag not found, it's the wrong tag and do nothing */
	if (ptr_to_last_tag != NULL) {
		index_to_start_of_last_tag = ptr_to_last_tag-xml_data;
		/* log_debug("\na= %d, %s", indexToStartOfLastTag, ptrToLastTag); */
		xml_data[index_to_start_of_last_tag+strlen(end_tag)] = '\0';
		/* log_debug("\na= %d, %s", indexToStartOfLastTag, ptrToLastTag); */
	}
}

static gchar
*quoted_printable_to_string(gchar *str, gint *text_length)
{
	gint len = strlen(str);
	gchar *text_data = g_new0(gchar, len);
	gint state = 0;
	guint32 save;

	/* quoted printable decode the input string */
	*text_length = g_mime_encoding_quoted_decode_step((guchar*) str, len,
	                                                  (guchar*) text_data, &state, &save);

	/* case analysis workaround for (contact, task, event and note), function does not remove anything if tag not found */
	remove_extra_chars_after_xml_end_tag(text_data, CONTACT_CLOSING_TAG);
	remove_extra_chars_after_xml_end_tag(text_data, TASK_CLOSING_TAG);
	remove_extra_chars_after_xml_end_tag(text_data, EVENT_CLOSING_TAG);
	remove_extra_chars_after_xml_end_tag(text_data, NOTE_CLOSING_TAG);

	return text_data;
}

#if 0
void
initialize_file_buffer(gchar *file_contents, gint size)
{
	gint i;
	for(i=0; i<size;i++)
		file_contents[i] = '\0';
}
#endif

static gchar*
get_content_from_g_mime_object (GMimeObject *g_mime_obj, gint *image_length)
{
	GMimeStream *g_mime_obj_stream = NULL;
	ssize_t stream_size;
	gchar *file_buf = NULL, *image_buf = NULL;

	g_mime_obj_stream = ((GMimePart*) g_mime_obj)->content->stream;
	stream_size = g_mime_stream_length (g_mime_obj_stream);
	file_buf = g_new0(gchar, stream_size);
	g_mime_stream_read (g_mime_obj_stream, file_buf, stream_size);

	if (((GMimePart*) g_mime_obj)->content->encoding == GMIME_CONTENT_ENCODING_BASE64) {
		image_buf = decode_base64_image_string (file_buf, stream_size, image_length);
		/* write_binary_file("picture.png", outbuf, imageLength); */ /* streamSize); */
		g_free(file_buf);
		return image_buf;
	} else if (((GMimePart*)g_mime_obj)->content->encoding == GMIME_CONTENT_ENCODING_QUOTEDPRINTABLE) {
		image_buf = quoted_printable_to_string(file_buf, image_length);
		/* write_binary_file("picture.png", outbuf, imageLength); */ /* streamSize); */
		g_free(file_buf);
		return image_buf;
	} else {
		*image_length = stream_size;
		return file_buf;
	}
}

static void
construct_g_mime_message (GMimeMessage *message)
{
	g_mime_message_set_sender (message, "\"Hans Mustermann\" <h.mustermann@tarent.de>");
	g_mime_message_set_reply_to (message, "test@test.com");
	g_mime_message_add_recipient (message, GMIME_RECIPIENT_TYPE_TO, "John E. Doe", "j.doe@tarent.de");
	g_mime_message_set_subject (message, "uUzgnRqsSd");
}

static void
add_klb_mail_list_to_g_mime_message (GMimeMessage *message, const GList* klb_mail_list)
{
	GMimeMultipart *multipart;
	GMimeDataWrapper *content;
	GMimePart *mime_part;
	GMimeStream *stream;
	const GList *iterator = klb_mail_list;

	if (klb_mail_list) {
		/* create a multipart/mixed part */
		multipart = g_mime_multipart_new_with_subtype ("mixed");

		while (iterator != NULL) {

			Kolab_conv_mail_part *klb_mail_part = ((Kolab_conv_mail_part*) iterator->data);

			gchar **tokens = g_strsplit (klb_mail_part->mime_type, "/", -1);

			mime_part = g_mime_part_new_with_type (tokens[0], tokens[1]);

			stream = g_mime_stream_mem_new_with_buffer (klb_mail_part->data,
			                                            klb_mail_part->length);

			content = g_mime_data_wrapper_new_with_stream (stream, GMIME_CONTENT_ENCODING_DEFAULT);
			g_object_unref (stream);

			/* Add base 64 encoding for image types as we want to write image data in base64 format */
			/* if (strcmp (kolabconvMailPart->mime_type, "image/png") == 0) { */
			g_mime_part_set_content_encoding (mime_part, GMIME_CONTENT_ENCODING_BASE64);
			/* } */

			/* set the content object on the new mime part */
			g_mime_part_set_content_object (mime_part, content);
			g_object_unref (content);

			if (klb_mail_part->name)
				g_mime_part_set_filename (mime_part, klb_mail_part->name);

			/* add our new text part to it */
			g_mime_multipart_add (multipart, (GMimeObject *) mime_part);
			g_object_unref (mime_part);

			iterator = iterator->next;

			g_strfreev (tokens);
			/* TODO: put following 3 frees in one function in kolab-conv.c and call this one also from kolabconv_free_kmail(...) */
			g_free(klb_mail_part->name);
			g_free(klb_mail_part->mime_type);
			g_free(klb_mail_part->data);
			g_free(klb_mail_part);
		}

		/* now append the message's toplevel part to our multipart */
		/* g_mime_multipart_add (multipart, message->mime_part); */

		/* now replace the message's toplevel mime part with our new multipart */
		g_mime_message_set_mime_part (message, (GMimeObject *) multipart);
		g_object_unref (multipart);
	}

}

/* ########################################PUBLIC INTERFACE######################################## */

#if 0
void
g_mime_read_email (gchar *email_file_name)
{

	GMimeStream *main_email_stream = NULL;
	GMimeParser *mime_parser = NULL;
	GMimeMessage *mime_message = NULL;
	/* ssize_t streamSize; */
	g_mime_objects_data g_mime_obj_data;
	GList *iterator = NULL;
	GMimeObject *g_mime_obj;
	gint *img_len = NULL;
	FILE *fp = NULL;
	gchar *file_buf = NULL;
	gint i = 0;

	fp = fopen (email_file_name, "r");

	/* initialize GMime */
	g_mime_init (0);

	/* Associate GMimeStream tp the file object */
	/* stream = g_mime_stream_file_new ( fp ); */
	main_email_stream = g_mime_stream_fs_new (dup (fileno (fp)));

	/* Assign a mimeParser to the stream to read it eaisly. */
	mime_parser = g_mime_parser_new_with_stream (main_email_stream);

	printf ("\n*****************STEP 1: Get Main Message**********************************\n");
	/* Use mimeParser to get the message part of mimeStream and print a sample e.g. From: */
	mime_message = g_mime_parser_construct_message (mime_parser);
	printf ("\nMime Message: Email sent From = %s", mime_message->from);
	printf ("\nMime Message: Subject = %s", mime_message->subject);
	printf ("\nMime Message: Reply To = %s", mime_message->reply_to);
	printf ("\nMime Message: tz offset = %d", mime_message->tz_offset);

	printf ("\n\n\n*****************STEP 2: Get all Parts of Message (Attachments)**********************************\n");
	/* Using the constructed mimeMessge get all attachments */
	g_mime_obj_data.g_mime_objects = NULL;
	g_mime_message_foreach (mime_message, (GMimeObjectForeachFunc) find_email_attachments,
	                        &g_mime_obj_data);

	iterator = g_mime_obj_data.g_mime_objects;

	printf ("\n\n\n*****************STEP 3: Loop through all Attachments**********************************\n");
	while (iterator != NULL) {
		/* Get the first GMimeObject */
		g_mime_obj = ((GMimeObject *) iterator->data);

		printf ("\nAttachment #%d", (i + 1));
		printf ("\n\tContent type: \n%s", g_mime_obj->content_type->type);
		printf ("\nHeaders: \n%s", g_mime_header_list_to_string (g_mime_obj->headers));

		if (g_mime_obj->disposition) {
			printf ("\nContent Disposition: %s", g_mime_content_disposition_to_string (
				     g_mime_obj->disposition, 2));

		}

		if (strcmp (g_mime_obj->content_type->type, "application") == 0) {

			file_buf = get_content_from_g_mime_object (g_mime_obj, img_len);

			printf ("\n++++Content: %s", file_buf);

			g_free (file_buf);
		} else if (strcmp (g_mime_obj->content_type->type, "image") == 0) {

			file_buf = get_content_from_g_mime_object (g_mime_obj, img_len);

			printf ("\n++++Image: %s", file_buf);

			g_free (file_buf);
		} else {
			printf ("\nGMimeObj type---->%s\n", g_mime_obj->content_type->type);

		}

		iterator = iterator->next;
		i++;
	}

	/* free/close the stream */
	g_object_unref (main_email_stream);
	fclose (fp);

}
#endif

Kolab_conv_mail*
read_kolab_email_file(const gchar *filename, GError** error) {
	GList *klb_mail_list = NULL, *iterator = NULL;
	Kolab_conv_mail_part *klb_mail_part = NULL;
	GMimeStream *main_email_stream = NULL;
	GMimeParser *mime_parser = NULL;
	GMimeMessage *mime_message = NULL;
	/* ssize_t streamSize; */
	g_mime_objects_data g_mime_obj_data;
	GMimeObject *g_mime_obj;
	FILE *fp = NULL;
	gchar contentType[50], *file_buf = NULL;
	gint i = 0, file_len = 0;

	(void)error;

	fp = fopen (filename, "r");

	/* initialize GMime */
	g_mime_init (0);

	/* Associate GMimeStream tp the file object */
	main_email_stream = g_mime_stream_fs_new (dup (fileno (fp)));

	/* Assign a mimeParser to the stream to read it eaisly. */
	mime_parser = g_mime_parser_new_with_stream (main_email_stream);

	/* Use mimeParser to get the message part of mimeStream and print a sample e.g. From: */
	mime_message = g_mime_parser_construct_message (mime_parser);

	/* Using the constructed mimeMessge get all attachments */
	g_mime_obj_data.g_mime_objects = NULL;
	g_mime_message_foreach (mime_message, find_email_attachments, &g_mime_obj_data);

	iterator = g_mime_obj_data.g_mime_objects;

	while (iterator != NULL) {
		gchar *type = NULL;
		/* Get the first GMimeObject */
		g_mime_obj = ((GMimeObject *) iterator->data);
		type = g_mime_obj->content_type->type;
		/* printf("\na %s, %s", gMimeObj->content_type->type, gMimeObj->content_type->subtype); */
		if (  strcmp (type, "Multipart") != 0 && strcmp (type, "multipart") != 0 &&
		      (strcmp (type, "Text") != 0) && (strcmp (type, "text") != 0) ){
			gchar *fileName = NULL;
			/* || strcmp (gMimeObj->content_type->type, "image") == 0) { */

			/* printf("\na %s, %s", gMimeObj->content_type->type, gMimeObj->content_type->subtype); */

			file_buf = get_content_from_g_mime_object (g_mime_obj, &file_len);

			if( file_buf ){
				klb_mail_part = g_new0(Kolab_conv_mail_part , 1);
				strcpy (contentType, g_mime_obj->content_type->type);
				strcat (contentType, "/");
				strcat (contentType, g_mime_obj->content_type->subtype);

				if(fileName)
					klb_mail_part->name = g_strdup (fileName);
				klb_mail_part->name = g_strdup (g_mime_part_get_filename ((GMimePart *)g_mime_obj));
				klb_mail_part->mime_type = g_strdup (contentType);

				klb_mail_part->data = g_new0(gchar, file_len);
				memcpy (klb_mail_part->data, file_buf, file_len);
				klb_mail_part->length = file_len;

				klb_mail_list = g_list_append (klb_mail_list, (Kolab_conv_mail_part *) klb_mail_part);
				g_free (file_buf);
			}

		}

		iterator = iterator->next;
		i++;
	}

	/* free/close the stream */
	g_object_unref (main_email_stream);
	g_object_unref (mime_parser);
	g_object_unref (mime_message);
	fclose (fp);

	return g_list_to_kolab_conv_mail (klb_mail_list); /* kolabconvMailList; */
}

void
write_kolab_email_file (const Kolab_conv_mail *mail, const gchar *filename, GError** error)
{
	GMimeMessage *message = NULL;
	GList *klb_mail_list = NULL;

	(void)error;

	g_mime_init (0);

	message = g_mime_message_new (TRUE);
	construct_g_mime_message (message);

	/* Add all attachments from the mail */
	klb_mail_list = klb_conv_mail_to_g_list(mail);
	add_klb_mail_list_to_g_mime_message (message, klb_mail_list);

	/* write_message_to_screen (message); */
	write_email_to_disk (message, filename);

	g_object_unref (message);
}
