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

#include "../../include/string.h"

#include "edv_types.h"
#include "edv_get.h"
#include "edv_date.h"
#include "edv_context.h"
#include "edv_cfg_list.h"


const gchar *EDVDateString(
	edv_context_struct *ctx,
	const gulong t
);
const gchar *EDVDateStringDuration(
	edv_context_struct *ctx,
	const gulong dt
);


#define ATOI(s)		(((s) != NULL) ? atoi(s) : 0)
#define ATOL(s)		(((s) != NULL) ? atol(s) : 0)
#define ATOF(s)		(((s) != NULL) ? atof(s) : 0.0f)
#define STRDUP(s)	(((s) != NULL) ? g_strdup(s) : NULL)

#define MAX(a,b)	(((a) > (b)) ? (a) : (b))
#define MIN(a,b)	(((a) < (b)) ? (a) : (b))
#define CLIP(a,l,h)	(MIN(MAX((a),(l)),(h)))
#define STRLEN(s)	(((s) != NULL) ? strlen(s) : 0)
#define STRISEMPTY(s)	(((s) != NULL) ? (*(s) == '\0') : TRUE)


/*
 *	Returns a statically allocated string describing the date &
 *	time.
 *
 *	The format and relativity of the date & time is specified by
 *	the configuration on the context.
 *
 *	The ctx specifies the Endeavour 2 Context.
 *
 *	The t specifies the time in seconds since EPOCH.
 */
const gchar *EDVDateString(
	edv_context_struct *ctx,
	const gulong t
)
{
	const gchar *format;
	edv_date_relativity relativity;

	if(ctx == NULL)
	    return(NULL);

	format = EDVGetS(ctx, EDV_CFG_PARM_DATE_FORMAT);
	relativity = EDVGetI(ctx, EDV_CFG_PARM_DATE_RELATIVITY);

	/* Handle by relativity */
	switch(relativity)
	{
	  case EDV_DATE_RELATIVITY_CURRENT:
	    if(TRUE)
	    {
		gulong ct, dt = (gulong)time(NULL) - t;
		static gchar buf[80];

		/* Less than one second? */
		if(dt < 1)
		{
		    g_snprintf(
			buf, sizeof(buf),
			"less than a second ago"
		    );
		}
		/* Less than one minute? */
		else if(dt < (1 * 60))
		{
		    ct = MAX(dt / 1, 1);
		    g_snprintf(
			buf, sizeof(buf),
			"%ld second%s ago",
			ct, ((ct == 1) ? "" : "s")
		    );
		}
		/* Less than one hour? */
		else if(dt < (60 * 60))
		{
		    ct = MAX(dt / 60, 1);
		    g_snprintf(
			buf, sizeof(buf),
			"%ld minute%s ago",
			ct, ((ct == 1) ? "" : "s")
		    );
		}
		/* Less than one day? */
		else if(dt < (24 * 60 * 60))
		{
		    ct = MAX(dt / 60 / 60, 1);
		    g_snprintf(
			buf, sizeof(buf),
			"%ld hour%s ago",
			ct, ((ct == 1) ? "" : "s")
		    );
		}
		/* Less than one week? */
		else if(dt < (7 * 24 * 60 * 60))
		{
		    ct = MAX(dt / 60 / 60 / 24, 1);
		    g_snprintf(
			buf, sizeof(buf),
			"%ld day%s ago",
			ct, ((ct == 1) ? "" : "s")
		    );
		}
		/* Less than one month (30 days)? */
		else if(dt < (30 * 24 * 60 * 60))
		{
		    ct = MAX(dt / 60 / 60 / 24 / 7, 1);
		    g_snprintf(
			buf, sizeof(buf),
			"%ld week%s ago",
			ct, ((ct == 1) ? "" : "s")
		    );
		}
		/* Less than 6 months ago? */
		else if(dt < (6 * 30 * 24 * 60 * 60))
		{
		    ct = MAX(dt / 60 / 60 / 24 / 30, 1);
		    g_snprintf(
			buf, sizeof(buf),
			"%ld month%s ago",
			ct, ((ct == 1) ? "" : "s")
		    );
		}
		else
		{
		    /* All else revert to using ctime() */
		    time_t tv = (time_t)t;
		    gchar *s = ctime(&tv);

		    strncpy(
			buf,
			(s != NULL) ? s : "",
			sizeof(buf)
		    );
		    buf[sizeof(buf) - 1] = '\0';

		    s = strchr(buf, '\n');
		    if(s != NULL)
			*s = '\0';
		}

		return(buf);
	    }
	    break;

	  case EDV_DATE_RELATIVITY_ABSOLUTE:
	    if(format == NULL)
	    {
		/* No format string given so assume to use ctime() */
		time_t t2 = (time_t)t;
		return((gchar *)ctime(&t2));
	    }
	    else
	    {
		static gchar buf[80];
		time_t tv = (time_t)t;
		const struct tm *tm_ptr;

		*buf = '\0';
		tm_ptr = localtime(&tv);

		if(tm_ptr != NULL)
		    strftime(
			buf, sizeof(buf),
			format, tm_ptr
		    );
		buf[sizeof(buf) - 1] = '\0';

		return(buf);
	    }
	    break;
	}

	return("");
}

/*
 *	Returns a statically allocated string describing the duration
 *	(delta time or time lapsed).
 *
 *	The format and relativity of the date & time is specified by
 *	the configuration on the context.
 *
 *	The ctx specifies the Endeavour 2 Context.
 *
 *	The dt specifies the delta time in seconds.
 */
const gchar *EDVDateStringDuration(
	edv_context_struct *ctx,
	const gulong dt
)
{
	gulong ct, ct2;
	static gchar buf[80];

	/* Less than one second? */
	if(dt < 1l)
	{
	    g_snprintf(
		buf, sizeof(buf),
		"less than a second"
	    );
	}
	/* Less than one minute? */
	else if(dt < (1l * 60l))
	{
	    ct = MAX(dt / 1l, 1l);
	    g_snprintf(
		buf, sizeof(buf),
		"%ld second%s",
		ct, ((ct == 1l) ? "" : "s")
	    );
	}
	/* Less than one hour? */
	else if(dt < (60l * 60l))
	{
	    ct = MAX(dt / 60l, 1l);
	    ct2 = MAX(dt / 1l, 1l) % 60l;
	    g_snprintf(
		buf, sizeof(buf),
		"%ld minute%s %ld second%s",
		ct, ((ct == 1l) ? "" : "s"),
		ct2, ((ct2 == 1l) ? "" : "s")
	    );
	}
	/* Less than one day? */
	else if(dt < (24l * 60l * 60l))
	{
	    ct = MAX(dt / 60l / 60l, 1l);
	    ct2 = MAX(dt / 60l, 1l) % 60l;
	    g_snprintf(
		buf, sizeof(buf),
		"%ld hour%s %ld minute%s",
		ct, ((ct == 1l) ? "" : "s"),
		ct2, ((ct2 == 1l) ? "" : "s")
	    );
	}
	/* Less than one week? */
	else if(dt < (7l * 24l * 60l * 60l))
	{
	    ct = MAX(dt / 60l / 60l / 24l, 1l);
	    ct2 = MAX(dt / 60l / 60l, 1l) % 24l;
	    g_snprintf(
		buf, sizeof(buf),
		"%ld day%s %ld hour%s",
		ct, ((ct == 1l) ? "" : "s"),
		ct2, ((ct2 == 1l) ? "" : "s")
	    );
	}
	/* Less than one month (30 days)? */
	else if(dt < (30l * 24l * 60l * 60l))
	{
	    ct = MAX(dt / 60l / 60l / 24l / 7l, 1l);
	    ct2 = MAX(dt / 60l / 60l / 24l, 1l) % 7l;
	    g_snprintf(
		buf, sizeof(buf),
		"%ld week%s %ld day%s",
		ct, ((ct == 1l) ? "" : "s"),
		ct2, ((ct2 == 1l) ? "" : "s")
	    );
	}
	/* Less than 6 months ago? */
#if 0
	else if(dt < (6l * 30l * 24l * 60l * 60l))
#else
	else
#endif
	{
	    ct = MAX(dt / 60l / 60l / 24l / 30l, 1l);
	    ct2 = MAX(dt / 60l / 60l / 24l, 1l) % 30l;
	    g_snprintf(
		buf, sizeof(buf),
		"%ld month%s %ld day%s",
		ct, ((ct == 1l) ? "" : "s"),
		ct2, ((ct2 == 1l) ? "" : "s")
	    );
	}

	return(buf);
}
