/* ---------------------------------------------------------------------
   (c) ED 2002-2005
   Project      : CLIB
   Function     : Multi-user manager
   Module       : MUSR
   File         : MUSR.C
   Created      : 03-09-2002
   Modified     : 17-03-2005
   --------------------------------------------------------------------- */

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

   0.0 - 03-09-2002 Created
   1.0 - 03-09-2002 Initial version
   1.1 - 16-10-2002 MUSR_refresh() added
   1.2 - 17-10-2002 callback return value added
   1.3 - 06-11-2002 transition value added
   1.4 - 07-11-2002 action flags added (ALL, FIRST, LAST)
   1.5 - 09-11-2002 action flags modified ALL becomes ALL_ON / ALL_OFF
   1.6 - 13-11-2002 callback return management debug.
   1.7 - 18-11-2002 "release a NO_ID client" debug.
   1.8 - 19-11-2002 "apply()" debug.
   .                error handlig debug.
   1.9 - 21-11-2002 Events order changed in release().
   1.10- 05-05-2004 Added MUSR_stra()
   1.11- 17-03-2005 Linux : include file names forced to lowercase
   --------------------------------------------------------------------- */
#ifdef __cplusplus
#error This source file is not C++ but rather C. Please use a C-compiler
#endif

#include <string.h>
#include <stdio.h>

#include "ed/inc/musr.h"
#include "ed/inc/bits.h"
#include "ed/inc/sys.h"
#include "ed/inc/sysalloc.h"

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

#define DBG MUSR_DBG

#define MODULE "MUSR"
#define VER "1.11"
#define ID "MUSR Module \"C\" (c) ED 2002-2005"

#define NO_ID MUSR_NO_ID

#define REFRESH ((uint) -1)

/* Word NumBer */
#define WNB 2

/* Word's Length (in bits) */
#define WL 32

/* maximum ID NumBer */
#define ID_NB (WL * WNB)

/* constants =========================================================== */
/* types =============================================================== */
/* structures ========================================================== */

typedef struct
{
   /* callback */
   fMUSR_OUT *pfOut;
   void *pDataOut;

   /* client flags */
   ulong client[WNB];

   /* object status */
   uint sts;
   uint sts_off;

   /* callback return value */
   int cb_ret;

   /* millesceanous internal flags */
   uint first:1;
   uint last:1;
   uint all_on:1;
   uint all_off:1;
}
sPRIVATE;

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

/* ---------------------------------------------------------------------
   is_full()
   ---------------------------------------------------------------------
   is the client list full ?
   ---------------------------------------------------------------------
   E : Context
   S :
   --------------------------------------------------------------------- */
static int is_full (sPRIVATE * this)
{
   int full = 1;
   size_t i;

   STACK_CHK ();

   for (i = 0; i < NELEM (this->client); i++)
   {
      if (this->client[i] != 0xFFFFFFFFUL)
      {
         full = 0;
         break;

      }
   }
   return full;
}

/* ---------------------------------------------------------------------
   is_null()
   ---------------------------------------------------------------------
   is the client list null ?
   ---------------------------------------------------------------------
   E : Context
   S :
   --------------------------------------------------------------------- */
static int is_null (sPRIVATE * this)
{
   int null = 1;
   size_t i;

   STACK_CHK ();

   for (i = 0; i < NELEM (this->client); i++)
   {
      if (this->client[i] != 0UL)
      {
         null = 0;
         break;

      }
   }
   return null;
}

/* ---------------------------------------------------------------------
   clear_cli()
   ---------------------------------------------------------------------
   effacement des clients
   ---------------------------------------------------------------------
   E : Context
   S :
   --------------------------------------------------------------------- */
static void clear_cli (sPRIVATE * this)
{
   size_t i;

   STACK_CHK ();

   for (i = 0; i < NELEM (this->client); i++)
   {
      this->client[i] = 0UL;
   }
}

/* ---------------------------------------------------------------------
   out()
   ---------------------------------------------------------------------
   Internal output function. Calls the user's function (callback)
   ---------------------------------------------------------------------
   E : Context
   S :
   --------------------------------------------------------------------- */
static int out (sPRIVATE * this, uint sts, eMUSR_TRA tra)
{
   int ret = -1;

   STACK_CHK ();

   if (this != NULL)
   {
      /* Calls the user's function */
      if (this->pfOut)
      {
         if (sts != REFRESH)
         {
            this->sts = sts;
         }
         ret = this->pfOut (this->pDataOut, this->sts, tra);
      }
   }
   return ret;
}

/* ---------------------------------------------------------------------
   on()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   E :
   S :
   --------------------------------------------------------------------- */
static eMUSR_ERR on (sPRIVATE * const this, uint sts, musr_cli_id_s * pid)
{
   eMUSR_ERR ret = MUSR_ERR_UNDEFINED;
   ASSERT (this != NULL);
   STACK_CHK ();

   /*  function code */

   if (pid)
   {
      if (*pid == NO_ID)
      {
         if (!is_full (this))
         {
            musr_cli_id_s id = 0;

            {
               size_t i;
               int found = 0;

               for (i = 0; !found && i < NELEM (this->client); i++)
               {
                  ulong m = bit0;

                  for (;;)
                  {
                     if (mGET (this->client[i], m))
                     {
                        id++;
                        if (id == WL)
                        {
                           break;
                        }
                        m <<= 1UL;
                     }
                     else
                     {
                        found = 1;
                        break;
                     }
                  }
               }
            }

            if (id < ID_NB)
            {
               if (is_null (this) || this->sts != sts)
               {
                  if (this->first)
                  {
                     this->cb_ret = out (this, sts, MUSR_TRA_FIRST);
                  }
               }
               *pid = id;
               mSET (this->client[id / WL], BIT (id % WL));
               ret = MUSR_OK;
            }
         }
         else
         {
            ret = MUSR_ERR_FULL;
         }
      }
      else
      {
         musr_cli_id_s const id = *pid;

         if (id >= 0 && id < ID_NB)
         {
            ulong const id_msk = BIT (id % WL);

            if (mGET (this->client[id / WL], id_msk))
            {
               if (this->client[id / WL] == 0UL || this->sts != sts)
               {
                  if (this->first)
                  {
                     this->cb_ret = out (this, sts, MUSR_TRA_FIRST);
                  }
               }
               mSET (this->client[id / WL], id_msk);
               ret = MUSR_OK;
            }
            else
            {
               ret = MUSR_ERR_UNKNOWN;
            }
         }
         else
         {
            ret = MUSR_ERR_UNKNOWN;
         }
      }
   }
   else
   {
      this->cb_ret = out (this, sts, MUSR_TRA_REFRESH);
      ret = MUSR_OK;
   }

   if (this->all_on)
   {
      int cb_ret = out (this, sts, MUSR_TRA_ALL_ON);

      /* priority to the first cb if occured */
      if (this->cb_ret == -1)
      {
         this->cb_ret = cb_ret;
      }
   }

   return ret;
}

/* ---------------------------------------------------------------------
   off()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   E :
   S :
   --------------------------------------------------------------------- */
static eMUSR_ERR off (sPRIVATE * const this, musr_cli_id_s * pid)
{
   eMUSR_ERR ret = MUSR_ERR_UNDEFINED;
   ASSERT (this != NULL);
   STACK_CHK ();

   /*  function code */

   if (this->all_off)
   {
      this->cb_ret = out (this, this->sts_off, MUSR_TRA_ALL_OFF);
   }

   if (pid != NULL)
   {
      musr_cli_id_s const id = *pid;

      if (id >= 0 && id < ID_NB)
      {
         ulong const id_msk = BIT (id % WL);
         size_t const i = id / WL;

         if (mGET (this->client[i], id_msk))
         {
            mCLR (this->client[i], id_msk);

            if (this->client[i] == 0UL)
            {
               if (this->last)
               {
                  int cb_ret = out (this, this->sts_off, MUSR_TRA_LAST);

                  /* priority to the first cb if occured */
                  if (this->cb_ret == -1)
                  {
                     this->cb_ret = cb_ret;
                  }
               }
            }
            *pid = NO_ID;
            ret = MUSR_OK;
         }
      }
      else
      {
         ret = MUSR_ERR_NO_ID;
      }
   }
   else
   {
      this->cb_ret = out (this, this->sts_off, MUSR_TRA_REFRESH);
      ret = MUSR_OK;
   }

   return ret;
}

/* ---------------------------------------------------------------------
   refresh()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   E :
   S :
   --------------------------------------------------------------------- */
static eMUSR_ERR refresh (sPRIVATE * const this)
{
   eMUSR_ERR ret = MUSR_OK;
   ASSERT (this != NULL);
   STACK_CHK ();

   /*  function code */

   this->cb_ret = out (this, REFRESH, MUSR_TRA_REFRESH);

   return ret;
}

#if DBG
/* ---------------------------------------------------------------------
   dbg_sts()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   E :
   S :
   --------------------------------------------------------------------- */
static void dbg_sts (sPRIVATE * const this)
{
   ASSERT (this != NULL);
   STACK_CHK ();

   /*  function code */

   {
      musr_cli_id_s id = 0;
      int none = 1;

      printf ("Status = %u", (uint) this->sts);
      {
         size_t i;
         for (i = 0; i < NELEM (this->client); i++)
         {
            ulong m = bit0;
            while (id < WL)
            {
               if (mGET (this->client[i], m))
               {
                  if (none)
                  {
                     none = 0;
                     printf (" Clients : [%u", (uint) id);
                  }
                  else
                  {
                     printf (", %u", (uint) id);
                  }
               }
               id++;
               m <<= 1UL;
            }
         }
      }

      if (none)
      {
         puts (" No clients");
      }
      else
      {
         puts ("]");
      }
   }
}

#endif

/* internal public data ================================================ */
/* internal public functions =========================================== */
/* entry points ======================================================== */

/* ---------------------------------------------------------------------
   MUSR_sver()
   ---------------------------------------------------------------------
   Returns the "Version" string
   ---------------------------------------------------------------------
   E :
   S : ASCIIZ string pointer
   --------------------------------------------------------------------- */
char const *MUSR_sver (void)
{
   STACK_CHK ();

   return VER;
}

/* ---------------------------------------------------------------------
   MUSR_sid()
   ---------------------------------------------------------------------
   Returns the "Identification" string
   ---------------------------------------------------------------------
   E :
   S : ASCIIZ string pointer
   --------------------------------------------------------------------- */
char const *MUSR_sid (void)
{
   STACK_CHK ();

   return ID;
}

/* ---------------------------------------------------------------------
   MUSR_serr()
   ---------------------------------------------------------------------
   Returns the "Error" string
   ---------------------------------------------------------------------
   E :
   S : ASCIIZ string pointer
   --------------------------------------------------------------------- */
char const *MUSR_serr (eMUSR_ERR err)
{
   char const *s = "Uknown error";
   /* error strings */
   static const char *const as[] =
   {
      "no error",
#define ITEM(a,b) #b,
#include "ed/inc/musr_err.itm"
#undef ITEM
   };

   STACK_CHK ();

   if (err < MUSR_ERR_NB)
   {
      s = as[err];
   }

   return s;
}

/* ---------------------------------------------------------------------
   MUSR_stra()
   ---------------------------------------------------------------------
   Returns the "Transition" string
   ---------------------------------------------------------------------
   E :
   S : ASCIIZ string pointer
   --------------------------------------------------------------------- */
char const *MUSR_stra (eMUSR_TRA tra)
{
   char const *s = "Uknown transition";
   /* error strings */
   static const char *const as[] =
   {
#define ITEM(a) #a,
#include "ed/inc/musr_tra.itm"
#undef ITEM
   };

   STACK_CHK ();

   if (tra < NELEM (as))
   {
      s = as[tra];
   }

   return s;
}

/* ---------------------------------------------------------------------
   MUSR_init()
   ---------------------------------------------------------------------
   Creates and initializes the C-object
   ---------------------------------------------------------------------
   E :
   S : Context
   --------------------------------------------------------------------- */
sMUSR *MUSR_init (uint sts_off)
{
   sMUSR *this = malloc (sizeof *this);
   STACK_CHK ();

   if (this != NULL)
   {
      /* All to typed 0 */
      CLR (this, sMUSR);

      GARBAGE (this);

      {
         sPRIVATE *pPrivate = malloc (sizeof *pPrivate);

         if (pPrivate != NULL)
         {
            /* All to typed 0 */
            CLR (pPrivate, sPRIVATE);

            GARBAGE (pPrivate);

            this->pPrivate = pPrivate;

            clear_cli (pPrivate);

            pPrivate->sts = (uint) - 1;
            pPrivate->sts_off = sts_off;
            pPrivate->pfOut = NULL;
            pPrivate->pDataOut = NULL;
            pPrivate->cb_ret = -1;

            pPrivate->first = 0;
            pPrivate->last = 1;
            pPrivate->all_on = 0;
            pPrivate->all_off = 0;

            this->last_err = MUSR_ERR_UNDEFINED;
         }
         else
         {
            MUSR_end (this);
            this = NULL;
         }
      }
   }
   return this;
}

/* ---------------------------------------------------------------------
   MUSR_end()
   ---------------------------------------------------------------------
   Suppresses the C-object
   ---------------------------------------------------------------------
   E : Context
   S :
   --------------------------------------------------------------------- */
void MUSR_end (sMUSR * const this)
{
   STACK_CHK ();

   if (this != NULL)
   {
      sPRIVATE *pPrivate = this->pPrivate;

      if (pPrivate != NULL)
      {
         /* suppress code ... */

         free (pPrivate);
         this->pPrivate = NULL;
      }
      free (this);
   }
}

/* ---------------------------------------------------------------------
   MUSR_install_out()
   ---------------------------------------------------------------------
   Installs a callback
   ---------------------------------------------------------------------
   E : Context
   S :
   --------------------------------------------------------------------- */
eMUSR_ERR MUSR_install_out (sMUSR * const this, fMUSR_OUT * pf, void *pData)
{
   eMUSR_ERR ret;
   STACK_CHK ();

   if (this != NULL)
   {
      sPRIVATE *pPrivate = this->pPrivate;

      if (pPrivate != NULL)
      {
         /* installs callback */

         pPrivate->pfOut = pf;
         pPrivate->pDataOut = pData;
         ret = MUSR_OK;
      }
      else
      {
         ret = MUSR_ERR_CONTEXT;
         this->last_err = ret;
      }
   }
   else
   {
      ret = MUSR_ERR_CONTEXT;
   }

   return ret;
}

/* ---------------------------------------------------------------------
   MUSR_set_first()
   ---------------------------------------------------------------------
   sets the 'first' flag (default 0)
   ---------------------------------------------------------------------
   E : Context
   E : first status 0|1
   S :
   --------------------------------------------------------------------- */
eMUSR_ERR MUSR_set_first (sMUSR * const this, int first)
{
   eMUSR_ERR ret;
   STACK_CHK ();

   if (this != NULL)
   {
      sPRIVATE *pPrivate = this->pPrivate;

      if (pPrivate != NULL)
      {
         pPrivate->first = first;
         ret = MUSR_OK;
      }
      else
      {
         ret = MUSR_ERR_CONTEXT;
         this->last_err = ret;
      }
   }
   else
   {
      ret = MUSR_ERR_CONTEXT;
   }

   return ret;
}

/* ---------------------------------------------------------------------
   MUSR_set_last()
   ---------------------------------------------------------------------
   sets the 'last' flag (default 0)
   ---------------------------------------------------------------------
   E : Context
   E : last status 0|1
   S :
   --------------------------------------------------------------------- */
eMUSR_ERR MUSR_set_last (sMUSR * const this, int last)
{
   eMUSR_ERR ret;
   STACK_CHK ();

   if (this != NULL)
   {
      sPRIVATE *pPrivate = this->pPrivate;

      if (pPrivate != NULL)
      {
         pPrivate->last = last;
         ret = MUSR_OK;
      }
      else
      {
         ret = MUSR_ERR_CONTEXT;
         this->last_err = ret;
      }
   }
   else
   {
      ret = MUSR_ERR_CONTEXT;
   }
   return ret;
}

/* ---------------------------------------------------------------------
   MUSR_set_all_on()
   ---------------------------------------------------------------------
   sets the 'all_on' flag
   ---------------------------------------------------------------------
   E : Context
   E : all_on status 0|1
   S :
   --------------------------------------------------------------------- */
eMUSR_ERR MUSR_set_all_on (sMUSR * const this, int all_on)
{
   eMUSR_ERR ret;
   STACK_CHK ();

   if (this != NULL)
   {
      sPRIVATE *pPrivate = this->pPrivate;

      if (pPrivate != NULL)
      {
         pPrivate->all_on = all_on;
         ret = MUSR_OK;
      }
      else
      {
         ret = MUSR_ERR_CONTEXT;
         this->last_err = ret;
      }
   }
   else
   {
      ret = MUSR_ERR_CONTEXT;
   }

   return ret;
}

/* ---------------------------------------------------------------------
   MUSR_set_all_off()
   ---------------------------------------------------------------------
   sets the 'all_off' flag
   ---------------------------------------------------------------------
   E : Context
   E : all status 0|1
   S :
   --------------------------------------------------------------------- */
eMUSR_ERR MUSR_set_all_off (sMUSR * const this, int all_off)
{
   eMUSR_ERR ret;
   STACK_CHK ();

   if (this != NULL)
   {
      sPRIVATE *pPrivate = this->pPrivate;

      if (pPrivate != NULL)
      {
         pPrivate->all_off = all_off;
         ret = MUSR_OK;
      }
      else
      {
         ret = MUSR_ERR_CONTEXT;
         this->last_err = ret;
      }
   }
   else
   {
      ret = MUSR_ERR_CONTEXT;
   }

   return ret;
}

/* ---------------------------------------------------------------------
   MUSR_errno()
   ---------------------------------------------------------------------
   Returns the current error code
   ---------------------------------------------------------------------
   E : Context
   S :
   --------------------------------------------------------------------- */
eMUSR_ERR MUSR_errno (sMUSR const *const this)
{
   eMUSR_ERR ret = -1;
   STACK_CHK ();

   if (this != NULL)
   {
      ret = this->last_err;
   }

   return ret;
}

/* ---------------------------------------------------------------------
   MUSR_cb_ret()
   ---------------------------------------------------------------------
   Returns the last callback return value
   -1 = invalid
   ---------------------------------------------------------------------
   E : Context
   S : Last callback return value
   --------------------------------------------------------------------- */
int MUSR_cb_ret (sMUSR const *this)
{
   int ret;
   STACK_CHK ();

   if (this != NULL)
   {
      sPRIVATE *pPrivate = this->pPrivate;

      if (pPrivate != NULL)
      {
         ret = pPrivate->cb_ret;
      }
      else
      {
         ret = MUSR_ERR_CONTEXT;
      }
   }
   else
   {
      ret = MUSR_ERR_CONTEXT;
   }

   return ret;
}

/* ---------------------------------------------------------------------
   MUSR_apply()
   ---------------------------------------------------------------------
   MUSR activation
   ---------------------------------------------------------------------
   E : Context
   E : Status
   E : Id address
   S :
   --------------------------------------------------------------------- */
eMUSR_ERR MUSR_apply (sMUSR * this
                      ,uint sts
                      ,musr_cli_id_s * pid)
{
   eMUSR_ERR ret;
   STACK_CHK ();

   if (this != NULL)
   {
      sPRIVATE *pPrivate = this->pPrivate;

      if (pPrivate != NULL)
      {
         if (sts != pPrivate->sts_off)
         {
            /*  function code */
            pPrivate->cb_ret = -1;

            ret = on (pPrivate, sts, pid);
         }
         else
         {
            ret = MUSR_ERR_PARAM;
         }
      }
      else
      {
         ret = MUSR_ERR_CONTEXT;
      }

      if (ret != 0)
      {
         this->last_err = ret;
      }
   }
   else
   {
      ret = MUSR_ERR_CONTEXT;
   }

   return ret;
}

/* ---------------------------------------------------------------------
   MUSR_release()
   ---------------------------------------------------------------------
   MUSR deactivation
   ---------------------------------------------------------------------
   E : Context
   E : Id address or NULL to clear all
   S :
   --------------------------------------------------------------------- */
eMUSR_ERR MUSR_release (sMUSR * this
                        ,musr_cli_id_s * pid)
{
   eMUSR_ERR ret;
   STACK_CHK ();

   if (this != NULL)
   {
      sPRIVATE *pPrivate = this->pPrivate;

      if (pPrivate != NULL)
      {
         /*  function code */
         pPrivate->cb_ret = -1;

         ret = off (pPrivate, pid);
      }
      else
      {
         ret = MUSR_ERR_CONTEXT;
      }

      if (ret != 0)
      {
         this->last_err = ret;
      }
   }
   else
   {
      ret = MUSR_ERR_CONTEXT;
   }
   return ret;
}

/* ---------------------------------------------------------------------
   MUSR_refresh()
   ---------------------------------------------------------------------
   Forces the notification with current status
   ---------------------------------------------------------------------
   E : Context
   S :
   --------------------------------------------------------------------- */
eMUSR_ERR MUSR_refresh (sMUSR * this)
{
   eMUSR_ERR ret;
   STACK_CHK ();

   if (this != NULL)
   {
      sPRIVATE *pPrivate = this->pPrivate;

      if (pPrivate != NULL)
      {
         /*  function code */
         pPrivate->cb_ret = -1;

         ret = refresh (pPrivate);

         if (ret != 0)
         {
            this->last_err = ret;
         }
      }
      else
      {
         ret = MUSR_ERR_CONTEXT;
      }
   }
   else
   {
      ret = MUSR_ERR_CONTEXT;
   }
   return ret;
}

/* ---------------------------------------------------------------------
   MUSR_dbg_sts()
   ---------------------------------------------------------------------
   send status to stdout
   ---------------------------------------------------------------------
   E : Context
   S :
   --------------------------------------------------------------------- */
void MUSR_dbg_sts (sMUSR * this)
{
   STACK_CHK ();

#if DBG
   if (this != NULL)
   {
      sPRIVATE *pPrivate = this->pPrivate;

      if (pPrivate != NULL)
      {
         /*  function code */
         dbg_sts (pPrivate);
      }
   }
#else
   (void) this;
#endif
}

/* File generated by 'NEW.EXE' Ver 1.30 (c) ED 1998-2001 */
