/*
 *
 * 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 of the License, or (at your option) version 3.
 *
 * 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 the program; if not, see <http://www.gnu.org/licenses/>
 *
 *
 * Authors:
 *		Michael Zucchi <notzed@ximian.com>
 *
 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
 *
 */

/*
  Abstract class for formatting mime messages
*/

#ifndef EM_FORMAT_H
#define EM_FORMAT_H

#include <glib-object.h>
#include <camel/camel-url.h>
#include <camel/camel-folder.h>
#include <camel/camel-stream.h>
#include <camel/camel-session.h>
#include <camel/camel-mime-part.h>
#include <camel/camel-mime-message.h>
#include <camel/camel-cipher-context.h>

/* Standard GObject macros */
#define EM_TYPE_FORMAT \
	(em_format_get_type ())
#define EM_FORMAT(obj) \
	(G_TYPE_CHECK_INSTANCE_CAST \
	((obj), EM_TYPE_FORMAT, EMFormat))
#define EM_FORMAT_CLASS(cls) \
	(G_TYPE_CHECK_CLASS_CAST \
	((cls), EM_TYPE_FORMAT, EMFormatClass))
#define EM_IS_FORMAT(obj) \
	(G_TYPE_CHECK_INSTANCE_TYPE \
	((obj), EM_TYPE_FORMAT))
#define EM_IS_FORMAT_CLASS(cls) \
	(G_TYPE_CHECK_CLASS_TYPE \
	((cls), EM_TYPE_FORMAT))
#define EM_FORMAT_GET_CLASS(obj) \
	(G_TYPE_INSTANCE_GET_CLASS \
	((obj), EM_TYPE_FORMAT, EMFormatClass))

G_BEGIN_DECLS

typedef struct _EMFormat EMFormat;
typedef struct _EMFormatClass EMFormatClass;
typedef struct _EMFormatPrivate EMFormatPrivate;

typedef struct _EMFormatHandler EMFormatHandler;
typedef struct _EMFormatHeader EMFormatHeader;

typedef void (*EMFormatFunc) (EMFormat *md, CamelStream *stream, CamelMimePart *part, const EMFormatHandler *info);

typedef enum _em_format_mode_t {
	EM_FORMAT_NORMAL,
	EM_FORMAT_ALLHEADERS,
	EM_FORMAT_SOURCE
} em_format_mode_t;

/**
 * struct _EMFormatHandler - MIME type handler.
 *
 * @mime_type: Type this handler handles.
 * @handler: The handler callback.
 * @flags: Handling flags, see enum _em_format_handler_t.
 * @old: The last handler set on this type.  Allows overrides to
 * fallback to previous implementation.
 *
 **/
struct _EMFormatHandler {
	gchar *mime_type;
	EMFormatFunc handler;
	guint32 flags;

	EMFormatHandler *old;
};

/**
 * enum _em_format_handler_t - Format handler flags.
 *
 * @EM_FORMAT_HANDLER_INLINE: This type should be shown expanded
 * inline by default.
 * @EM_FORMAT_HANDLER_INLINE_DISPOSITION: This type should always be
 * shown inline, despite what the Content-Disposition suggests.
 *
 **/
enum _em_format_handler_t {
	EM_FORMAT_HANDLER_INLINE = 1<<0,
	EM_FORMAT_HANDLER_INLINE_DISPOSITION = 1<<1
};

typedef struct _EMFormatPURI EMFormatPURI;
typedef void (*EMFormatPURIFunc)(EMFormat *md, CamelStream *stream, EMFormatPURI *puri);

/**
 * struct _EMFormatPURI - Pending URI object.
 *
 * @free: May be set by allocator and will be called when no longer needed.
 * @format:
 * @uri: Calculated URI of the part, if the part has one in its
 * Content-Location field.
 * @cid: The RFC2046 Content-Id of the part.  If none is present, a unique value
 * is calculated from @part_id.
 * @part_id: A unique identifier for each part.
 * @func: Callback for when the URI is requested.  The callback writes
 * its data to the supplied stream.
 * @part:
 * @use_count:
 *
 * This is used for multipart/related, and other formatters which may
 * need to include a reference to out-of-band data in the content
 * stream.
 *
 * This object may be subclassed as a struct.
 **/
struct _EMFormatPURI {
	void (*free)(EMFormatPURI *p); /* optional callback for freeing user-fields */
	EMFormat *format;

	gchar *uri;		/* will be the location of the part, may be empty */
	gchar *cid;		/* will always be set, a fake one created if needed */
	gchar *part_id;		/* will always be set, emf->part_id->str for this part */

	EMFormatPURIFunc func;
	CamelMimePart *part;

	guint use_count;	/* used by multipart/related to see if it was accessed */
};

struct _EMFormatHeader {
	guint32 flags;		/* E_FORMAT_HEADER_* */
	gchar name[1];
};

#define EM_FORMAT_HEADER_BOLD (1<<0)
#define EM_FORMAT_HEADER_LAST (1<<4) /* reserve 4 slots */

#define EM_FORMAT_VALIDITY_FOUND_PGP       (1<<0)
#define EM_FORMAT_VALIDITY_FOUND_SMIME     (1<<1)
#define EM_FORMAT_VALIDITY_FOUND_SIGNED    (1<<2)
#define EM_FORMAT_VALIDITY_FOUND_ENCRYPTED (1<<3)

/**
 * struct _EMFormat - Mail formatter object.
 *
 * @parent:
 * @priv:
 * @message:
 * @folder:
 * @uid:
 * @part_id:
 * @header_list:
 * @session:
 * @base url:
 * @snoop_mime_type:
 * @valid:
 * @valid_parent:
 * @inline_table:
 * @pending_uri_table:
 * @pending_uri_tree:
 * @pending_uri_level:
 * @mode:
 * @charset:
 * @default_charset:
 *
 * Most fields are private or read-only.
 *
 * This is the base MIME formatter class.  It provides no formatting
 * itself, but drives most of the basic types, including multipart / * types.
 **/
struct _EMFormat {
	GObject parent;

	EMFormatPrivate *priv;

	CamelMimeMessage *message; /* the current message */

	CamelFolder *folder;
	gchar *uid;

	GString *part_id;	/* current part id prefix, for identifying parts directly */

	GQueue header_list;	/* if empty, then all */

	CamelSession *session; /* session, used for authentication when required */
	CamelURL *base;	/* content-base header or absolute content-location, for any part */

	const gchar *snoop_mime_type; /* if we snooped an application/octet-stream type, what we snooped */

	/* for validity enveloping */
	CamelCipherValidity *valid;
	CamelCipherValidity *valid_parent;
	/* for checking whether found any signed/encrypted parts */
	guint32 validity_found;

	/* for forcing inlining */
	GHashTable *inline_table;

	/* global lookup table for message */
	GHashTable *pending_uri_table;

	/* This structure is used internally to form a visibility tree of
	 * parts in the current formatting stream.  This is to implement the
	 * part resolution rules for RFC2387 to implement multipart/related. */
	GNode *pending_uri_tree;
	/* current level to search from */
	GNode *pending_uri_level;

	em_format_mode_t mode;	/* source/headers/etc */
	gchar *charset;		/* charset override */
	gchar *default_charset;	/* charset fallback */
	gboolean composer; /* Formatting from composer ?*/
	gboolean print;
};

struct _EMFormatClass {
	GObjectClass parent_class;

	GHashTable *type_handlers;

	/* lookup handler, default falls back to hashtable above */
	const EMFormatHandler *(*find_handler)(EMFormat *, const gchar *mime_type);

	/* start formatting a message */
	void (*format_clone)(EMFormat *, CamelFolder *, const gchar *uid, CamelMimeMessage *, EMFormat *);

	/* some internel error/inconsistency */
	void (*format_error)(EMFormat *, CamelStream *, const gchar *msg);

	/* use for external structured parts */
	void (*format_attachment)(EMFormat *, CamelStream *, CamelMimePart *, const gchar *mime_type, const EMFormatHandler *info);

	/* use for unparsable content */
	void (*format_source)(EMFormat *, CamelStream *, CamelMimePart *);
	/* for outputing secure(d) content */
	void (*format_secure)(EMFormat *, CamelStream *, CamelMimePart *, CamelCipherValidity *);

	/* returns true if the formatter is still busy with pending stuff */
	gboolean (*busy)(EMFormat *);

	/* Shows optional way to open messages  */
	void (*format_optional)(EMFormat *, CamelStream *, CamelMimePart *, CamelStream* );

	/* signals */
	/* complete, alternative to polling busy, for asynchronous work */
	void (*complete)(EMFormat *);
};

void		em_format_set_mode		(EMFormat *emf,
						 em_format_mode_t type);
void		em_format_set_charset		(EMFormat *emf,
						 const gchar *charset);
void		em_format_set_default_charset	(EMFormat *emf,
						 const gchar *charset);

/* also indicates to show all headers */
void		em_format_clear_headers		(EMFormat *emf);

void		em_format_default_headers	(EMFormat *emf);
void		em_format_add_header		(EMFormat *emf,
						 const gchar *name,
						 guint32 flags);

/* FIXME: Need a 'clone' api to copy details about the current view (inlines etc)
   Or maybe it should live with sub-classes? */

gint		em_format_is_attachment		(EMFormat *emf,
						 CamelMimePart *part);

gint		em_format_is_inline		(EMFormat *emf,
						 const gchar *partid,
						 CamelMimePart *part,
						 const EMFormatHandler *handle);
void		em_format_set_inline		(EMFormat *emf,
						 const gchar *partid,
						 gint state);

gchar *		em_format_describe_part		(CamelMimePart *part,
						 const gchar *mime_type);

/* for implementers */
GType		em_format_get_type		(void);

void		em_format_class_add_handler	(EMFormatClass *emfc,
						 EMFormatHandler *info);
void		em_format_class_remove_handler	(EMFormatClass *emfc,
						 EMFormatHandler *info);
const EMFormatHandler *
		em_format_find_handler		(EMFormat *emf,
						 const gchar *mime_type);
const EMFormatHandler *
		em_format_fallback_handler	(EMFormat *emf,
						 const gchar *mime_type);

/* puri is short for pending uri ... really */
EMFormatPURI *	em_format_add_puri		(EMFormat *emf,
						 gsize size,
						 const gchar *uri,
						 CamelMimePart *part,
						 EMFormatPURIFunc func);
EMFormatPURI *	em_format_find_visible_puri	(EMFormat *emf,
						 const gchar *uri);
EMFormatPURI *	em_format_find_puri		(EMFormat *emf,
						 const gchar *uri);
void		em_format_clear_puri_tree	(EMFormat *emf);
void		em_format_push_level		(EMFormat *emf);
void		em_format_pull_level		(EMFormat *emf);

/* clones inline state/view and format, or use to redraw */
void		em_format_format_clone		(EMFormat *emf,
						 CamelFolder *folder,
						 const gchar *uid,
						 CamelMimeMessage *message,
						 EMFormat *source);

/* formats a new message */
void		em_format_format		(EMFormat *emf,
						 CamelFolder *folder,
						 const gchar *uid,
						 CamelMimeMessage *message);
void		em_format_redraw		(EMFormat *emf);
void		em_format_format_attachment	(EMFormat *emf,
						 CamelStream *stream,
						 CamelMimePart *mime_part,
						 const gchar *mime_type,
						 const EMFormatHandler *info);
void		em_format_format_error		(EMFormat *emf,
						 CamelStream *stream,
						 const gchar *format,
						 ...) G_GNUC_PRINTF (3, 4);
void		em_format_format_secure		(EMFormat *emf,
						 CamelStream *stream,
						 CamelMimePart *mime_part,
						 CamelCipherValidity *valid);
void		em_format_format_source		(EMFormat *emf,
						 CamelStream *stream,
						 CamelMimePart *mime_part);

gboolean	em_format_busy			(EMFormat *emf);

/* raw content only */
void		em_format_format_content	(EMFormat *emf,
						 CamelStream *stream,
						 CamelMimePart *part);

/* raw content text parts - should this just be checked/done by above? */
void		em_format_format_text		(EMFormat *emf,
						 CamelStream *stream,
						 CamelDataWrapper *part);

void		em_format_part_as		(EMFormat *emf,
						 CamelStream *stream,
						 CamelMimePart *part,
						 const gchar *mime_type);
void		em_format_part			(EMFormat *emf,
						 CamelStream *stream,
						 CamelMimePart *part);
void		em_format_merge_handler		(EMFormat *new,
						 EMFormat *old);

const gchar * em_format_snoop_type (CamelMimePart *part);

G_END_DECLS

#endif /* EM_FORMAT_H */
