/* $Id: e2_list.c 602 2007-08-13 01:13:47Z tpgww $

Copyright (C) 2004-2007 tooar <tooar@gmx.net>
Portions copyright (C) 1999 Michael Clark.

This file is part of emelFM2.
emelFM2 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 3, or (at your option)
any later version.

emelFM2 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 emelFM2; see the file GPL. If not, contact the Free Software
Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/

/**
@file src/utils/e2_list.c
@brief glist utilities

glist utility-functions

*/

#include "emelfm2.h"
#include <string.h>

/**
@brief compare function for finding @a b in a GList of strings

@param a string data for current member of the glist being scanned
@param b string to find in the glist

@return 0 if the strings are equal
*/
gint e2_list_strcmp (const gchar *a, const gchar *b)
{
	return !g_str_equal (a, b);
}
/**
@brief update history-list for string @a latest
@a latest will be prepended to the list.
@param latest string to be listed
@param history store for history list pointer
@param cur store for new history length, or NULL
@param max the maximum length of the list, or 0 for no limit
@param doubl TRUE to allow multiple-instances of @a command in the list

@return
*/
void e2_list_update_history (const gchar *latest, GList **history,
	guint *cur, guint max, gboolean doubl)
{
	GList *tmp = doubl ? NULL : g_list_find_custom (*history, latest,
		(GCompareFunc) e2_list_strcmp);

	if (tmp != NULL)
	{	//found the string to be added in history already
		if (tmp != *history)
		{	//somewhere later in the list
			*history = g_list_remove_link (*history, tmp);
			*history = g_list_concat (tmp, *history);
		}
		//no net change of list length
	}
	else
	{	//multiples allowed, or new entry
		tmp = *history;
		if (tmp == NULL || !g_str_equal ((gchar *)tmp->data, latest))
		{
			*history = g_list_prepend (*history, g_strdup (latest));
			if (max > 0 && g_list_length (*history) > max)
			{
				tmp = g_list_last (*history);
				g_free (tmp->data);
				*history = g_list_delete_link (*history, tmp);
			}
		}
	}
	if (cur != NULL)
		*cur = g_list_length (*history);
}
/**
@brief convert a list of strings to an array of strings

@param list GList with gchar pointers as data

@return NULL-terminated array of strings from @a list
*/
gchar **e2_list_to_strv (GList *list)
{
	guint len = g_list_length (list);
	gchar **strv = g_malloc ((len + 1) * sizeof (gpointer));	//try_malloc
	gint i;
	GList *l = list;
	for (i = 0; i < len; i++)
	{
		strv[i] = l->data;
		l = g_list_next (l);
	}
	strv[len] = NULL;
	return strv;
}
/**
@brief copy a GList of strings

This function copies a GList and also copies the data of each element
with g_strdup.

@param list the GList to copy

@return new GList that has to be freed
*/
GList *e2_list_copy_with_data (GList *list)
{
	GList *copy = NULL;
	GList *tmp;
	for (tmp = g_list_first (list); tmp != NULL; tmp = g_list_next (tmp))
	{
		copy = g_list_append (copy, g_strdup (tmp->data));
	}
	return copy;
}
/**
@brief clear list of strings
The list pointer is set to NULL after clearing
@param list store for list pointer

@return
*/
void e2_list_free_with_data (GList **list)
{
	if ((list != NULL) && (*list != NULL))
	{
		g_list_foreach (*list, (GFunc) g_free, NULL);
		g_list_free (*list);
		*list = NULL;
	}
}
/**
@brief clear listed strings, but not the list itself

@param list store for list pointer

@return
*/
/* UNUSED
void e2_list_free_data_only (GList **list)
{
	if ((list != NULL) && (*list != NULL))
	{
		g_list_foreach (*list, (GFunc) g_free, NULL);
	}
} */
/**
@brief find member of list @a list, whose data is @a search_text

@param list pointer to list to be scanned
@param search_text the text to find

@return pointer to list member that holds @a search_text, or NULL
*/
/* UNUSED
GList *e2_list_find_data_string (GList *list, gchar *search_text)
{
	GList *tmp;
	gchar *curr;

	for (tmp = list; tmp != NULL; tmp = tmp->next)
	{
		curr = tmp->data;
		if (g_str_equal (curr, search_text)) return tmp;
	}
	return NULL;
} */

/**
@brief break @a list into two parts, the second part starting at index @a breaker

@param list pointer to list to be processed, may be NULL
If @a part1 == @a part2, @a part1 will simply be overwritten
If @a part1 &/| @a part2 is NULL, corresponding list will be cleared, any data will leak
@param breaker index of a member of @a list which will start the second part
@param part1 store for pointer to first broken part, may be NULL
@param part2 store for pointer to second broken part, may be NULL

@return
*/
void e2_list_nth_break (GList **list, guint breaker, GList **part1, GList **part2)
{
	if (*list == NULL)
		e2_list_break (NULL, NULL, FALSE, part1, part2);
	else if (breaker > g_list_length (*list))
		e2_list_break (list, NULL, TRUE, part1, part2);
	else
		e2_list_break (list, g_list_nth (*list, breaker), FALSE, part1, part2);
}

/**
@brief break @a list into two parts, the second part starting with @a breaker

@param list pointer to list to be processed, may be NULL
@a part1 or @a part2 may be address of @a list
If @a part1 == @a part2, @a part1 will simply be overwritten
If @a part1 &/| @a part2 is NULL, corresponding list will be cleared, any data will leak
@param breaker pointer to member of @a list which will start the second part, may be NULL
@param nullatend if TRUE, and @a breqak is NULL, it's at the end of @a list, i.e. from a nextward scan
@param part1 store for pointer to first broken part, may be NULL
@param part2 store for pointer to second broken part, may be NULL

@return
*/
void e2_list_break (GList **list, GList *breaker, gboolean nullatend,
	GList **part1, GList **part2)
{
	if (*list != NULL)
	{
		if (breaker != NULL)
		{
			GList *prev = breaker->prev;
			if (prev != NULL)
			{
				prev->next = NULL;
				breaker->prev = NULL;
				if (part1 != NULL)
					*part1 = *list;
				else
				{
					g_list_free (*list);	//any data will leak
					*list = NULL;
				}
			}
			if (part2 != NULL)
				*part2 = breaker;	//g_list_concat (NULL, breaker);
			else
			{
				g_list_free (breaker);	//any data will leak
//				breaker = NULL;
			}

		}
		else	//NULL breaker
		{
			if (nullatend)
			{
				if (part1 != NULL)
					*part1 = *list;
				else
				{
					g_list_free (*list);	//any data will leak
					*list = NULL;
				}
				if (part2 != NULL)
					*part2 = NULL;
			}
			else
			{
				if (part1 != NULL)
					*part1 = NULL;
				if (part2 != NULL)
					*part2 = *list;
				else
				{
					g_list_free (*list);	//any data will leak
					*list = NULL;
				}
			}
		}
	}
	else //list doesn't exist
	{
		if (part1 != NULL)
			*part1 = NULL;
		if (part2 != NULL)
			*part2 = NULL;
	}
}
