/* 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 "btp_node.h"
#include "btp_debug.h"

static void node_delete (BtpNode* node);


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

BtpTree*
btp_tree_new (BPeer* peer, GURL* url)
{
  BtpTree* tree;

  tree = g_new0 (BtpTree, 1);
  tree->group.peer = peer;
  tree->group.url = url;

  return tree;

}




/*

  Delete the BTP tree.  Nodes may still be around at the end of this
  call, but we the only callbacks we should get are write callbacks.

  Last checked:	???

 */
void
btp_tree_delete (BtpTree* tree)
{
  GSList* i;

  g_return_if_fail (tree);
  g_return_if_fail (!tree->is_deleting);
  BTPP (1, "btp_tree_delete %p\n", tree);

  tree->is_deleting = TRUE;

  /* Delete the children and siblings lists */
  g_slist_free (tree->children);
  g_slist_free (tree->siblings);
  tree->children = NULL;
  tree->siblings = NULL;
  tree->shortcuts = NULL;

  /* Delete all nodes.  Note that the nodes may not be deleted until
     their out queues are empty.  Send packet needs 'me' to determine
     the port, so we delete it last. */
  for (i = tree->nodes; i != NULL; )
    {
      BtpNode* node = (BtpNode*) i->data;
      i = i->next;	/* node might be deleted */

      g_return_if_fail(node);

      if (node != tree->me)
	node_delete (node);
    }
  node_delete (tree->me);

  g_slist_free(tree->nodes);	
  tree->nodes = NULL;

  if (tree->switch_timeout)
    g_source_remove(tree->switch_timeout);

  if (tree->request_neighbors_timeout)
    g_source_remove(tree->request_neighbors_timeout);

  if (tree->find_new_parent_timeout)
    g_source_remove(tree->find_new_parent_timeout);

  if (tree->find_new_shortcut_timeout)
    g_source_remove(tree->find_new_shortcut_timeout);

  if (tree->send_node_info_timeout)
    g_source_remove(tree->send_node_info_timeout);

  /* Zero myself out to be safe */
  memset(tree, 0, sizeof(*tree));

  g_free(tree);
}


static void
node_delete (BtpNode* node)
{
  gboolean delete_done;

  /* If the delete is delayed (so that the connection can be closed,
     then remove all references to the tree/group. */
  delete_done = btp_node_delete(node);
  if (!delete_done)
    {
      node->tree = NULL;
      if (node->conn)
	node->conn->group = NULL;
    }
}



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

void
btp_tree_print (FILE* file, BtpTree* tree)
{
  gchar* url_str;
  GSList* i;

  g_return_if_fail (tree);

  fprintf (file, "\nBtpTree\n");
  url_str = gnet_url_get_nice_string(tree->group.url);
  fprintf (file, "url:        %s\n", url_str);
  g_free (url_str);
  fprintf (file, "passive:    %d\n", tree->passive);
  fprintf (file, "sw-to:      %d\n", tree->switch_timeout != 0);
  fprintf (file, "rn-to:      %d\n", tree->request_neighbors_timeout != 0);
  fprintf (file, "fnp-to:     %d\n", tree->find_new_parent_timeout != 0);
  fprintf (file, "fns-to:     %d\n", tree->find_new_shortcut_timeout != 0);
  fprintf (file, "sni-to:     %d\n", tree->send_node_info_timeout != 0);
  fprintf (file, "\n");

  
  fprintf (file, "ME:\n");
  btp_node_print (file, tree->me);
  fprintf (file, "\nROOT:\n");
  btp_node_print (file, tree->root);
  fprintf (file, "\nPARENT:\n");
  btp_node_print (file, tree->parent);
  fprintf (file, "\nGPARENT:\n");
  btp_node_print (file, tree->gparent);
  fprintf (file, "\nNEW_PARENT:\n");
  btp_node_print (file, tree->new_parent);
  fprintf (file, "\n");

  fprintf (file, "CHILDREN: \n");
  for (i = tree->children; i != NULL; i = i->next)
    {
      btp_node_print (file, (BtpNode*) i->data);
      fprintf (file, "\n");
    }
  fprintf (file, "\n");

  fprintf (file, "SIBLINGS: \n");
  for (i = tree->siblings; i != NULL; i = i->next)
    {
      btp_node_print (file, (BtpNode*) i->data);
      fprintf (file, "\n");
    }
  fprintf (file, "\n");

  fprintf (file, "SHORTCUTS: \n");
  for (i = tree->shortcuts; i != NULL; i = i->next)
    {
      btp_node_print (file, (BtpNode*) i->data);
      fprintf (file, "\n");
    }
  fprintf (file, "\n");

  fprintf (file, "NODES: \n");
  for (i = tree->nodes; i != NULL; i = i->next)
    {
      btp_node_print (file, (BtpNode*) i->data);
      fprintf (file, "\n");
    }
  fprintf (file, "\n");
}


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


gboolean
btp_tree_is_me (BtpNode* node)
{
  g_return_val_if_fail (node, FALSE);
  g_return_val_if_fail (node->tree, FALSE);

  return (node->tree->me == node);
}


gboolean
btp_tree_is_root (BtpNode* node)
{
  g_return_val_if_fail (node, FALSE);
  g_return_val_if_fail (node->tree, FALSE);

  return (node->tree->root == node);
}


gboolean
btp_tree_is_parent (BtpNode* node)
{
  g_return_val_if_fail (node, FALSE);
  g_return_val_if_fail (node->tree, FALSE);

  return (node->tree->parent == node);
}


gboolean
btp_tree_is_gparent (BtpNode* node)
{
  g_return_val_if_fail (node, FALSE);
  g_return_val_if_fail (node->tree, FALSE);

  return (node->tree->gparent == node);
}


gboolean
btp_tree_is_sibling (BtpNode* node)
{
  g_return_val_if_fail (node, FALSE);
  g_return_val_if_fail (node->tree, FALSE);

  return (g_slist_find(node->tree->siblings, node) != NULL);
}


gboolean
btp_tree_is_child (BtpNode* node)
{
  g_return_val_if_fail (node, FALSE);
  g_return_val_if_fail (node->tree, FALSE);

  return (g_slist_find(node->tree->children, node) != NULL);
}


gboolean
btp_tree_is_shortcut (BtpNode* node)
{
  g_return_val_if_fail (node, FALSE);
  g_return_val_if_fail (node->tree, FALSE);

  return (g_slist_find(node->tree->shortcuts, node) != NULL);
}


gboolean
btp_tree_is_new_parent (BtpNode* node)
{
  g_return_val_if_fail (node, FALSE);
  g_return_val_if_fail (node->tree, FALSE);

  return (node->tree->new_parent == node);
}


gboolean
btp_tree_is_one_hop (BtpNode* node)
{
  g_return_val_if_fail (node, FALSE);
  g_return_val_if_fail (node->tree, FALSE);

  return (node == node->tree->parent ||
	  g_slist_find(node->tree->children, node) != NULL);
}


gboolean
btp_tree_is_two_hop (BtpNode* node)
{
  g_return_val_if_fail (node, FALSE);
  g_return_val_if_fail (node->tree, FALSE);

  return (node == node->tree->gparent ||
	  g_slist_find(node->tree->siblings, node) != NULL);
}


gboolean
btp_tree_is_tree_neighbor (BtpNode* node)
{
  if ( btp_tree_is_child (node) ||
       btp_tree_is_parent (node) )
    return TRUE;

  return FALSE;
}


gboolean
btp_tree_is_mesh_neighbor (BtpNode* node)
{
  if ( btp_tree_is_tree_neighbor(node) || 
       btp_tree_is_shortcut(node) )
    return TRUE;

  return FALSE;
}




gchar*   
btp_tree_relation_to_string (BtpNode* node)
{
  static gchar* relation_to_string[] = 
  {
    "me",
    "root",
    "parent",
    "gparent",
    "child",
    "sibling",
    "none"
  };

  if (btp_tree_is_me(node))
    return relation_to_string[0];
  else if (btp_tree_is_root(node))
    return relation_to_string[1];
  else if (btp_tree_is_parent(node))
    return relation_to_string[2];
  else if (btp_tree_is_gparent(node))
    return relation_to_string[3];
  else if (btp_tree_is_child(node))
    return relation_to_string[4];
  else if (btp_tree_is_sibling(node))
    return relation_to_string[5];

  return relation_to_string[6];
}



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

void
btp_tree_add_sibling (BtpNode* node)
{
  g_return_if_fail (node);
  g_return_if_fail (node->tree);
  g_return_if_fail (node != node->tree->me);

  if (!g_slist_find(node->tree->siblings, node))
    node->tree->siblings = g_slist_prepend (node->tree->siblings, node);
}


void
btp_tree_remove_sibling (BtpNode* node)
{
  g_return_if_fail (node);
  g_return_if_fail (node->tree);

  node->tree->siblings = g_slist_remove (node->tree->siblings, node);
}


void	
btp_tree_add_child (BtpNode* node)
{
  g_return_if_fail (node);
  g_return_if_fail (node->tree);
  g_return_if_fail (node != node->tree->me);

  if (!g_slist_find(node->tree->children, node))
    node->tree->children = g_slist_prepend (node->tree->children, node);
}


void
btp_tree_remove_child (BtpNode* node)
{
  g_return_if_fail (node);
  g_return_if_fail (node->tree);

  node->tree->children = g_slist_remove (node->tree->children, node);
}


void
btp_tree_add_shortcut (BtpNode* node)
{
  g_return_if_fail (node);
  g_return_if_fail (node->tree);
  g_return_if_fail (node != node->tree->me);

  if (!g_slist_find(node->tree->shortcuts, node))
    node->tree->shortcuts = g_slist_prepend (node->tree->shortcuts, node);
}


void
btp_tree_remove_shortcut (BtpNode* node)
{
  g_return_if_fail (node);
  g_return_if_fail (node->tree);

  node->tree->shortcuts = g_slist_remove (node->tree->shortcuts, node);
}


guint
btp_tree_degree (BtpTree* tree)
{
  guint degree = 0;

  g_return_val_if_fail (tree, 0);

  if (tree->parent)
    degree++;

  degree += g_slist_length (tree->children);
  degree += g_slist_length (tree->shortcuts);

  return degree;
}


guint
btp_tree_parent_degree (BtpTree* tree)
{
  g_return_val_if_fail (tree, 0);

  if (!tree->parent)
    return 0;

  return (tree->root != tree->parent)?1:0 /* parent's parent */ + 
    1 /* me */ + 
    g_slist_length (tree->siblings);
}


gboolean
btp_tree_is_node (BtpNode* node)
{
  g_return_val_if_fail (node, FALSE);
  g_return_val_if_fail (node->tree, FALSE);

  return (g_slist_find(node->tree->nodes, node) != NULL);
}


void	
btp_tree_add_node (BtpNode* node)
{
  g_return_if_fail (node);
  g_return_if_fail (node->tree);

  if (!g_slist_find(node->tree->nodes, node))
    node->tree->nodes = g_slist_prepend (node->tree->nodes, node);
}


void
btp_tree_remove_node (BtpNode* node)
{
  g_return_if_fail (node);
  g_return_if_fail (node->tree);

  node->tree->nodes = g_slist_remove (node->tree->nodes, node);
}


BtpNode* 
btp_tree_find_node (BtpTree* tree, gchar* hostname, gint port)
{
  GSList* i;

  g_return_val_if_fail (tree, NULL);
  g_return_val_if_fail (hostname, NULL);


  for (i = tree->nodes; i != NULL; i = i->next)
    {
      BtpNode* node = (BtpNode*) i->data;

      g_return_val_if_fail (node, NULL);
      g_return_val_if_fail (node->hostname, NULL);

      if (port == node->port &&
	  strcmp(hostname, node->hostname) == 0)
	return node;
    }

  return NULL;
}


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


void
btp_tree_send_mcast (BtpTree* tree, const void* buffer, guint16 length)
{
  BPacket* pkt;

  g_return_if_fail (tree);
  g_return_if_fail (buffer);

  pkt = b_packet_new_mcast (NULL, buffer, length);
  pkt->seq = g_htons(tree->group.seq_num++);
  pkt->source_id = g_htonl(tree->group.source_id);

  btp_tree_forward_mcast (tree, NULL, pkt);

  b_packet_delete (pkt);
}


void
btp_tree_forward_mcast (BtpTree* tree, BtpNode* not_node, BPacket* pkt)
{
  GSList* i;

  g_return_if_fail (tree);
  g_return_if_fail (pkt);

  /* Forward to other nodes */
# define SEND_PKT(N)	do{ \
    if ((N) != not_node) {				\
      BPacket* cpkt = b_packet_clone (pkt); 		\
      g_assert ((N)->conn);				\
      cpkt->id = (N)->conn->out_conn_id; 		\
      b_conn_send_packet ((N)->conn, cpkt); } } while(0)

  /* Send to parent */
  if (tree->parent)
    SEND_PKT (tree->parent);

  /* Send to children */
  for (i = tree->children; i != NULL; i = i->next)
    SEND_PKT ((BtpNode*) i->data);

  /* Send to shortcuts */
  for (i = tree->shortcuts; i != NULL; i = i->next)
    SEND_PKT ((BtpNode*) i->data);

# undef SEND_PACKET  
}


void
btp_tree_send_ucast (BtpTree* tree, BtpNode* node, const void* buffer, guint16 length)
{
  BPacket* packet;

  g_return_if_fail (tree);
  g_return_if_fail (node);
  g_return_if_fail (buffer);

  packet = b_packet_new_ucast (node->conn, buffer, length);
  b_conn_send_packet (node->conn, packet);
}


syntax highlighted by Code2HTML, v. 0.9.1