/* BTP library - Banana Tree Protocol
 * Copyright (C) 1999-2001  The Regents of the University of Michigan
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the 
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA  02111-1307, USA.
 */


#include "b_conn_handler.h"
#include "btp_debug.h"
#include "util.h"



typedef struct _BConnHandler
{
  BConn* conn;

  gint	type;
  gint	subtype;

  BConnHandlerReceiveFunc receive;

  BConnHandlerTimeoutFunc timeout;
  guint timer;

  gpointer user_data;

} BConnHandler;


static BConnHandler* b_conn_handler_get (BConn* conn, gint type, gint subtype, gpointer user_data);
static gboolean	handler_timeout (gpointer data);



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



void
b_conn_handler_add (BConn* conn, gint type, gint subtype, 
		    BConnHandlerReceiveFunc receive, gpointer user_data)
{
  BTPP (9, "HANDLER ADD %p, %d, %d, %p\n", conn, type, subtype, receive);

  b_conn_handler_add_full (conn, type, subtype, receive, NULL, 0, user_data);
}


void
b_conn_handler_add_full (BConn* conn, gint type, gint subtype, 
			 BConnHandlerReceiveFunc receive, 
			 BConnHandlerTimeoutFunc timeout, guint time,
			 gpointer user_data)
{
  BConnHandler* handler;

  g_return_if_fail (conn);
  g_return_if_fail (receive);

  BTPP (9, "HANDLER ADD FULL %p, %d, %d, %p\n", conn, type, subtype, receive);

  handler = b_conn_handler_get (conn, type, subtype, user_data);
  if (handler)
    {
      /* Make sure handler is identical */
      g_return_if_fail (handler->receive == receive);
      g_return_if_fail (handler->timeout == timeout);

      /* But don't allow an identical handler */
      return;
    }

  handler = g_new0 (BConnHandler, 1);
  handler->conn = conn;
  handler->type = type;
  handler->subtype = subtype;
  handler->receive = receive;
  handler->timeout = timeout;
  handler->user_data = user_data;

  if (timeout)
    handler->timer = g_timeout_add (time, handler_timeout, handler);

  conn->handlers = g_slist_prepend (conn->handlers, handler);
}



void
b_conn_handler_remove (BConn* conn, gint type, gint subtype, gpointer user_data)
{
  BConnHandler* handler;

  BTPP (9, "HANDLER REMOVE %p, %d, %d\n", conn, type, subtype);

  handler = b_conn_handler_get (conn, type, subtype, user_data);

  if (handler)
    {
      conn->handlers = g_slist_remove (conn->handlers, handler);

      if (handler->timer)
	g_source_remove (handler->timer);

      g_free (handler);
    }
  else
    BTPP (9, "HANDLER REMOVE: NOT HERE\n");
}



void
b_conn_handler_dispatch (BConn* conn, BPacket* packet)
{
  GSList* i;
  GSList* handlers;
  gboolean found_handler = FALSE;

  g_return_if_fail (conn);
  g_return_if_fail (!conn->should_destroy);
  g_return_if_fail (packet);

  BTPP (9, "HANDLER DISPATCH %p, %s, %d\n", conn, 
	b_packet_type_to_string(packet->type), packet->subtype);

  handlers = g_slist_copy (conn->handlers);

  for (i = handlers; i != NULL; i = i->next)
    {
      BConnHandler* handler = (BConnHandler*) i->data;

      g_return_if_fail (handler);

      /* Skip if the handler is no longer in the handler list */
      if (!g_slist_find (conn->handlers, handler))
	continue;

      /* Dispatch if this is the appropriate handler. */
      if ((handler->type == -1 || handler->type == packet->type) &&
	  (handler->subtype == -1 || handler->subtype == packet->subtype))
	{
	  found_handler = TRUE;

	  /* Cancel the timeout */
	  if (handler->timer)
	    {
	      g_source_remove (handler->timer);
	      handler->timer = 0;
	    }

	  /* Call the function. */
	  BTPP (9, "HANDLER DISPATCH to %p\n", handler->receive);
	  handler->receive (conn, packet, handler->user_data);

	  /* If the conn has been destoryed, return */
	  if (conn->should_destroy)
	    return;
	}
    }

  g_slist_free (handlers);

  if (!found_handler)
    {
      g_warning ("Couldn't find handler for packet of type %s, %d\n", 
		 b_packet_type_to_string(packet->type), packet->subtype);
    }
}


void
b_conn_handler_remove_all (BConn* conn)
{
  GSList* i;

  for (i = conn->handlers; i != NULL; i = i->next)
    {
        BConnHandler* handler = (BConnHandler*) i->data;

	if (handler->timer)
	  g_source_remove (handler->timer);

	g_free (handler);
    }
  
  g_slist_free (conn->handlers);
  conn->handlers = NULL;
}


static BConnHandler*
b_conn_handler_get (BConn* conn, gint type, gint subtype, gpointer user_data)
{
  GSList* i;

  for (i = conn->handlers; i != NULL; i = i->next)
    {
      BConnHandler* handler = (BConnHandler*) i->data;

      if (handler->type == type && 
	  handler->subtype == subtype && 
	  handler->user_data == user_data)
	return handler;
    }

  return NULL;
}


static gboolean
handler_timeout (gpointer data)
{
  BConnHandler* handler = (BConnHandler*) data;

/*    fprintf (stderr, "handler timeout for packet of type %s, %d\n",  */
/*  	   b_packet_type_to_string(handler->type), handler->subtype); */
/*    b_conn_print (stderr, handler->conn); */
/*    fprintf (stderr, "\n"); */

  /* Clear timer */
  handler->timer = 0;

  /* Remove from handlers */
  handler->conn->handlers = g_slist_remove (handler->conn->handlers, handler);

  /* Call timeout function */
  handler->timeout (handler->conn, handler->type, handler->subtype, 
		    handler->user_data);

  g_free (handler);

  return FALSE;
}


void
b_conn_handler_print (FILE* file, BConn* conn)
{
  GSList* i;

  for (i = conn->handlers; i != NULL; i = i->next)
    {
      BConnHandler* handler = (BConnHandler*) i->data;
      fprintf (file, "(%s, %d, %s) ",
	       b_packet_type_to_string(handler->type), 
	       handler->subtype,
	       handler->timer?"timer":"no timer");
    }
}


syntax highlighted by Code2HTML, v. 0.9.1