#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <gtk/gtk.h>

#include "../include/string.h"
#include "../include/strexp.h"
#include "../include/fio.h"
#include "../include/disk.h"

#include "edvtypes.h"
#include "edvhistory.h"
#include "edvhistoryfio.h"
#include "config.h"


void EDVHistoryLoadFromFile(
	const gchar *filename,
	edv_history_struct ***list, gint *total,
	gint (*progress_cb)(gpointer, gulong, gulong),
	gpointer client_data
);

void EDVHistoryAppendToStream(
	FILE *fp, const edv_history_struct *h_ptr
);
void EDVHistoryAppendToFile(
	const gchar *filename, const edv_history_struct *h_ptr
);
void EDVHistorySaveToFile(
	const gchar *filename,
	edv_history_struct **list, gint total,
	gint (*progress_cb)(gpointer, gulong, gulong),
	gpointer client_data
);


#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)


/*
 *	Appends the history items loaded from the given file to the
 *	given list.
 */
void EDVHistoryLoadFromFile(
	const gchar *filename,
	edv_history_struct ***list, gint *total,
	gint (*progress_cb)(gpointer, gulong, gulong),
	gpointer client_data
)
{
	gchar *parm;
	FILE *fp;
	gulong file_size;
	gint h_num = -1;
	edv_history_struct *h_ptr = NULL;

	struct stat stat_buf;


	if((list == NULL) || (total == NULL) || STRISEMPTY(filename))
	    return;

	/* Open history file for reading */
	fp = FOpen(filename, "rb");
	if(fp == NULL)
	    return;

	/* Get file statistics */
	if(fstat(fileno(fp), &stat_buf))
	    file_size = 0l;
	else
	    file_size = (gulong)stat_buf.st_size;

	/* Call progress callback */
	if(progress_cb != NULL)
	    progress_cb(
		client_data, 0l, file_size
	    );

	/* Begin reading history file */
	parm = NULL;
	while(TRUE)
	{
	    /* Read next parameter */
	    parm = FSeekNextParm(
		fp, parm,
		EDV_CFG_COMMENT_CHAR,
		EDV_CFG_DELIMINATOR_CHAR
	    );
	    if(parm == NULL)
		break;

	    /* Call progress callback */
	    if(progress_cb != NULL)
	    {
		if(progress_cb(
		    client_data, (gulong)ftell(fp), file_size
		))
		    break;
	    }

	    /* Begin handling parameter */

	    /* Start of history item? */
	    if(!g_strcasecmp(parm, "Begin"))
	    {
		gint value[1];
		FGetValuesI(fp, value, 1);

		h_num = MAX(*total, 0);
		*total = h_num + 1;
		*list = (edv_history_struct **)g_realloc(
		    *list, (*total) * sizeof(edv_history_struct *)
		);
		if(*list == NULL)
		{
		    *total = 0;
		    h_num = -1;
		    h_ptr = NULL;
		    break;
		}

		(*list)[h_num] = h_ptr = EDVHistoryNew();
		if(h_ptr == NULL)
		    break;

		h_ptr->type = value[0];
	    }
	    /* TimeStart */
	    else if(!g_strcasecmp(parm, "TimeStart"))
	    {
		glong value[1];
		FGetValuesL(fp, value, 1);
		if(h_ptr != NULL)
		{
		    h_ptr->time_start = (gulong)value[0];
		}
	    }
	    /* TimeEnd */
	    else if(!g_strcasecmp(parm, "TimeEnd"))
	    {
		glong value[1];
		FGetValuesL(fp, value, 1);
		if(h_ptr != NULL)
		{
		    h_ptr->time_end = (gulong)value[0];
		}
	    }
	    /* Status */
	    else if(!g_strcasecmp(parm, "Status"))
	    {
		gint value[1];
		FGetValuesI(fp, value, 1);
		if(h_ptr != NULL)
		{
		    h_ptr->status = value[0];
		}
	    }
	    /* Source */
	    else if(!g_strcasecmp(parm, "Source"))
	    {
		gchar *value = FGetString(fp);
		if(h_ptr != NULL)
		{
		    g_free(h_ptr->src_path);
		    h_ptr->src_path = STRDUP(value);
		}
		g_free(value);
	    }
	    /* Target */
	    else if(!g_strcasecmp(parm, "Target"))
	    {
		gchar *value = FGetString(fp);
		if(h_ptr != NULL)
		{
		    g_free(h_ptr->tar_path);
		    h_ptr->tar_path = STRDUP(value);
		}
		g_free(value);
	    }
	    /* Comments */
	    else if(!g_strcasecmp(parm, "Comments"))
	    {
		gchar *value = FGetString(fp);
		if(h_ptr != NULL)
		{
		    g_free(h_ptr->comments);
		    h_ptr->comments = STRDUP(value);
		}
		g_free(value);
	    }
	    /* End */
	    else if(!g_strcasecmp(parm, "End"))
	    {
		FSeekNextLine(fp);

		/* Reset contexts */
		h_num = -1;
		h_ptr = NULL;
	    }
	    else
	    {
		FSeekNextLine(fp);
	    }
	}

	/* Delete last line (if any) */
	g_free(parm);

	/* Close history file */
	FClose(fp);
}


/*
 *	 Appends the history item to the stream fp.
 */
void EDVHistoryAppendToStream(
	FILE *fp, const edv_history_struct *h_ptr
)
{
	if((fp == NULL) || (h_ptr == NULL))
	    return;

	fprintf(
	    fp,
	    "Begin = %i\n",
	    h_ptr->type
	);
	if(h_ptr->time_start > 0)
	    fprintf(
		fp,
		"TimeStart = %ld\n",
		h_ptr->time_start
	    );
	if(h_ptr->time_end > 0)
	    fprintf(
		fp,
		"TimeEnd = %ld\n",
		h_ptr->time_end
	    );
	fprintf(
	    fp,
	    "Status = %i\n",
	    h_ptr->status
	);
	if(!STRISEMPTY(h_ptr->src_path))
	    fprintf(
		fp,
		"\tSource = %s\n",
		h_ptr->src_path
	    );
	if(!STRISEMPTY(h_ptr->tar_path))
	    fprintf(
		fp,
		"\tTarget = %s\n",
		h_ptr->tar_path
	    );
	if(!STRISEMPTY(h_ptr->comments))
	    fprintf(
		fp,
		"\tComments = %s\n",
		h_ptr->comments
	    );
	fprintf(fp, "End\n");
}

/*
 *	Appends the history item to the given file.
 */
void EDVHistoryAppendToFile(
	const gchar *filename, const edv_history_struct *h_ptr
)
{
	FILE *fp = FOpen(filename, "ab");
	if(fp == NULL)
	    return;

	EDVHistoryAppendToStream(fp, h_ptr);

	FClose(fp);
}

/*
 *	Saves the history items in the given list to the given file.
 */
void EDVHistorySaveToFile(
	const gchar *filename,
	edv_history_struct **list, gint total,
	gint (*progress_cb)(gpointer, gulong, gulong),
	gpointer client_data
)
{
	gboolean aborted = FALSE;
	gint i;
	gchar *parent_path;
	FILE *fp;
	const edv_history_struct *h_ptr;

	if((list == NULL) || STRISEMPTY(filename))
	    return;

	/* Get parent directory and create it as needed */
	parent_path = g_dirname(filename);
	if(parent_path != NULL)
	{
	    rmkdir(parent_path, S_IRUSR | S_IWUSR | S_IXUSR);
	    g_free(parent_path);
	    parent_path = NULL;
	}

	/* Open history file for writing */
	fp = FOpen(filename, "wb");
	if(fp == NULL)
	    return;

	/* Report initial progress */
	if(progress_cb != NULL)
	{
	    if(progress_cb(
		client_data, 0l, (gulong)total
	    ))
		aborted = TRUE;
	}

	/* Iterate through history items */
	for(i = 0; i < total; i++)
	{
	    if(aborted)
		break;

	    h_ptr = list[i];
	    if(h_ptr == NULL)
		continue;

	    /* Report progress */
	    if(progress_cb != NULL)
	    {
		if(progress_cb(
		    client_data, (gulong)(i + 1), (gulong)total
		))
		{
		    aborted = TRUE;
		    break;
		}
	    }

	    EDVHistoryAppendToStream(fp, h_ptr);
	}

	/* Close history file */
	FClose(fp);

	/* Report final progress */
	if((progress_cb != NULL) && !aborted)
	    progress_cb(
		client_data, (gulong)total, (gulong)total
	    );
}
