#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <time.h>

#include <glib.h>

#define G_BEGIN_DECLS
#define G_END_DECLS

#include "broken-date-parser.h"

const unsigned short camel_mime_special_table[256] = {
	0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005,
	0x0005, 0x00e7, 0x0007, 0x0005, 0x0005, 0x0027, 0x0005, 0x0005,
	0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005,
	0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005,
	0x00f2, 0x03c0, 0x004c, 0x02c0, 0x02c0, 0x00c0, 0x02c0, 0x00c0,
	0x004c, 0x004c, 0x01c0, 0x03c0, 0x004c, 0x03c0, 0x0248, 0x0144,
	0x03c0, 0x03c0, 0x03c0, 0x03c0, 0x03c0, 0x03c0, 0x03c0, 0x03c0,
	0x03c0, 0x03c0, 0x004c, 0x004c, 0x004c, 0x0004, 0x004c, 0x0044,
	0x004c, 0x03c0, 0x03c0, 0x03c0, 0x03c0, 0x03c0, 0x03c0, 0x03c0,
	0x03c0, 0x03c0, 0x03c0, 0x03c0, 0x03c0, 0x03c0, 0x03c0, 0x03c0,
	0x03c0, 0x03c0, 0x03c0, 0x03c0, 0x03c0, 0x03c0, 0x03c0, 0x03c0,
	0x03c0, 0x03c0, 0x03c0, 0x006c, 0x00ec, 0x006c, 0x02c0, 0x0240,
	0x02c0, 0x03c0, 0x03c0, 0x03c0, 0x03c0, 0x03c0, 0x03c0, 0x03c0,
	0x03c0, 0x03c0, 0x03c0, 0x03c0, 0x03c0, 0x03c0, 0x03c0, 0x03c0,
	0x03c0, 0x03c0, 0x03c0, 0x03c0, 0x03c0, 0x03c0, 0x03c0, 0x03c0,
	0x03c0, 0x03c0, 0x03c0, 0x02c0, 0x02c0, 0x02c0, 0x02c0, 0x0005,
	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
};

/* camel ctype type functions for rfc822/rfc2047/other, which are non-locale specific */
enum {
	CAMEL_MIME_IS_CTRL		= 1<<0,
	CAMEL_MIME_IS_LWSP		= 1<<1,
	CAMEL_MIME_IS_TSPECIAL	= 1<<2,
};

#define camel_mime_is_lwsp(x) ((camel_mime_special_table[(unsigned char)(x)] & CAMEL_MIME_IS_LWSP) != 0)
#define camel_mime_is_ttoken(x) ((camel_mime_special_table[(unsigned char)(x)] & (CAMEL_MIME_IS_TSPECIAL|CAMEL_MIME_IS_LWSP|CAMEL_MIME_IS_CTRL)) == 0)

static const char tz_months [][4] = {
	"Jan", "Feb", "Mar", "Apr", "May", "Jun",
	"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};

static const char tz_days [][4] = {
	"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
};

/* hrm, is there a library for this shit? */
static struct {
	char *name;
	int offset;
} tz_offsets [] = {
	{ "UT", 0 },
	{ "GMT", 0 },
	{ "EST", -500 },	/* these are all US timezones.  bloody yanks */
	{ "EDT", -400 },
	{ "CST", -600 },
	{ "CDT", -500 },
	{ "MST", -700 },
	{ "MDT", -600 },
	{ "PST", -800 },
	{ "PDT", -700 },
	{ "Z", 0 },
	{ "A", -100 },
	{ "M", -1200 },
	{ "N", 100 },
	{ "Y", 1200 },
};

static void
header_decode_lwsp(const char **in)
{
	const char *inptr = *in;
	char c;

	printf("is ws: '%s'\n", *in);

	while ((camel_mime_is_lwsp(*inptr) || *inptr =='(') && *inptr != '\0') {
		while (camel_mime_is_lwsp(*inptr) && *inptr != '\0') {
			printf("(%c)", *inptr);
			inptr++;
		}
		printf("\n");

		/* check for comments */
		if (*inptr == '(') {
			int depth = 1;
			inptr++;
			while (depth && (c=*inptr) && *inptr != '\0') {
				if (c=='\\' && inptr[1]) {
					inptr++;
				} else if (c=='(') {
					depth++;
				} else if (c==')') {
					depth--;
				}
				inptr++;
			}
		}
	}
	*in = inptr;
}


/* should this return -1 for no int? */

/**
 * camel_header_decode_int:
 * @in: pointer to input string
 *
 * Extracts an integer token from @in and updates the pointer to point
 * to after the end of the integer token (sort of like strtol).
 *
 * Returns the int value
 **/
int
camel_header_decode_int(const char **in)
{
	const char *inptr = *in;
	int c, v=0;

	header_decode_lwsp(&inptr);
	while ( (c=*inptr++ & 0xff)
		&& isdigit(c) ) {
		v = v*10+(c-'0');
	}
	*in = inptr-1;
	return v;
}


/* these are all internal parser functions */

static char *
decode_token (const char **in)
{
	const char *inptr = *in;
	const char *start;

	header_decode_lwsp (&inptr);
	start = inptr;
	while (camel_mime_is_ttoken (*inptr))
		inptr++;
	if (inptr > start) {
		*in = inptr;
		return g_strndup (start, inptr - start);
	} else {
		return NULL;
	}
}


/* convert a date to time_t representation */
/* this is an awful mess oh well */
time_t
camel_header_decode_date(const char *in)
{
	const char *inptr = in;
	char *monthname;
	gboolean foundmonth;
	int year, offset = 0;
	struct tm tm;
	int i;
	time_t t;

	if (in == NULL) {
		return 0;
	}

	printf ("\ndecoding date '%s'\n", inptr);

	memset (&tm, 0, sizeof(tm));

	header_decode_lwsp (&inptr);
	if (!isdigit (*inptr)) {
		char *day = decode_token (&inptr);
		/* we dont really care about the day, it's only for display */
		if (day) {
			printf ("got day: %s\n", day);
			g_free (day);
			header_decode_lwsp (&inptr);
			if (*inptr == ',') {
				inptr++;
			} else {
#ifndef CLEAN_DATE
				return parse_broken_date (in, NULL);
#else
				return 0;
#endif /* ! CLEAN_DATE */
			}
		}
	}
	tm.tm_mday = camel_header_decode_int(&inptr);
#ifndef CLEAN_DATE
	if (tm.tm_mday == 0) {
		return parse_broken_date (in, NULL);
	}
#endif /* ! CLEAN_DATE */

	monthname = decode_token(&inptr);
	foundmonth = FALSE;
	if (monthname) {
		for (i=0;i<sizeof(tz_months)/sizeof(tz_months[0]);i++) {
			if (!g_ascii_strcasecmp(tz_months[i], monthname)) {
				tm.tm_mon = i;
				foundmonth = TRUE;
				break;
			}
		}
		g_free(monthname);
	}
#ifndef CLEAN_DATE
	if (!foundmonth) {
		return parse_broken_date (in, NULL);
	}
#endif /* ! CLEAN_DATE */

	year = camel_header_decode_int(&inptr);
	if (year < 69) {
		tm.tm_year = 100 + year;
	} else if (year < 100) {
		tm.tm_year = year;
	} else if (year >= 100 && year < 1900) {
		tm.tm_year = year;
	} else {
		tm.tm_year = year - 1900;
	}
	/* get the time ... yurck */
	tm.tm_hour = camel_header_decode_int(&inptr);
	header_decode_lwsp(&inptr);
	if (*inptr == ':')
		inptr++;
	tm.tm_min = camel_header_decode_int(&inptr);
	header_decode_lwsp(&inptr);
	if (*inptr == ':')
		inptr++;
	tm.tm_sec = camel_header_decode_int(&inptr);
	header_decode_lwsp(&inptr);
	if (*inptr == '+'
	    || *inptr == '-') {
		offset = (*inptr++)=='-'?-1:1;
		offset = offset * camel_header_decode_int(&inptr);
		printf("abs signed offset = %d\n", offset);
		if (offset < -1200 || offset > 1400)
			offset = 0;
	} else if (isdigit(*inptr)) {
		offset = camel_header_decode_int(&inptr);
		printf("abs offset = %d\n", offset);
		if (offset < -1200 || offset > 1400)
			offset = 0;
	} else {
		char *tz = decode_token(&inptr);

		if (tz) {
			for (i=0;i<sizeof(tz_offsets)/sizeof(tz_offsets[0]);i++) {
				if (!g_ascii_strcasecmp(tz_offsets[i].name, tz)) {
					offset = tz_offsets[i].offset;
					break;
				}
			}
			g_free(tz);
		}
		/* some broken mailers seem to put in things like GMT+1030 instead of just +1030 */
		header_decode_lwsp(&inptr);
		if (*inptr == '+' || *inptr == '-') {
			int sign = (*inptr++)=='-'?-1:1;
			offset = offset + (camel_header_decode_int(&inptr)*sign);
		}
		printf("named offset = %d\n", offset);
	}

	t = e_mktime_utc(&tm);

	/* t is now GMT of the time we want, but not offset by the timezone ... */

	printf(" gmt normalized? = %s\n", ctime(&t));

	/* this should convert the time to the GMT equiv time */
	t -= ( (offset/100) * 60*60) + (offset % 100)*60;

	printf(" gmt normalized for timezone? = %s\n", ctime(&t));


	return t;
}


int main(int argc, char *argv[]) {
  int retval;

  retval = 0;

  camel_header_decode_date(argv[1]);

  return retval;
}
