/* ---------------------------------------------------------------------
   (c) ED 2004
   Project      : CLIB
   Function     : Flexible array
   Module       : FARR
   File         : farr.c
   Created      : 15-12-2004
   Modified     : 21-04-2006
   --------------------------------------------------------------------- */

/* ---------------------------------------------------------------------
   Log

   1.3 21-04-2006 out of memory error fixed.
   1.2 03-05-2005 Added farr_qsort() and farr_traverse()
   1.1 16-12-2004 farr_nb() becomes farr_nb_max(). Added farr_nb_cur()
   1.0 15-12-2004 Initial version
   0.0 15-12-2004 Created

   --------------------------------------------------------------------- */
#ifdef __cplusplus
#error This is not C++. Please use a C compiler.
#endif

#include "ed/inc/farr.h"
#include "ed/inc/sysalloc.h"
#include "ed/inc/sys.h"

/* private macro definitions =========================================== */

#define ID "FARR"
#define VER "1.3"

#define DBG 0

#if DBG
#define MODULE ID "."
#endif

#define DBG_MEM 0

/* private constants =================================================== */

/* private types ======================================================= */

/* private structures ================================================== */

struct farr
{
   size_t elem_size;
   size_t nb_elem;
   size_t w;

   /* could have been (void*), but 'char*' helps pointer's arithmetics.
    * No dereference is executed
    */
   char *arr;
};

/* private functions =================================================== */

/* public internal functions =========================================== */

/* entry points ======================================================== */

/* ---------------------------------------------------------------------
   farr_sid ()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
char const *farr_sid (void)
{
   return ID;
}

/* ---------------------------------------------------------------------
   farr_sver ()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
char const *farr_sver (void)
{
   return VER;
}

/* ---------------------------------------------------------------------
   farr_serr ()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
char const *farr_serr (farr_err_e err)
{
   char const *serr = "FARR_ERR_?";

   if (err < FARR_ERR_NB)
   {
      static char const *as[] =
      {
         "OK",
#define ITEM(n_, s_)\
         #s_,

#include "ed/inc/farr_err.itm"

#undef ITEM
      };

      serr = as[err];
   }

   return serr;
}

/* ---------------------------------------------------------------------
   farr_create ()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
farr_s *farr_create (size_t elem_size)
{
   farr_s *this = malloc (sizeof *this);

   if (this != NULL)
   {
      CLR (this, farr_s);

      {
         size_t const nb_elem = 2;
         size_t const size = elem_size * nb_elem;
         void *const arr = malloc (size);

         if (arr)
         {
            memset (arr, 0, size);

            this->elem_size = elem_size;
            this->nb_elem = nb_elem;
            this->arr = arr;
         }
         else
         {
            farr_delete (this), this = NULL;
         }
      }
   }
   return this;
}

/* ---------------------------------------------------------------------
   farr_delete ()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
void farr_delete (farr_s * this)
{
   if (this != NULL)
   {
      FREE (this->arr);

      free (this);
   }
}

/* ---------------------------------------------------------------------
   farr_init ()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
farr_err_e farr_init (farr_s * this)
{
   farr_err_e err = FARR_OK;

   if (this != NULL)
   {
      this->w = 0;
   }
   else
   {
      err = FARR_ERR_CONTEXT;
   }
   return err;
}

/* ---------------------------------------------------------------------
   farr_add ()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
farr_err_e farr_add (farr_s * this, void const *p_data)
{
   farr_err_e err = FARR_OK;

   if (this != NULL)
   {
      if (this->w == this->nb_elem)
      {
         /* grow by 2 */
         size_t const nb_elem = this->nb_elem * 2;
#if !DBG_MEM
         size_t const size = this->elem_size * nb_elem;
         char *const arr = realloc (this->arr, size);
#else
         char *const arr = NULL;
#endif
         if (arr != NULL)
         {
            /* clean the end of the array */
            memset (arr + this->elem_size * this->nb_elem
                    ,0
                    ,this->elem_size * this->nb_elem);

            /* record the new information */
            this->nb_elem = nb_elem;
            this->arr = arr;
         }
         else
         {
            err = FARR_ERR_MEMORY;
         }
      }

      /* record the data into the array */
      if (err == FARR_OK)
      {
         size_t const ndx = this->w * this->elem_size;
         memcpy (this->arr + (ndx)
                 ,p_data
                 ,this->elem_size);
#if DBG
         printf (MODULE "farr_add(%u) ndx=%u" EOL
                 ,(unsigned) this->w
                 ,(unsigned) ndx);
         SYS_dump (this->arr
                   ,this->elem_size * this->nb_elem);
#endif
         this->w++;
      }
   }
   else
   {
      err = FARR_ERR_CONTEXT;
   }
   return err;
}

/* ---------------------------------------------------------------------
   farr_get ()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
farr_err_e farr_get (farr_s * this, size_t i, void *p_data)
{
   farr_err_e err = FARR_OK;

   if (this != NULL)
   {
      if (i < this->nb_elem)
      {
         if (p_data != NULL)
         {
            /* make a copy of the data to the user */
            size_t const ndx = i * this->elem_size;
            memcpy (p_data
                    ,this->arr + ndx
                    ,this->elem_size);

#if DBG
            printf (MODULE "farr_get(%u) ndx=%u" EOL
                    ,(unsigned) i
                    ,(unsigned) ndx);
            SYS_dump (p_data, this->elem_size);
#endif
         }
         else
         {
            err = FARR_ERR_ADDRESS;
         }
      }
      else
      {
         err = FARR_ERR_INDEX;
      }
   }
   else
   {
      err = FARR_ERR_CONTEXT;
   }
   return err;
}

/* ---------------------------------------------------------------------
   farr_put ()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
farr_err_e farr_put (farr_s * this, size_t i, void const *p_data)
{
   farr_err_e err = FARR_OK;

   if (this != NULL)
   {
      if (i < this->nb_elem)
      {
         if (p_data != NULL)
         {
            /* make a copy of the data to the user */
            size_t const ndx = i * this->elem_size;
            memcpy (this->arr + ndx
                    ,p_data
                    ,this->elem_size);

#if DBG
            printf (MODULE "farr_put(%u) ndx=%u" EOL
                    ,(unsigned) i
                    ,(unsigned) ndx);
            SYS_dump (this->arr + ndx, this->elem_size);
#endif
         }
         else
         {
            err = FARR_ERR_ADDRESS;
         }
      }
      else
      {
         err = FARR_ERR_INDEX;
      }
   }
   else
   {
      err = FARR_ERR_CONTEXT;
   }
   return err;
}

/* ---------------------------------------------------------------------
   farr_qsort ()
   ---------------------------------------------------------------------
   trier le tableau
   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
farr_err_e farr_qsort (farr_s * this, farr_cmp_f * pf)
{
   farr_err_e err = FARR_OK;

   if (this != NULL)
   {
      if (this->w > 1)
      {
         qsort (this->arr, this->w, this->elem_size, pf);
      }
   }
   else
   {
      err = FARR_ERR_CONTEXT;
   }
   return err;
}

/* ---------------------------------------------------------------------
   farr_nb_max ()
   ---------------------------------------------------------------------
   retourne le nombre d'elements max du tableau...
   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
size_t farr_nb_max (farr_s * this)
{
   size_t nb = 0;

   if (this != NULL)
   {
      nb = this->nb_elem;
   }
   return nb;
}

/* ---------------------------------------------------------------------
   farr_nb_cur ()
   ---------------------------------------------------------------------
   retourne le nombre d'elements courant du tableau...
   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
size_t farr_nb_cur (farr_s * this)
{
   size_t nb = 0;

   if (this != NULL)
   {
      nb = this->w;
   }
   return nb;
}

/* ---------------------------------------------------------------------
   farr_traverse ()
   ---------------------------------------------------------------------
   parcours le tableau en partant de 0
   Appelle une fonction utilisateur a chaque fois avec l'adresse du bloc
   de donnee courant.

   Si la fonction utilisateur retoure autre chose que 0, le parcours
   s'arrete.
   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
farr_err_e farr_traverse (farr_s * this, farr_lst_f * pf, void *p_user)
{
   farr_err_e err = FARR_OK;

   if (this != NULL)
   {
      if (pf != NULL)
      {
         size_t i;

#if DBG
         printf (MODULE "Traversing");
#endif

         for (i = 0; i < this->w; i++)
         {
            size_t const ndx = i * this->elem_size;
            void const *p_data = this->arr + ndx;
#if DBG
            printf (".");
#endif
            if (pf (p_data, p_user) != 0)
            {
#if DBG
               printf ("interrupted");
#endif
               err = FARR_ERR_INTERRUPTED;
               break;
            }
         }
#if DBG
         printf ("\n");
#endif
      }
      else
      {
         err = FARR_ERR_CALLBACK;
      }
   }
   else
   {
      err = FARR_ERR_CONTEXT;
   }
   return err;
}

/* public data ========================================================= */

/* ---------------------------------------------------------------------
   Generated by NEW (c) ED 2.4
   Powered by C-code generator (c) ED 2003 1.0
   --------------------------------------------------------------------- */
