/*********************************************************************
 *
 * 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.
 ********************************************************************/
 
#include "protoTree.h"
#include "protoDebug.h"  // for DMSG()

#include <string.h>
//#include <errno.h>  // for errno

ProtoTree::Item::Item()
 : value((void*)NULL), key((const char*)NULL), keysize(0), bit(0),
   parent((Item*)NULL), left((Item*)NULL), right((Item*)NULL)
{
    
}
ProtoTree::Item::Item(const char* theKey, unsigned int theKeysize, void* theValue)
 : value(theValue), key(theKey), keysize(theKeysize), bit(0),
   parent((Item*)NULL), left((Item*)NULL), right((Item*)NULL)
{
       
}

ProtoTree::Item::~Item()
{
    
}

unsigned int ProtoTree::Item::GetDepth() const 
{
    unsigned int depth = 0;
    const Item* p = this;
    while ((p = p->parent)) depth++;
    return depth;
}  // end ProtoTree::Item::GetDepth()

ProtoTree::ProtoTree()
 : keysize_min(0), keysize_max(0), roots((Item**)NULL)
{
}

ProtoTree::~ProtoTree()
{
    Destroy();
}

bool ProtoTree::Init(unsigned int keysizeMin, unsigned int keysizeMax)
{
    Destroy();
    unsigned int numRoots = keysizeMax - keysizeMin + 1;
    if (!(roots = new Item*[numRoots]))
    {
        DMSG(0, "ProtoTree:Init() error allocating roots: %s\n", GetErrorString());
        return false;   
    }
    memset(roots, 0, numRoots*sizeof(Item**));
    keysize_min = keysizeMin;
    keysize_max = keysizeMax;
    return true;
}  // end ProtoTree::Init()

bool ProtoTree::IsEmpty()
{
    unsigned int numRoots = keysize_max - keysize_min + 1;
    for (unsigned int i =0 ; i < numRoots; i++)
    {
        if (NULL != roots[i]) return false;
    }
    return true;
}  // end ProtoTree::IsEmpty()

void ProtoTree::Destroy()
{
    if (roots)
    {
        Empty();
        delete roots;
        roots = (Item**)NULL;
        keysize_min = keysize_max = 0;
    }
}  // end ProtoTree:Destroy()


void ProtoTree::Empty()
{
    if (roots)
    {
        for (unsigned int i = keysize_min; i <= keysize_max; i++)
        {
            Item* next;
            while ((next = roots[i - keysize_min]))
            {
                next = Remove(next);
                delete next;   
            }
        }
    }
}  // end ProtoTree::Empty()

ProtoTree::Item* ProtoTree::GetRoot()
{
    if (roots)
    {
        for (unsigned int i = keysize_min; i <= keysize_max; i++)
        {
            Item* nextRoot = roots[i - keysize_min];
            if (NULL != nextRoot) return nextRoot;
        }
    }
    return NULL;
}  // end ProtoTree::GetRoot()

bool ProtoTree::Insert(ProtoTree::Item* item)
        
{
    if (!roots)
    {
        DMSG(0, "ProtoTree::Insert() error: uninitialized tree\n");
        return false;  
    }
    unsigned int keysize = item->keysize;
    Item* subroot = roots[keysize - keysize_min];
    if (subroot)
    {
        // 1) Find closest match to "item"
        const char* key = item->key;
        Item* x = subroot;
        Item* p;
        do
        {
            p = x;
            x = Bit(key, x->bit) ? x->right : x->left;
        } while (p == x->parent);
        
        // Compare        
        if (!x->IsEqual(key, keysize))
        {
            // 2) Find index of first differing bit ("i")
            const char* ptr1 = key;
            const char* ptr2 = x->key;
            while (*ptr1 == *ptr2)
            {
                ptr1++;
                ptr2++;   
            }   
            unsigned int i = (ptr1 - key) << 3;
            unsigned char delta = *ptr1 ^ *ptr2;
            unsigned char mask = 0x80;
            while (0 == (mask & delta))
            {
                mask >>= 1;
                i++;
            }
            item->bit = i;
            // 3) Find "item" insertion point
            x = subroot;
            do
            {
                p = x;
                x = Bit(key, x->bit) ? x->right : x->left;
            } while ((x->bit < i) && (p == x->parent)); 
            // 4) Insert "item" into tree
            if (Bit(key, i))
            {
                item->left = x;
                item->right = item;
            }
            else
            {
                item->left = item;
                item->right = x;                 
            }
            item->parent = p;
            if (Bit(key, p->bit))
                p->right = item;
            else 
                p->left = item;
            if (p == x->parent)  x->parent = item;
            return true;                        
        }
        else
        {
            DMSG(0, "ProtoTree::Insert() Equivalent item already in tree!\n");
            return false;   
        }          
    }
    else
    {
        // Subtree is empty.  Make "item" a subtree root
        roots[keysize - keysize_min] = item;
        item->parent = (Item*)NULL;
        item->left = item->right = item;
        item->bit = 0;
        return true;
    }
}  // end ProtoTree::Insert()

// The return value matters very much here!
// (item pointers are sometimes swapped to preserve tree logic)
ProtoTree::Item* ProtoTree::Remove(ProtoTree::Item* item)
{
    if (((item == item->left) || (item == item->right)) &&  item->parent)
    {
        // non-root "item" has at least one self-pointer  
        Item* orphan = (item == item->right) ? item->left : item->right;
        if (orphan->parent == item) 
            orphan->parent = item->parent;
        if (orphan->left == item)   
            orphan->left = orphan;
        else if (orphan->right == item)
            orphan->right = orphan;
        if (item == item->parent->left)
            item->parent->left = orphan;
        else if (item == item->parent->right)
            item->parent->right = orphan;
        item->parent = item->left = item->right = (Item*)NULL;
        return item;
    }
    else
    {
        // "item" has no self-pointers or is "root"
        // 1) Find terminal "q"  with backpointer to "item"   
        Item* x = item;
        Item* q;
        do
        {
            q = x;
            if (Bit(item->key, x->bit))
                x = x->right;
            else
                x = x->left;
        } while (x != item);
        // 2) Find terminal "r" with backpointer to "q"
        x = q;
        Item* r;
        do
        {
            r = x;
            if (Bit(q->key, x->bit))
                x = x->right;
            else
                x = x->left;
        } while (x != q);
        // 3) Remove "q" node from tree
        if (q->parent)
        {
            if (q == q->parent->left)
            {
                if (q->left->parent == q)
                {
                    q->parent->left = q->left;
                    q->left->parent = q->parent;
                }
                else if (q->right->parent == q)
                {
                    q->parent->left = q->right;
                    q->right->parent = q->parent;
                }
                else
                {
                    q->parent->left = item;
                }
            }
            else if (q == q->parent->right)
            {
                if (q->right->parent == q)
                {
                    q->parent->right = q->right;
                    q->right->parent = q->parent;
                }
                else if (q->left->parent == q)
                {
                    q->parent->right = q->left;
                    q->left->parent = q->parent;
                }
                else
                {
                    q->parent->right = item;
                }
            }
        }
        else
        {
            roots[item->keysize - keysize_min] = (Item*)NULL;  
        }
        // 4) Fix backpointer to "q" if applicable
        if (r != q)
        {
            if (r->left == q)
                r->left = item;
            else 
                r->right = item;
            if (q == r->parent) r->parent = q->parent;
        }
        // 5)  Transform "q"->"item" and "item"->"q"
        const char* keyTemp = item->key;
        item->key = q->key;  
        q->key = keyTemp;    
        void* valueTemp = item->value;
        item->value = q->value;
        q->value = valueTemp;
        q->parent = q->left = q->right = (Item*)NULL;
        return q;
    }
}  // end ProtoTree::Remove()

// Find item with exact match to key and keysize
ProtoTree::Item* ProtoTree::Find(const char*  key, 
                                 unsigned int keysize) const
{
    ASSERT(keysize >= keysize_min);
    ASSERT(keysize <= keysize_max);
    Item* x = roots[keysize - keysize_min];
    if (x)
    {
        Item* p;
        do 
        { 
            p = x;
            x = Bit(key, x->bit) ? x->right : x->left;   
        } while (x->parent == p);
        return (x->IsEqual(key, keysize) ? x : NULL);   
    }    
    else
    {
        return (Item*)NULL;
    }
}  // end ProtoTree::FindClosestMatch()

// Finds closest matching entry that is a prefix to "key"
ProtoTree::Item* ProtoTree::FindPrefix(const char*  key, 
                                       unsigned int keysize) const
{
    ASSERT(keysize >= keysize_min);
    ASSERT(keysize <= keysize_max);
    Item* x = NULL;
    while (keysize >= keysize_min)
    {
        x = roots[keysize - keysize_min];
        if (x)
        {
            Item* p;
            do 
            { 
                p = x;
                x = Bit(key, x->bit) ? x->right : x->left; 
            } while (x->parent == p);
            if (x->IsEqual(key, x->keysize)) return x;
        }
        keysize--; 
    }
    return NULL;
}  // end ProtoTree::FindPrefix()

ProtoTree::Item* ProtoTree::FindPrefixSubtree(const char*  prefix, 
                                              unsigned int prefixLen,
                                              unsigned int keysize) const
{
    keysize = keysize ? keysize : keysize_min;
    keysize = keysize < keysize_min ? keysize_min : keysize;
    while (keysize < prefixLen) keysize++;
    while (keysize <= keysize_max)
    {
        Item* x = roots[keysize - keysize_min];
        if (x)
        {
            Item* p;
            do 
            { 
                p = x;
                x = Bit(prefix, x->bit) ? x->right : x->left;  
            } while ((x->parent == p) && (x->bit < prefixLen));
            if (x->PrefixIsEqual(prefix, prefixLen)) return x;
        } 
        else
        {
            keysize++;   
        }
    }
    return (Item*)NULL;
}  // end ProtoTree::FindPrefixSubtree()

ProtoTree::Iterator::Iterator(const ProtoTree& t)
 : tree(t), keysize(t.keysize_min), next((Item*)NULL), 
   subtree((Item*)NULL), prev((Item*)NULL)
{
    if (!tree.roots) return; 
    while (keysize <= tree.keysize_max)
    {
        Item* root = tree.roots[keysize - tree.keysize_min];
        if (root)
        {
            Item* x = root;
            while (x->left->parent == x) x = x->left;
            next = x;
            return;
        }
        else
        {
            keysize++;   
        }
    }     
}  

void ProtoTree::Iterator::Reset(ProtoTree::Item* subtreeRoot)
{
    keysize = subtreeRoot ? subtreeRoot->keysize : tree.keysize_min;
    prev = next = (Item*)NULL;
    if (!tree.roots) return;
    while (keysize <= tree.keysize_max)
    {
        Item* root = subtreeRoot ? subtreeRoot : tree.roots[keysize - tree.keysize_min];
        subtree = subtreeRoot;
        if (root)
        {
            Item* x = root;
            while (x->left->parent == x) x = x->left;
            next = x;
            return;
        }
        else
        {
            keysize++;    
        }        
    }
}  // end ProtoTree::Iterator::Reset()


ProtoTree::Item* ProtoTree::Iterator::GetNextItem()
{
    Item* i = next;
    if (i)
    {
        if (i->right->parent == i)
        {
            Item* y = i->right;
            while (y->left->parent == y) y = y->left;
            if (y != i) 
            {
                next = y;
                prev = i;
                return i;   
            } 
        }
        if (subtree)
        {
            if ((i != i->right->parent) &&
                (i != i->right) &&
                (i->right->bit < subtree->bit) &&
                (prev != i->left) &&
                (prev != i->right))
            {
               prev = i->right;
               return prev; 
            }
            if ((i != i->left->parent) &&
                (i != i->left) &&
                (i->left->bit < subtree->bit) &&
                (prev != i->left))
            {
                prev = i->left;
                return prev;   
            }
        }
        
        Item* x = i;
        Item* y = i->parent;
        while (y && (y->right == x))
        {
            x = y;
            y = y->parent;   
        }
        if (y)
        {
            if (subtree)
                next = y->bit < subtree->bit ? NULL : y;
            else
                next = y;
            return i;   
        }
        next = (Item*)NULL;
        prev = i;
        // For now subtree iterations occur 
        // only within a single subroot
        if (subtree) return i; 
        // For whole tree iterations, look to next subroot (if there is one)
        while (keysize < tree.keysize_max)
        {
            
            keysize++;
            Item* root = tree.roots[keysize - tree.keysize_min];
            if (root)
            {
                Item* z = root;
                while (z->left->parent == z)
                    z = z->left;
                next = z;
                break;
            }
        }
        return i;
    }
    else
    {
        prev = (Item*)NULL;
        return (Item*)NULL;
    } 
}  // end ProtoTree::Iterator::GetNextItem();
