/*********************************************************************
 *
 * AUTHORIZATION TO USE AND DISTRIBUTE
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that: 
 *
 * (1) source code distributions retain this paragraph in its entirety, 
 *  
 * (2) distributions including binary code include this paragraph in
 *     its entirety in the documentation or other materials provided 
 *     with the distribution, and 
 *
 * (3) all advertising materials mentioning features or use of this 
 *     software display the following acknowledgment:
 * 
 *      "This product includes software written and developed 
 *       by Brian Adamson of the Naval Research Laboratory (NRL)." 
 *         
 *  The name of NRL, the name(s) of NRL  employee(s), or any entity
 *  of the United States Government may not be used to endorse or
 *  promote  products derived from this software, nor does the 
 *  inclusion of the NRL written and developed software  directly or
 *  indirectly suggest NRL or United States  Government endorsement
 *  of this product.
 * 
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 ********************************************************************/

#ifndef _PROTO_TREE
#define _PROTO_TREE

/*******************************************************
 * protoTree.h - This is a general purpose prefix-based  
 *               C++ Patricia tree. It is designed to
 *               work with keys of incremental _byte_ 
 *               size.  The code also provides the 
 *               ability to iterate over items with a 
 *               common prefix of arbitrary bit length.
 *               Note this prefix iteration must currently
 *               be done separately for each keysize in the
 *               tree (This may be automated in the future)
 *
 *               The class ProtoTree provides a relatively
 *               lightweight but reasonable performance
 *               data storage and retrieval mechanism which
 *               might be useful in prototype protocol
 *               implementations.  Thus, its inclusion
 *               in the NRL Protolib.
 ******************************************************/

#include <string.h>

class ProtoTree
{
    public:
        ProtoTree();
        ~ProtoTree();
        
        // "keysizeMin" and "keysizeMax" are in units of "bits"
        bool Init(unsigned int keysizeMin, unsigned int keysizeMax);
        bool IsReady() const {return (NULL != roots);}
        bool IsEmpty();
        void Empty();
        void Destroy();
        unsigned long KeysizeMin() const {return keysize_min;}
        unsigned long KeysizeMax() const {return keysize_max;}
        
        class Item;
        bool Insert(Item* item);
        // Note: Item returned may not be the same passed in!
        ProtoTree::Item* Remove(Item* item); 
        
        // Find item with exact match to "key" and "keysize"
        ProtoTree::Item* Find(const char* key, unsigned int keysize) const;
        
        // Find largest item which is a prefix of the "key"
        ProtoTree::Item* FindPrefix(const char* key, unsigned int keysize) const;
        
        // This finds the root of a subtree of Items matching the given prefix
        ProtoTree::Item* FindPrefixSubtree(const char*     prefix, 
                                           unsigned int    prefixLen, // in bits
                                           unsigned int    keysize = 0) const;
        
        ProtoTree::Item* GetRoot();

        // class ProtoTree::Item serves as a container for items to be stored in the tree
        class Iterator;
        class Item
        {
            friend class ProtoTree;
            friend class Iterator;

            public: 
                Item();
                Item(const char* theKey, unsigned int theKeysize, void* theValue = NULL);  
                ~Item();
                void Init(const char* theKey, unsigned int theKeysize, void* theValue = NULL)
                {
                    value = theValue;
                    key = theKey;
                    keysize = theKeysize;
                }
                void* GetValue() const {return value;}
                const char* GetKey() const {return key;}
                unsigned int GetKeysize() const {return keysize;}
                
                unsigned int GetDepth() const;
                Item* GetParent() const {return parent;}
                Item* GetLeft() const {return left;}
                Item* GetRight() const {return right;}
                unsigned int GetBit() {return bit;}

            private:
                // bitwise comparison of the two keys
                bool IsEqual(const char* theKey, unsigned int theKeysize) const
                {
                    unsigned int n  = theKeysize >> 3;
                    return ((keysize != theKeysize) ? false :
                            ((0 != memcmp(key, theKey, n)) ? false :
                              (0 == ((unsigned char)(0xff << (8 - (theKeysize & 0x07))) & 
                                    (key[n] ^ theKey[n])))));
                }
                bool PrefixIsEqual(const char* prefix, unsigned int prefixSize) const
                {
                    unsigned int n  = prefixSize >> 3;
                    return ((0 != memcmp(key, prefix, n)) ? false :
                             (0 == ((unsigned char)(0xff << (8 - (prefixSize & 0x07))) & 
                                   (key[n] ^ prefix[n]))));
                }
                               
                void*               value;
                const char*         key;
                unsigned int        keysize;
                unsigned int        bit;

                Item*               parent;
                Item*               left;
                Item*               right;
        };  // end class ProtoTree::Item

        // This can be used to iterate over the entire tree (default)
        // or a particular "subtree"
        class Iterator
        {
            public:
                Iterator(const ProtoTree& tree);
                void Reset(Item* subtreeRoot = (Item*)NULL);
                Item* GetNextItem();

            private:
                const ProtoTree&    tree;
                unsigned int        keysize;
                Item*               next;
                Item*               subtree;
                Item*               prev;
        };  // end class ProtoTree::Iterator     
        
                
        friend class ProtoTree::Iterator;
            
    protected:
        Item* FindClosestMatch(const char* key, unsigned int keySize) const;
        static bool Bit(const char* mask, unsigned int index)
            {return (0 != (mask[index >> 3] & (0x80 >> (index & 0x07))));}
        
        unsigned int    keysize_min;
        unsigned int    keysize_max;
        Item**          roots;
        
};  // end class ProtoTree

#endif // PROTO_TREE
