/*
 * Copyright (c) 2004-2005 Endace Technology Ltd, Hamilton, New Zealand.
 * All rights reserved.
 *
 * This source code is proprietary to Endace Technology Limited and no part
 * of it may be redistributed, published or disclosed except as outlined in
 * the written contract supplied with this product.
 *
 * $Id: negation_tree.c 6096 2007-01-19 02:59:08Z lipi $
 */

/* File header. */
#include "negation_tree.h"

/* C Standard Library headers. */
#include <assert.h>
#include <stdlib.h>
#include <string.h>

/* CVS Header. */
static const char * const kNegationTreeCvsHeader __attribute__ ((unused)) = "$Id: negation_tree.c 6096 2007-01-19 02:59:08Z lipi $";

/**
 * Tree node
 *
 * This structure is used in the tree representing the set of
 * addresses to be searched.
 */
typedef struct TreeNode
{
	struct TreeNode* mLeft; /**< used for ones */
	struct TreeNode* mRight; /**< used for zeroes */
	struct TreeNode* mParent; /**< node above this one */
	
	uint8_t mIncluded;

} TreeNode, *TreeNodePtr;

typedef struct TreeHeader
{
	TreeNodePtr mRoot;
	NegationTreeListNodePtr mList;

} TreeHeader, *TreeHeaderPtr;


/* Internal routines. */
static TreeNodePtr new_tree_node(TreeNodePtr parent, uint8_t included);
static void dispose_tree_node(TreeNodePtr node);
static void display_path(TreeNodePtr node, FILE* outfile, char buffer[33], uint8_t level);
static void create_path_list(TreeHeaderPtr header, TreeNodePtr node, uint32_t value, uint8_t level);


/* Implementation of internal routines. */

/**
 * Create tree node
 *
 * Allocate memory for the node and set its parent
 *
 * \param parent    pointer to the parent of the new node
 * \param included  ???
 *
 * \return pointer to the new node
 */
static TreeNodePtr
new_tree_node(TreeNodePtr parent, uint8_t included)
{
	TreeNodePtr node = malloc(sizeof(TreeNode));

	memset(node, 0, sizeof(TreeNode));

	node->mParent = parent;
	node->mIncluded = included;

	return node;
}

/**
 * Dispose node
 *
 * Dispose the entire subtree starting from the node (recursive function).
 *
 * \param node pointer to the starting node of the subtree to be disposed
 */
static void
dispose_tree_node(TreeNodePtr node)
{
	node->mParent = NULL;
	if (node->mLeft)
	{
		dispose_tree_node(node->mLeft);
		node->mLeft = NULL;
	}

	if (node->mRight)
	{
		dispose_tree_node(node->mRight);
		node->mRight = NULL;
	}

	free(node);
}


/**
 * Display path
 *
 * Display the path starting from the given node. Print the included
 * nodes from the given level. Recursive function, the buffer is
 * supposed to be filled with dashes when the function is called first
 * time (outmost call of the recursion).
 *
 * \param node     pointer to the starting node of the subtree to be displayed
 * \param outfile  FILE pointer to write to
 * \param buffer   ternary (true/false/don't-care) bit pattern in ASCII
 * \param level    current recursion level
 */
static void
display_path(TreeNodePtr node, FILE* outfile, char buffer[33], uint8_t level)
{
	if ((node->mIncluded) && (level <= 32))
	{
        /* print the whole buffer if this is a leaf-node */
		if ((node->mLeft == NULL) && (node->mRight == NULL))
		{
			fprintf(outfile, "node included: %s\n", buffer);
		}
		else if (level < 32)
        /* add true/false/don't-care bit to the buffer and continue
         * evaluating one level deeper */
		{
			if (node->mLeft)
			{
				buffer[level] = '1';
				display_path(node->mLeft, outfile, buffer, 1 + level);
				buffer[level] = '-';
			}
			
			if (node->mRight)
			{
				buffer[level] = '0';
				display_path(node->mRight, outfile, buffer, 1 + level);
				buffer[level] = '-';
			}
		}
	}
}

/**
 * Create list of all paths of the tree
 *
 * Discover the tree and prepend all leaf-nodes found to the list of
 * the nodes.
 *
 * \param header pointer to the header of the tree
 * \param node   pointer to the current node (recursive function)
 * \param value  address belonging to the node
 * \param level  current recursion level
 */
static void
create_path_list(TreeHeaderPtr header, TreeNodePtr node, uint32_t value, uint8_t level)
{
	assert(level <= 32);

	if (node->mIncluded)
	{
		if ((node->mLeft == NULL) && (node->mRight == NULL)) /* leaf-node */
		{
			NegationTreeListNodePtr list_node = (NegationTreeListNodePtr) malloc(sizeof(NegationTreeListNode));
		    int index;

			memset(list_node, 0, sizeof(NegationTreeListNode));

			list_node->mask = 0;
            /* (partially) fill list-node's mask with ones, starting from MSB */
			for (index = 31; index >= level; index--)
			{
				list_node->mask |= (1 << index);
			}

			list_node->value = value;
			list_node->next = header->mList;
			header->mList = list_node;
		}
		else if (level)
		{
			if (node->mLeft)
			{
				value |= (1 << (level - 1));
				create_path_list(header, node->mLeft, value, level - 1);
				value &= ~(1 << (level - 1));
			}
			
			if (node->mRight)
			{
				create_path_list(header, node->mRight, value, level - 1);
			}
		}
	}
}


/**
 * Initialize negation tree
 *
 * Allocate memory for the root node and create its children (left and right).
 *
 * \return  pointer to the header of the tree
 */
NegationTreePtr
negation_tree_init(void)
{
	TreeHeaderPtr header = (TreeHeaderPtr) malloc(sizeof(TreeHeader));

	header->mList = NULL;
	header->mRoot = new_tree_node(NULL, 1);
	header->mRoot->mLeft = new_tree_node(header->mRoot, 1);
	header->mRoot->mRight = new_tree_node(header->mRoot, 1);

	return (NegationTreePtr) header;
}


/**
 * Dispose entire tree
 *
 * \param  pointer to the tree-header
 */
void
negation_tree_dispose(NegationTreePtr tree)
{
	TreeHeaderPtr header = (TreeHeaderPtr) tree;
	
	dispose_tree_node(header->mRoot);

	while (header->mList)
	{
		NegationTreeListNodePtr node = header->mList;

		header->mList = node->next;
		
		free(node);
	}

	free(header);
}

/**
 * Add a node to the negation tree
 *
 * Insert a new node with the given address/mask into the
 * tree. Traverse the tree to find the point of insertion, create
 * nodes and/or dispose unsued (don't-care) subtrees as necessary.
 * This will result in a tree containing the relevant NON-MATCHING
 * addresses only.
 *
 * The don't-care bits must start from the LSB and form a continuous
 * region. TODO: implement explicit check for this
 *
 * \param tree     pointer to the header of negation tree
 * \param address  IP address belongin to the node
 * \param mask     mask of don't care bits (zeroes indicate don't-care)
 */
void
negation_tree_add(NegationTreePtr tree, uint32_t address, uint32_t mask)
{
	TreeHeaderPtr header = (TreeHeaderPtr) tree;
	TreeNodePtr node = header->mRoot;
	int index;

    /* start searching from the left */
	for (index = 31; index > -1; index--)
	{
		uint32_t bit_mask = (1 << index);
		uint8_t last_bit = 0;

		if (index > 0)
		{
			uint32_t temp_bit_mask = 1 << (index - 1);
			if ((temp_bit_mask & mask) == 0)
			{
                /* quit searching right before the first zero bit of
                 * mask (i.e. the first don't-care bit) would have
                 * been reached */
				last_bit = 1;
			}
		}

		if (address & bit_mask)
		{
			/* Left branch. */
			node = node->mLeft;
		}
		else
		{
			/* Right branch. */
			node = node->mRight;
		}

		if (last_bit)
		{
			/* End of non-masked data. */
			node->mIncluded = 0;

            /* this is a leaf-node, dispose masked-out subtree
             * (i.e. don't-care region of address) */

			if (node->mLeft)
			{
				dispose_tree_node(node->mLeft);
				node->mLeft = NULL;
			}

			if (node->mRight)
			{
				dispose_tree_node(node->mRight);
				node->mRight = NULL;
			}
			break;
		}
		else
		{
            /* create new nodes for next bit */

			if (node->mLeft == NULL)
			{
				node->mLeft = new_tree_node(node, 1);
			}

			if (node->mRight == NULL)
			{
				node->mRight = new_tree_node(node, 1);
			}
		}
	} /* continue with next bit on the right */
}

/**
 * Display entire negation tree by printing the address belonging to
 * each node, using '-' for the don't-care bits.
 *
 * \param tree     pointer to the header of the tree
 * \param outfile  pointer to file descriptor to print to
 */
void
negation_tree_display_paths(NegationTreePtr tree, FILE* outfile)
{
	TreeHeaderPtr header = (TreeHeaderPtr) tree;
	TreeNodePtr node = header->mRoot;
	char buffer[33];

	memset(buffer, '-', 32);
	buffer[32] = '\0';

	display_path(node, outfile, buffer, 0);
}

/**
 * Get the list of addreses in the negation tree. The returned list
 * starts with the most specific nodes, i.e. nodes with the least
 * amount of don't-care bits.
 *
 * \param tree  pointer to the header of the tree
 * 
 * \return pointer to the list of nodes
 */
NegationTreeListNodePtr
negation_tree_get_list(NegationTreePtr tree)
{
	TreeHeaderPtr header = (TreeHeaderPtr) tree;

	if (!header->mList)
	{
		uint32_t value = 0;

		create_path_list(header, header->mRoot, value, 32);
	}

	assert(header->mList);

	return header->mList;
}
