/* 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 <gnet/gnet.h>
#include <stdlib.h>

#include "util.h"

#include "btp.h"
#include "btp_node.h"
#include "btp_proto.h"
#include "b_packet.h"
#include "btp_debug.h"


static void mcast_read_cb (BtpTree* tree, BtpNode* from_node, 
			   guint8* buffer, guint length, gpointer user_data);
static void ucast_read_cb (BtpTree* tree, BtpNode* from_node, 
			   guint8* buffer, guint length, gpointer user_data);
static void error_cb (BtpTree* tree, gpointer user_data);

static guint    btp_max_degree = BTP_MAX_DEGREE;
static gboolean btp_follow_nodes = TRUE;
static gboolean btp_use_shortcuts = FALSE;


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



/**

   Create a new BTP tree.

   Last checked: 	2001-5-7 DAH

 */
Btp* 
btp_create (BPeer* peer, const gchar* name)
{
  GURL*	   url;
  Btp*     btp  = NULL;
  BtpTree* tree = NULL;

  g_return_val_if_fail (peer, NULL);
  g_return_val_if_fail (name, NULL);

  BTPP (1, "btp_create %s\n", name);

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

  /* Check if tree already exists */
  if (btp_has (peer, name))
    return NULL;

  /* Create URL */
  if (*name == '/')
    url = gnet_url_new_fields ("btp", peer->hostname, peer->port, name);
  else
    {    
      url = gnet_url_new_fields ("btp", peer->hostname, peer->port, NULL);
      url->resource = g_strconcat ("/", name, NULL);
    }

  /* Create tree */
  tree = btp_tree_new (peer, url);

  /* Set initial sequence number */
  tree->group.seq_num = rand() % G_MAXUINT16;
  tree->group.source_id = rand() % G_MAXUINT32;

  /* Set default policies */
  tree->max_degree = btp_max_degree;
  tree->follow_nodes = btp_follow_nodes;
  tree->use_shortcuts = btp_use_shortcuts;

  /* Add me to tree */
  tree->me = btp_node_new (tree, peer->hostname, peer->port, NULL);

  /* Make me root */
  tree->root = tree->me;
  tree->parent = NULL;

  /* Add peer handler */
  b_peer_add_handler (peer, url->resource, btp_peer_handler, tree);

  /* Create BTP */
  btp = g_new0 (Btp, 1);
  btp->tree = tree;

  /* Set callbacks */
  tree->mcast_read_func  	= mcast_read_cb;
  tree->ucast_read_func  	= ucast_read_cb;
  tree->error_func	 	= error_cb;
  tree->mcast_read_user_data 	= btp;
  tree->ucast_read_user_data 	= btp;
  tree->error_user_data      	= btp;

  /* Initialize */
  btp_proto_init (tree);

  return btp;
}



/**

   Join an existing BtpTree.

   Last checked: 	2001-5-7 DAH

 */
Btp*  
btp_join (BPeer* peer, const GURL* url)
{
  Btp*     btp;
  BtpTree* tree;
  GURL*    gurl;

  g_return_val_if_fail (peer, NULL);
  g_return_val_if_fail (url, NULL);

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

  /* Check if we have this tree.  b_peer would get confused because it
     doesn't know if an incoming connection is going to us, or to the
     root.  TODO: Allow this. */
  if (btp_has (peer, url->resource?url->resource:"/"))
    return NULL;

  gurl = gnet_url_clone (url); /* do not delete */
  if (gurl->port == 0)
    gurl->port = BTP_PORT;

  /* Create tree */
  tree = btp_tree_new (peer, gurl);
  
  /* Set initial sequence number */
  tree->group.seq_num = rand() % G_MAXUINT16;
  tree->group.source_id = rand() % G_MAXUINT32;

  /* Set default policies */
  tree->max_degree = btp_max_degree;
  tree->follow_nodes = btp_follow_nodes;
  tree->use_shortcuts = btp_use_shortcuts;

  /* Create me */
  tree->me = btp_node_new (tree, peer->hostname, peer->port, NULL);

  /* Create root and connect */
  tree->root = btp_node_new (tree, gurl->hostname, gurl->port, NULL);
  tree->parent = tree->root;
  b_conn_connect (tree->root->conn);

  /* Add peer handler */
  b_peer_add_handler (peer, url->resource?url->resource:"/", 
		      btp_peer_handler, tree);

  /* Create BTP */
  btp = g_new0 (Btp, 1);
  btp->tree = tree;

  /* Set callbacks */
  tree->mcast_read_func  	= mcast_read_cb;
  tree->ucast_read_func  	= ucast_read_cb;
  tree->error_func	 	= error_cb;
  tree->mcast_read_user_data 	= btp;
  tree->ucast_read_user_data 	= btp;
  tree->error_user_data      	= btp;

  /* Initialize */
  btp_proto_init (tree);

  return btp;
}



/**


 */
Btp*        
btp_join_passive (BPeer* peer, const GURL* url)
{
  Btp* btp;

  g_return_val_if_fail (peer, NULL);
  g_return_val_if_fail (url, NULL);

  /* ******************** */
  
  btp = btp_join (peer, url);
  if (!btp) return NULL;
  btp->tree->passive = TRUE;

  /* We have no parent in passive mode */
  btp->tree->parent = NULL;

  return btp;
}



void
btp_leave (Btp* btp)
{
  BGroup* group;

  g_return_if_fail (btp);

  group = &btp->tree->group;
  b_peer_remove_handler (group->peer, group->url->resource);

  gnet_url_delete (group->url);

  btp_tree_delete (btp->tree);

  memset (btp, 0, sizeof(*btp));
  g_free (btp);
}


gboolean
btp_has (BPeer* peer, const gchar* name)
{
  gchar*   tname;
  gboolean rv;

  g_return_val_if_fail (peer, FALSE);
  g_return_val_if_fail (name, FALSE);

  /* Clean up name so it starts with / */
  if (*name == '/')
    tname = g_strdup (name);
  else
    tname = g_strconcat ("/", name, NULL);

  rv = b_peer_has_handler (peer, tname);
  g_free (tname);
  return rv;
}



guint
btp_get_max_degree (Btp* btp)
{
  if (btp)
    return btp->tree->max_degree;
  else
    return btp_max_degree;
}


void
btp_set_max_degree (Btp* btp, guint max_degree)
{
  if (btp)
    btp->tree->max_degree = max_degree;
  else
    btp_max_degree = max_degree;
}


gboolean
btp_get_follow_nodes (Btp* btp)
{
  if (btp)
    return btp->tree->follow_nodes;
  else
    return btp_follow_nodes;
}


void
btp_set_follow_nodes (Btp* btp, gboolean follow_nodes)
{
  if (btp)
    btp->tree->follow_nodes = follow_nodes;
  else
    btp_follow_nodes = follow_nodes;
}


gboolean
btp_get_use_shortcuts (Btp* btp)
{
  if (btp)
    return btp->tree->use_shortcuts;
  else
    return btp_use_shortcuts;
}


void
btp_set_use_shortcuts (Btp* btp, gboolean use_shortcuts)
{
  if (btp)
    btp->tree->use_shortcuts = use_shortcuts;
  else
    btp_use_shortcuts = use_shortcuts;
}


GURL* 
btp_get_url (Btp* btp)
{
  g_return_val_if_fail (btp, NULL);

  return btp->tree->group.url;
}


gboolean
btp_is_up (const Btp* btp)
{
  if (btp && btp->tree)
    {
      if (btp_tree_is_root (btp->tree->me))
	return TRUE;
      
      if (btp->tree->parent &&
	  btp->tree->parent->conn &&
	  b_conn_is_connected (btp->tree->parent->conn))
	return TRUE;
    }

  return FALSE;
}


void
btp_print (FILE* file, BPeer* peer)
{
  GSList* trees;
  GSList* i;

  g_return_if_fail (file);
  g_return_if_fail (peer);

  trees = b_peer_get_handler_data (peer);
  fprintf (file, "BtpPeer\n");
  for (i = trees; i != NULL; i = i->next)
    btp_tree_print (file, (BtpTree*) i->data);
  g_slist_free (trees);
}


void
btp_send (Btp* btp, const guint8* buffer, guint16 length)
{
  g_return_if_fail (btp);

  btp_tree_send_mcast (btp->tree, buffer, length);
}



/* **************************************** */
/* Callbacks */


static void
mcast_read_cb (BtpTree* tree, BtpNode* from_node, 
	       guint8* buffer, guint length, gpointer user_data)
{
  Btp* btp = (Btp*) user_data;

  /* Pass packet up */
  if (btp->packet_func)
    (btp->packet_func)(btp, buffer, length, btp->packet_user_data);
}


static void
ucast_read_cb (BtpTree* tree, BtpNode* from_node, 
	       guint8* buffer, guint length, gpointer user_data)
{
  Btp* btp = (Btp*) user_data;

  /* Pass packet up */
  if (btp->packet_func)
    (btp->packet_func)(btp, buffer, length, btp->packet_user_data);
}


static void 	
error_cb (BtpTree* tree, gpointer user_data)
{
  Btp* btp = (Btp*) user_data;

  /* Pass error up */
  if (btp->error_func)
    (btp->error_func)(btp, btp->error_user_data);
}


syntax highlighted by Code2HTML, v. 0.9.1