/*
 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
 * Copyright (C) 1999,2000 Hiroyuki Yamamoto
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include <glib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>

#include "enriched.h"
#include "codeconv.h"
#include "utils.h"

#define ERTFBUFSIZE	8192


static ERTFState ertf_read_line		(ERTFParser	*parser);
static void ertf_append_char		(ERTFParser	*parser,
					 gchar		 ch);
static void ertf_append_str		(ERTFParser	*parser,
					 const gchar	*str,
					 gint		 len);

static ERTFState ertf_parse_tag		(ERTFParser	*parser);
static void ertf_get_parenthesis	(ERTFParser	*parser, 
					 gchar		*buf, 
					 gint 		 len);

ERTFParser *ertf_parser_new(FILE *fp, CodeConverter *conv)
{
	ERTFParser *parser;

	g_return_val_if_fail(fp   != NULL, NULL);
	g_return_val_if_fail(conv != NULL, NULL);

	parser             = g_new0(ERTFParser, 1);
	parser->fp         = fp;
	parser->conv       = conv;
	parser->str        = g_string_new(NULL);
	parser->buf        = g_string_new(NULL);
	parser->bufp       = parser->buf->str;
	parser->newline    = TRUE;
	parser->empty_line = TRUE;
	parser->space      = FALSE;
	parser->pre        = FALSE;

	return parser;
}

void ertf_parser_destroy(ERTFParser *parser)
{
	g_string_free(parser->str, TRUE);
	g_string_free(parser->buf, TRUE);
	g_free(parser);
}

gchar *ertf_parse(ERTFParser *parser)
{
	parser->state = ERTF_NORMAL;
	g_string_truncate(parser->str, 0);

	if (*parser->bufp == '\0') {
		g_string_truncate(parser->buf, 0);
		parser->bufp = parser->buf->str;
		if (ertf_read_line(parser) == ERTF_EOF)
			return NULL;
	}
	
	while (*parser->bufp != '\0') {
		switch (*parser->bufp) {
			case '<':
				if (parser->str->len == 0)
					ertf_parse_tag(parser);
				else
					return parser->str->str;
				break;
			case '\n':
			case '\r':	
				if (parser->bufp[0] == '\r' && parser->bufp[1] == '\n') 
						parser->bufp++;

				if (!parser->pre) {
					/* When not pre (not <nofill>), 1 CRLF = SPACE, N>1 CRLF = N-1 CRLF*/
					if (!parser->newline) {
						parser->newline = TRUE;
						parser->space = TRUE;
					parser->bufp++;
					break;
					}

				}
			default:
				ertf_append_char(parser, *parser->bufp++);
		}
	}
	
	return parser->str->str;
}

static ERTFState ertf_read_line(ERTFParser *parser)
{
	gchar buf[ERTFBUFSIZE];
	gchar buf2[ERTFBUFSIZE];
	gint index;

	if (fgets(buf, sizeof(buf), parser->fp) == NULL) {
		parser->state = ERTF_EOF;
		return ERTF_EOF;
	}

	if (conv_convert(parser->conv, buf2, sizeof(buf2), buf) < 0) {
		g_warning("ertf_read_line(): code conversion failed\n");

		index = parser->bufp - parser->buf->str;

		g_string_append(parser->buf, buf);

		parser->bufp = parser->buf->str + index;

		return ERTF_ERR;
	}

	index = parser->bufp - parser->buf->str;

	g_string_append(parser->buf, buf2);

	parser->bufp = parser->buf->str + index;

	return ERTF_NORMAL;
}

static void ertf_append_char(ERTFParser *parser, gchar ch)
{
	GString *str = parser->str;

	if (!parser->pre && parser->space) {
		if (ch != '\n')
			g_string_append_c(str, ' ');
		parser->space = FALSE;
	}

	g_string_append_c(str, ch);

	parser->empty_line = FALSE;
	if (ch == '\n') {
		if (parser->newline)
			parser->empty_line = TRUE;
		else
			parser->newline = TRUE;
	} else
		parser->newline = FALSE;
}

static void ertf_append_str(ERTFParser *parser, const gchar *str, gint len)
{
	GString *string = parser->str;

	if (!parser->pre && parser->space) {
		g_string_append_c(string, ' ');
		parser->space = FALSE;
	}

	if (len == 0) return;
	if (len < 0)
		g_string_append(string, str);
	else {
		gchar *s;
		Xstrndup_a(s, str, len, return);
		g_string_append(string, s);
	}

	parser->empty_line = FALSE;
	if (string->len > 0 && string->str[string->len - 1] == '\n') {
		parser->newline = TRUE;
		if (string->len > 1 && string->str[string->len - 2] == '\n')
			parser->empty_line = TRUE;
	} else
		parser->newline = FALSE;
}

static ERTFState ertf_parse_tag(ERTFParser *parser)
{
	gchar buf[ERTFBUFSIZE];
	gchar *p;
	
	ertf_get_parenthesis (parser, buf, sizeof(buf));
	
	for (p = buf; *p != '\0'; p++) {
		if (isspace (*p)) {
			*p = '\0';
			break;
		}
	}

	parser->state = ERTF_UNKNOWN;
	if (buf[0] == '\0') return parser->state;

	g_strdown (buf);

	if (!strcmp(buf, "nofill")) {
		parser->pre   = TRUE;
		parser->state = ERTF_NOFILL;
	}
	else if (!strcmp(buf, "/nofill")) {
		parser->pre   = FALSE;
		parser->state = ERTF_NORMAL;
	}
	
	return parser->state;
}
		
static void ertf_get_parenthesis(ERTFParser *parser, gchar *buf, gint len)
{
	gchar *p;

	buf[0] = '\0';
	g_return_if_fail(*parser->bufp == '<');

	/* ignore params */
	if (!g_strncasecmp(parser->bufp, "<param>", 4)) {
		parser->bufp += 7;
		while ((p = strstr(parser->bufp, "</param>")) == NULL)
			if (ertf_read_line(parser) == ERTF_EOF) return;
		parser->bufp = p + 8;
		return;
	}
	parser->bufp++;
	while ((p = strchr(parser->bufp, '>')) == NULL)
		if (ertf_read_line(parser) == ERTF_EOF) return;

	strncpy2(buf, parser->bufp, MIN(p - parser->bufp + 1, len));
	parser->bufp = p + 1;
}

