/*******************************************************************
  uLan Utilities Library - C library of basic reusable constructions

  ul_evcbase.h	- event connectors

  (C) Copyright 2001-2003 by Pavel Pisa - Originator

  The uLan utilities library can be used, copied and modified under
  next licenses
    - GPL - GNU General Public License
    - LGPL - GNU Lesser General Public License
    - MPL - Mozilla Public License
    - and other licenses added by project originators
  Code can be modified and re-distributed under any combination
  of the above listed licenses. If contributor does not agree with
  some of the licenses, he/she can delete appropriate line.
  Warning, if you delete all lines, you are not allowed to
  distribute source code and/or binaries utilizing code.
  
  See files COPYING and README for details.

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

#ifndef _UL_EVCBASE_H
#define _UL_EVCBASE_H

#include <stdarg.h>

#include "ul_utdefs.h"
#include "ul_list.h"

#ifdef __cplusplus
extern "C" {
#endif

struct evc_link;
struct evc_tx_hub;
struct evc_rx_hub;

typedef int evc_rx_fnc_t(void);
typedef int evc_prop_fnc_t(struct evc_link *link, va_list args);

/**
 * struct evc_link - Event Connector Link
 * @src:	describes source of the event link, contains pointer
 *		to &evc_tx_hub_t and @peers links list
 * @dst:	determines destination of the event, it can be @standalone
 *		rx_fnc() function with with @context or &evc_tx_hub_t
 *		in the @multi case
 * @propagate:	pointer to the arguments propagation function,
 * @refcnt:	link reference counter
 * @recursive:	link can propagate could be invoked recursively,
 *		else recursive events are ignored by link
 * @blocked:	event propagation is blocked for the link,
 *		can be used by application
 * @ready:	link is ready and has purpose to live - it connects
 *		two active entities  
 * @dead:	link is dead and cannot propagate events
 * @delete_pend: link is being deleted, but it is taken simultaneously,
 *		delete has to wait for finish of the propagate and to moving
 *		to the next link
 * @malloced:	link has been malloced and should be automatically freed
 *		when referenc counts drop to zero
 * @standalone:	link is used for standalone function invocation
 * @tx_full_hub: @src points to the full hub structure
 * @rx_full_hub: @dst points to the full hub structure
 * @taken:	link is in middle of the propagation process
 *
 * The link delivers events from the source to the destination.
 * The link specific function propagate() is called for each link leading from
 * the hub activated by evc_tx_hub_emit() and evc_tx_hub_propagate().
 * The propagate function is responsible for parameters transformation before
 * invocation of standalone or destination hub rx_fnc() function.
 */
 
typedef struct evc_link {
  struct {
    ul_list_node_t peers;
    struct evc_tx_hub *hub;
  } src;
  union {
    struct {
      ul_list_node_t peers;
      struct evc_rx_hub *hub;
    } multi;
    struct {
      evc_rx_fnc_t *rx_fnc;
      void *context;
      void **weakptr;
    } standalone;
  } dst;
  evc_prop_fnc_t *propagate;
  int		refcnt;
  unsigned	recursive:1;
  unsigned	blocked:1;
  unsigned	ready:1;
  unsigned	dead:1;
  unsigned	delete_pend:1;
  unsigned	malloced:1;
  unsigned	standalone:1;
  unsigned	tx_full_hub:1;
  unsigned	rx_full_hub:1;
  short		taken;
} evc_link_t;

/**
 * struct evc_tx_hub - Event Transmit Hub
 * @links:	list of links outgoing from the hub
 */
typedef struct evc_tx_hub {
  ul_list_head_t links;
} evc_tx_hub_t;

/**
 * struct evc_rx_hub -  Event Receiving Hub
 * @links:	list of links incoming to the hub
 * @rx_fnc:	function invoked when event arives
 * @context:	context for rx_fnc()
 */
typedef struct evc_rx_hub {
  ul_list_head_t links;
  evc_rx_fnc_t *rx_fnc;
  void *context;
} evc_rx_hub_t;

evc_link_t *evc_link_new(void);
evc_link_t *evc_link_new_standalone(evc_rx_fnc_t *rx_fnc, void *context);
int evc_link_init(evc_link_t *link);
int evc_link_init_standalone(evc_link_t *link, evc_rx_fnc_t *rx_fnc,
                     void *context);
int evc_link_connect(evc_link_t *link, evc_tx_hub_t *src, evc_rx_hub_t *dst,
                     evc_prop_fnc_t *prop);
int evc_link_connect_standalone(evc_link_t *link, evc_tx_hub_t *src,
           evc_prop_fnc_t *prop);
int evc_link_delete(evc_link_t *link);
void evc_link_dispose(evc_link_t *link);

int evc_tx_hub_init(evc_tx_hub_t *hub);
void evc_tx_hub_done(evc_tx_hub_t *hub);
void evc_tx_hub_propagate(evc_tx_hub_t *hub, va_list args);
void evc_tx_hub_emit(evc_tx_hub_t *hub, ...);
int evc_tx_hub_eol_weakptr(evc_tx_hub_t *hub, void **weakptr);

int evc_rx_hub_init(evc_rx_hub_t *hub, evc_rx_fnc_t *rx_fnc, void *context);
void evc_rx_hub_done(evc_rx_hub_t *hub);

int evc_link_propagate_i_p_l(evc_link_t *link, va_list args);

/**
 * evc_link_inc_refcnt - Increment Link Reference Count
 * @link:	pointer to link
 */
static inline
void evc_link_inc_refcnt(evc_link_t *link)
{
  if(!link) return;
  if(link->refcnt >= 0) link->refcnt++;
}

/**
 * evc_link_dec_refcnt - Decrement Link Reference Count
 * @link:	pointer to link
 *
 * if the link reference count drops to 0, link is deleted
 * from hubs by evc_link_dispose() function and if @malloced
 * is sed, link memory is disposed by free().
 * Special handlink can be achieved if propagate() returns non-zero
 * value if called with @ded link.
 */
static inline
void evc_link_dec_refcnt(evc_link_t *link)
{
  if(!link) return;
  if(link->refcnt > 0) link->refcnt--;
  if(!link->refcnt) evc_link_dispose(link);
}

#ifdef __cplusplus
} /* extern "C"*/
#endif


#endif /* _UL_EVCBASE_H */
