/*****************************************************************************

        GRAOUMF TRACKER 2
        Author: Laurent de Soras, 1996-2016

--- Legal stuff ---

This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://sam.zoy.org/wtfpl/COPYING for more details.

*****************************************************************************/



/*\\\ FICHIERS INCLUDE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

#include	<stdio.h>

#include	"base_ct.h"
#include	"Container.h"
#include	"List.h"
#include	"log.h"



/*\\\ CONSTANTES PUBLIQUES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*\\\ CONSTANTES PRIVEES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*\\\ TYPES & STRUCTURES PRIVEES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*\\\ DEFINITION DES VARIABLES DE CLASSE PUBLIQUES \\\\\\\\\\\\\\\\\\\\\\\\\*/



/*\\\ DEFINITION DES VARIABLES DE CLASSE PRIVEES \\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*\\\ METHODES PUBLIQUES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*==========================================================================*/
/*      Nom: (constructeur)                                                 */
/*      Description: Initialise                                             */
/*      Parametres en entree:                                               */
/*      Parametres en sortie:                                               */
/*      Parametres en entree/sortie:                                        */
/*==========================================================================*/

List::List (void)
{
	_next_ptr = NULL;
	_data_ptr = NULL;
}



List::List (const List &other)
{
	if (other._next_ptr != NULL)
	{
		_next_ptr = new List (*(other._next_ptr));
	}
	else
	{
		_next_ptr = NULL;
	}

	if (other._data_ptr != NULL)
	{
		_data_ptr = other._data_ptr->clone ();
	}
	else
	{
		_data_ptr = NULL;
	}
}



/*==========================================================================*/
/*      Nom: (destructeur)                                                  */
/*      Description: Detruit                                                */
/*==========================================================================*/

List::~List ()
{
	clear ();
}



/*==========================================================================*/
/*      Nom: check_ok                                                       */
/*      Description: Verifie l'intergrite de l'objet.                       */
/*      Retour: 0 si l'objet parait correct.                                */
/*==========================================================================*/

signed int	List::check_ok (void) const
{
	if (this == NULL)
	{
		LOG_printf ("List::check_ok: Error: \"this\" pointer is NULL.\n");
		return (-1);
	}

	if (   _next_ptr == NULL
	    && _data_ptr == NULL)
	{
		return (0);
	}

	if (_data_ptr == NULL)
	{
		LOG_printf ("List::check_ok: Error: _data_ptr is NULL.\n");
		return (-1);
	}

	return (0);
}



/*==========================================================================*/
/*      Nom: self_display                                                   */
/*      Description: Affiche "textuellement" le contenu courant de l'objet. */
/*==========================================================================*/

void	List::self_display (void) const
{

	/*** A faire ***/

}



void	List::operator = (const List &other)
{
	if (this == &other)
	{
		return;
	}

	clear ();

	if (other._next_ptr != NULL)
	{
		_next_ptr = new List (*(other._next_ptr));
	}
	else
	{
		_next_ptr = NULL;
	}

	if (other._data_ptr != NULL)
	{
		_data_ptr = other._data_ptr->clone ();
	}
	else
	{
		_data_ptr = NULL;
	}
}



bool	List::is_empty (void) const
{
	return (_next_ptr == NULL);
}



List	&List::next (void)
{
#if ! defined (NDEBUG)
	if (_next_ptr == NULL)
	{
		LOG_printf ("List::next: Error: no more element. Last element is returned.\n");
		return (*this);
	}
#endif

	return (*_next_ptr);
}



/* On peut donner comme index la longueur pour avoir l'element fantome */
List	&List::elt (long index)
{
	List	*current_ptr;

	current_ptr = this;

	while (index > 0)
	{
		current_ptr = current_ptr->_next_ptr;
#if ! defined (NDEBUG)
		if (current_ptr == NULL)
		{
			LOG_printf ("List::elt: Error: no more element.\n");
			break;
		}
#endif
		index --;
	}

	return (*current_ptr);
}



/* On peut donner comme index la longueur pour ajouter a la fin de la liste */
signed int	List::add (const Container *other_ptr, long index)
{
	List	*current_ptr;
	List	*new_ptr;

	current_ptr = &elt (index);
	new_ptr = new List;
	if (new_ptr == NULL)
	{
		LOG_printf ("List::add: Error: couldn't create new list element.\n");
		return (-1);
	}

	new_ptr->_next_ptr = current_ptr->_next_ptr;
	new_ptr->_data_ptr = current_ptr->_data_ptr;
	current_ptr->_next_ptr = new_ptr;
	current_ptr->_data_ptr = other_ptr->clone ();
	if (current_ptr->_data_ptr == NULL)
	{
		LOG_printf ("List::add: Error: couldn't duplicate element.\n");
		return (-1);
	}

	return (0);
}



// Equivalent a push_front
signed int	List::push (const Container *other_ptr)
{
	return (add (other_ptr));
}



// Equivalent a push_back
signed int	List::add_to_end (const Container *other_ptr)
{
	return (add (other_ptr, length ()));
}



// Acces au Nieme element
Container	*List::get (long index)
{
	List	*current_ptr;

	current_ptr = &elt (index);
#if ! defined (NDEBUG)
	if (current_ptr->_next_ptr == NULL)
	{
		LOG_printf ("List::get: Error: element doesn't exist.\n");
		return (NULL);
	}
#endif

	return (current_ptr->_data_ptr);
}



// pop_front(), mais retourne -1 si la liste est vide
signed int	List::pop (Container **dest_ptr_ptr, long index)
{
	List	*current_ptr;

	current_ptr = &elt (index);
#if ! defined (NDEBUG)
	if (current_ptr->_next_ptr == NULL)
	{
		LOG_printf ("List::pop: Error: element doesn't exist.\n");
		return (-1);
	}
#endif

	*dest_ptr_ptr = current_ptr->_data_ptr->clone ();
	current_ptr->del ();

	return (0);
}



// erase() du Nieme element
signed int	List::del (long index)
{
	List	*current_ptr;
	List	*next_ptr;

	current_ptr = &elt (index);
#if ! defined (NDEBUG)
	if (current_ptr->_next_ptr == NULL)
	{
		LOG_printf ("List::del: Error: element doesn't exist.\n");
		return (-1);
	}
#endif

	delete current_ptr->_data_ptr;
	next_ptr = current_ptr->_next_ptr;
	current_ptr->_next_ptr = next_ptr->_next_ptr;
	current_ptr->_data_ptr = next_ptr->_data_ptr;
	next_ptr->_next_ptr = NULL;
	next_ptr->_data_ptr = NULL;
	delete next_ptr;

	return (0);
}



long	List::length (void)
{
	List		*current_ptr;
	long		length;

	current_ptr = this;
	length = 0;

	while ((current_ptr = current_ptr->_next_ptr) != NULL)
	{
		length ++;
	}

	return (length);
}



void	List::clear (void)
{
	List		*temp_ptr;

	while (_next_ptr != NULL)
	{
		/* On retire de la liste l'element a detruire */
		temp_ptr = _next_ptr;
		_next_ptr = _next_ptr->_next_ptr;
		temp_ptr->_next_ptr = NULL;
		delete temp_ptr;
	}

	if (_data_ptr != NULL)
	{
		delete _data_ptr;
		_data_ptr = NULL;
	}
}



/*\\\ METHODES PRIVEES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/****************************************************************************/
/*                                                                          */
/*                                                                          */
/*                                                                          */
/****************************************************************************/



/*==========================================================================*/
/*      Nom:                                                                */
/*      Description:                                                        */
/*      Parametres en entree:                                               */
/*      Parametres en sortie:                                               */
/*      Parametres en entree/sortie:                                        */
/*      Retour:                                                             */
/*==========================================================================*/
