/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (the "MPL"); you may not use this file except in
 * compliance with the MPL.  You may obtain a copy of the MPL at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the MPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the MPL
 * for the specific language governing rights and limitations under the
 * MPL.
 *
 * The Initial Developer of this code under the MPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1999 Netscape Communications Corporation.  All Rights
 * Reserved.
 *
 * Original Author:
 *   Chris Waterson <waterson@netscape.com>
 */

#include "nsVoidBTree.h"

#ifdef DEBUG
#include <stdio.h>
#endif

// Set this to force the tree to be verified after every insertion and
// removal.
//#define PARANOID 1


//----------------------------------------------------------------------
// nsVoidBTree::Node
//
//   Implementation methods
//

nsresult
nsVoidBTree::Node::Create(Type aType, PRInt32 aCapacity, Node** aResult)
{
    // So we only ever have to do one allocation for a Node, we do a
    // "naked" heap allocation, computing the size of the node and
    // "padding" it out so that it can hold aCapacity slots.
    char* bytes = new char[sizeof(Node) + (aCapacity - 1) * sizeof(void*)];
    if (! bytes)
        return NS_ERROR_OUT_OF_MEMORY;

    Node* result = NS_REINTERPRET_CAST(Node*, bytes);
    result->mBits = 0;
    result->SetType(aType);

    *aResult = result;
    return NS_OK;
}

nsresult
nsVoidBTree::Node::Destroy(Node* aNode)
{
    char* bytes = NS_REINTERPRET_CAST(char*, aNode);
    delete[] bytes;
    return NS_OK;
}

void
nsVoidBTree::Node::InsertElementAt(void* aElement, PRInt32 aIndex)
{
    NS_PRECONDITION(aIndex >= 0 && aIndex <= GetCount(), "bad index");

    PRInt32 count = GetCount();
    SetCount(count + 1);

    while (count > aIndex) {
        mData[count] = mData[count - 1];
        --count;
    }

    mData[aIndex] = aElement;
}

void
nsVoidBTree::Node::RemoveElementAt(PRInt32 aIndex)
{
    NS_PRECONDITION(aIndex >= 0 && aIndex < GetCount(), "bad index");

    PRInt32 count = GetCount();
    SetCount(count - 1);
    
    while (aIndex < count) {
        mData[aIndex] = mData[aIndex + 1];
        ++aIndex;
    }
}


//----------------------------------------------------------------------
//
// nsVoidBTree::Path
//
//   Implementation methods
//

nsVoidBTree::Path::Path(const Path& aOther)
    : mTop(aOther.mTop)
{
    for (PRInt32 i = 0; i < mTop; ++i)
        mLink[i] = aOther.mLink[i];
}

nsVoidBTree::Path&
nsVoidBTree::Path::operator=(const Path& aOther)
{
    mTop = aOther.mTop;
    for (PRInt32 i = 0; i < mTop; ++i)
        mLink[i] = aOther.mLink[i];
    return *this;
}

inline nsresult
nsVoidBTree::Path::Push(Node* aNode, PRInt32 aIndex)
{
    // XXX If you overflow this thing, think about making larger index
    // or data nodes. You can pack a _lot_ of data into a pretty flat
    // tree.
    NS_PRECONDITION(mTop <= kMaxDepth, "overflow");
    if (mTop > kMaxDepth)
        return NS_ERROR_OUT_OF_MEMORY;

    mLink[mTop].mNode  = aNode;
    mLink[mTop].mIndex = aIndex;
    ++mTop;

    return NS_OK;
}


inline void
nsVoidBTree::Path::Pop(Node** aNode, PRInt32* aIndex)
{
    --mTop;
    *aNode  = mLink[mTop].mNode;
    *aIndex = mLink[mTop].mIndex;
}

//----------------------------------------------------------------------
//
//    nsVoidBTree methods
//

nsVoidBTree::nsVoidBTree(const nsVoidBTree& aOther)
{
    ConstIterator last = aOther.Last();
    for (ConstIterator element = aOther.First(); element != last; ++element)
        AppendElement(*element);
}

nsVoidBTree&
nsVoidBTree::operator=(const nsVoidBTree& aOther)
{
    Clear();
    ConstIterator last = aOther.Last();
    for (ConstIterator element = aOther.First(); element != last; ++element)
        AppendElement(*element);
    return *this;
}

PRInt32
nsVoidBTree::Count() const
{
    if (IsEmpty())
        return 0;

    if (IsSingleElement())
        return 1;

    Node* root = NS_REINTERPRET_CAST(Node*, mRoot & kRoot_PointerMask);
    return root->GetSubTreeSize();
}

void*
nsVoidBTree::ElementAt(PRInt32 aIndex) const
{
    if (aIndex < 0 || aIndex >= Count())
        return nsnull;

    if (IsSingleElement())
        return NS_REINTERPRET_CAST(void*, mRoot & kRoot_PointerMask);

    Node* current = NS_REINTERPRET_CAST(Node*, mRoot & kRoot_PointerMask);
    while (current->GetType() != Node::eType_Data) {
        // We're still in the index. Find the right leaf.
        Node* next = nsnull;

        PRInt32 count = current->GetCount();
        for (PRInt32 i = 0; i < count; ++i) {
            Node* child = NS_REINTERPRET_CAST(Node*, current->GetElementAt(i));

            PRInt32 childcount = child->GetSubTreeSize();
            if (PRInt32(aIndex) < childcount) {
                next = child;
                break;
            }

            aIndex -= childcount;
        }

        if (! next) {
            NS_ERROR("corrupted");
            return nsnull;
        }

        current = next;
    }

    return current->GetElementAt(aIndex);
}


PRInt32
nsVoidBTree::IndexOf(void* aPossibleElement) const
{
    NS_PRECONDITION((PRWord(aPossibleElement) & ~kRoot_PointerMask) == 0,
                    "uh oh, someone wants to use the pointer bits");

    NS_PRECONDITION(aPossibleElement != nsnull, "nsVoidBTree can't handle null elements");
    if (aPossibleElement == nsnull)
        return -1;

    PRInt32 result = 0;
    ConstIterator last = Last();
    for (ConstIterator element = First(); element != last; ++element, ++result) {
        if (aPossibleElement == *element)
            return result;
    }

    return -1;
}

  
PRBool
nsVoidBTree::InsertElementAt(void* aElement, PRInt32 aIndex)
{
    NS_PRECONDITION((PRWord(aElement) & ~kRoot_PointerMask) == 0,
                    "uh oh, someone wants to use the pointer bits");

    if ((PRWord(aElement) & ~kRoot_PointerMask) != 0)
        return PR_FALSE;

    NS_PRECONDITION(aElement != nsnull, "nsVoidBTree can't handle null elements");
    if (aElement == nsnull)
        return PR_FALSE;

    PRInt32 count = Count();

    if (aIndex < 0 || aIndex > count)
        return PR_FALSE;

    nsresult rv;

    if (IsSingleElement()) {
        // We're only a single element holder, and haven't yet
        // "faulted" to create the btree.

        if (count == 0) {
            // If we have *no* elements, then just set the root
            // pointer and we're done.
            mRoot = PRWord(aElement);
            return PR_TRUE;
        }

        // If we already had an element, and now we're adding
        // another. Fault and start creating the btree.
        void* element = NS_REINTERPRET_CAST(void*, mRoot & kRoot_PointerMask);

        Node* newroot;
        rv = Node::Create(Node::eType_Data, kDataCapacity, &newroot);
        if (NS_FAILED(rv)) return PR_FALSE;

        newroot->InsertElementAt(element, 0);
        newroot->SetSubTreeSize(1);
        SetRoot(newroot);
    }

    Path path;

    Node* current = NS_REINTERPRET_CAST(Node*, mRoot & kRoot_PointerMask);
    while (current->GetType() != Node::eType_Data) {
        // We're still in the index. Find the right leaf.
        Node* next = nsnull;

        count = current->GetCount();
        for (PRInt32 i = 0; i < count; ++i) {
            Node* child = NS_REINTERPRET_CAST(Node*, current->GetElementAt(i));

            PRInt32 childcount = child->GetSubTreeSize();
            if (PRInt32(aIndex) <= childcount) {
                rv = path.Push(current, i + 1);
                if (NS_FAILED(rv)) return PR_FALSE;

                next = child;
                break;
            }

            aIndex -= childcount;
        }

        if (! next) {
            NS_ERROR("corrupted");
            return PR_FALSE;
        }

        current = next;
    }

    if (current->GetCount() >= kDataCapacity) {
        // We just blew the data node's buffer. Create another
        // datanode and split.
        rv = Split(path, current, aElement, aIndex);
        if (NS_FAILED(rv)) return PR_FALSE;
    }
    else {
        current->InsertElementAt(aElement, aIndex);
        current->SetSubTreeSize(current->GetSubTreeSize() + 1);
    }

    while (path.Length() > 0) {
        PRInt32 index;
        path.Pop(&current, &index);
        current->SetSubTreeSize(current->GetSubTreeSize() + 1);
    }

#ifdef PARANOID
    Verify(NS_REINTERPRET_CAST(Node*, mRoot & kRoot_PointerMask));
#endif

    return PR_TRUE;
}

PRBool
nsVoidBTree::ReplaceElementAt(void* aElement, PRInt32 aIndex)
{
    NS_PRECONDITION((PRWord(aElement) & ~kRoot_PointerMask) == 0,
                    "uh oh, someone wants to use the pointer bits");

    if ((PRWord(aElement) & ~kRoot_PointerMask) != 0)
        return PR_FALSE;

    NS_PRECONDITION(aElement != nsnull, "nsVoidBTree can't handle null elements");
    if (aElement == nsnull)
        return PR_FALSE;

    if (aIndex < 0 || aIndex >= Count())
        return PR_FALSE;

    if (IsSingleElement()) {
        mRoot = PRWord(aElement);
        return PR_TRUE;
    }

    Node* current = NS_REINTERPRET_CAST(Node*, mRoot & kRoot_PointerMask);
    while (current->GetType() != Node::eType_Data) {
        // We're still in the index. Find the right leaf.
        Node* next = nsnull;

        PRInt32 count = current->GetCount();
        for (PRInt32 i = 0; i < count; ++i) {
            Node* child = NS_REINTERPRET_CAST(Node*, current->GetElementAt(i));

            PRInt32 childcount = child->GetSubTreeSize();
            if (PRInt32(aIndex) < childcount) {
                next = child;
                break;
            }

            aIndex -= childcount;
        }

        if (! next) {
            NS_ERROR("corrupted");
            return PR_FALSE;
        }

        current = next;
    }

    current->SetElementAt(aElement, aIndex);
    return PR_TRUE;
}

PRBool
nsVoidBTree::RemoveElement(void* aElement)
{
    PRInt32 index = IndexOf(aElement);
    return (index >= 0) ? RemoveElementAt(index) : PR_FALSE;
}

PRBool
nsVoidBTree::RemoveElementAt(PRInt32 aIndex)
{
    PRInt32 count = Count();

    if (aIndex < 0 || aIndex >= count)
        return PR_FALSE;

    if (IsSingleElement()) {
        // We're removing the one and only element
        mRoot = 0;
        return PR_TRUE;
    }

    // We've got more than one element, and we're removing it.
    nsresult rv;
    Path path;

    Node* root = NS_REINTERPRET_CAST(Node*, mRoot & kRoot_PointerMask);

    Node* current = root;
    while (current->GetType() != Node::eType_Data) {
        // We're still in the index. Find the right leaf.
        Node* next = nsnull;

        count = current->GetCount();
        for (PRInt32 i = 0; i < count; ++i) {
            Node* child = NS_REINTERPRET_CAST(Node*, current->GetElementAt(i));

            PRInt32 childcount = child->GetSubTreeSize();
            if (PRInt32(aIndex) < childcount) {
                rv = path.Push(current, i);
                if (NS_FAILED(rv)) return PR_FALSE;

                next = child;
                break;
            }
            
            aIndex -= childcount;
        }

        if (! next) {
            NS_ERROR("corrupted");
            return PR_FALSE;
        }

        current = next;
    }

    current->RemoveElementAt(aIndex);

    while ((current->GetCount() == 0) && (current != root)) {
        Node* doomed = current;

        PRInt32 index;
        path.Pop(&current, &index);
        current->RemoveElementAt(index);

        Node::Destroy(doomed);
    }

    current->SetSubTreeSize(current->GetSubTreeSize() - 1);

    while (path.Length() > 0) {
        PRInt32 index;
        path.Pop(&current, &index);
        current->SetSubTreeSize(current->GetSubTreeSize() - 1);
    }

    while ((root->GetType() == Node::eType_Index) && (root->GetCount() == 1)) {
        Node* doomed = root;
        root = NS_REINTERPRET_CAST(Node*, root->GetElementAt(0));
        SetRoot(root);
        Node::Destroy(doomed);
    }

#ifdef PARANOID
    Verify(root);
#endif

    return PR_TRUE;
}

void
nsVoidBTree::Clear(void)
{
    if (IsEmpty())
        return;

    if (! IsSingleElement()) {
        Node* root = NS_REINTERPRET_CAST(Node*, mRoot & kRoot_PointerMask);

#ifdef PARANOID
        Dump(root, 0);
#endif

        DestroySubtree(root);
    }

    mRoot = 0;
}


void
nsVoidBTree::Compact(void)
{
    // XXX We could go through and try to merge datanodes.
}

PRBool
nsVoidBTree::EnumerateForwards(EnumFunc aFunc, void* aData) const
{
    PRBool running = PR_TRUE;

    ConstIterator last = Last();
    for (ConstIterator element = First(); running && element != last; ++element)
        running = (*aFunc)(*element, aData);

    return running;
}

PRBool
nsVoidBTree::EnumerateBackwards(EnumFunc aFunc, void* aData) const
{
    PRBool running = PR_TRUE;

    ConstIterator element = Last();
    ConstIterator first = First();

    if (element != first) {
        do {
            running = (*aFunc)(*--element, aData);
        } while (running && element != first);
    }

    return running;
}


void
nsVoidBTree::SizeOf(nsISizeOfHandler* aHandler, PRUint32* aResult) const
{
    if (! aResult)
        return;

    *aResult = sizeof(*this);

    if (IsSingleElement())
        return;

    Path path;
    path.Push(NS_REINTERPRET_CAST(Node*, mRoot & kRoot_PointerMask), 0);

    while (path.Length()) {
        Node* current;
        PRInt32 index;
        path.Pop(&current, &index);

        if (current->GetType() == Node::eType_Data) {
            *aResult += sizeof(Node) + (sizeof(void*) * (kDataCapacity - 1));
        }
        else {
            *aResult += sizeof(Node) + (sizeof(void*) * (kIndexCapacity - 1));

            // If we're in an index node, and there are still kids to
            // traverse, well, traverse 'em.
            if (index < current->GetCount()) {
                path.Push(current, index + 1);
                path.Push(NS_STATIC_CAST(Node*, current->GetElementAt(index)), 0);
            }
        }
    }
}

//----------------------------------------------------------------------

nsresult
nsVoidBTree::Split(Path& path, Node* aOldNode, void* aElementToInsert, PRInt32 aSplitIndex)
{
    nsresult rv;

    PRInt32 capacity = (aOldNode->GetType() == Node::eType_Data) ? kDataCapacity : kIndexCapacity;
    PRInt32 delta = 0;


    Node* newnode;
    rv = Node::Create(aOldNode->GetType(), capacity, &newnode);
    if (NS_FAILED(rv)) return rv;

    if (aSplitIndex == capacity) {
        // If aSplitIndex is the same as the capacity of the node,
        // then there'll be nothing to copy from the old node to the
        // new node, and the element is really meant to be inserted in
        // the newnode. In that case, do it _now_ so that newnode's
        // subtree size will be correct.
        newnode->InsertElementAt(aElementToInsert, 0);

        if (newnode->GetType() == Node::eType_Data) {
            newnode->SetSubTreeSize(1);
        }
        else {
            Node* child = NS_REINTERPRET_CAST(Node*, aElementToInsert);
            newnode->SetSubTreeSize(child->GetSubTreeSize());
        }
    }
    else {
        // We're meant to insert the element into the oldnode at
        // aSplitIndex. Copy data from aOldNode to the newnode but
        // _don't_ insert newnode yet. We may need to recursively
        // split parents, an operation that allocs, and hence, may
        // fail. If it does fail, we wan't to not screw up the
        // existing datastructure.
        //
        // Note that it should be the case that count == capacity, but
        // who knows, we may decide at some point to prematurely split
        // nodes for some reason or another.
        PRInt32 count = aOldNode->GetCount();
        PRInt32 i = aSplitIndex;
        PRInt32 j = 0;

        newnode->SetCount(count - aSplitIndex);
        while (i < count) {
            if (aOldNode->GetType() == Node::eType_Data) {
                ++delta;
            }
            else {
                Node* migrating = NS_REINTERPRET_CAST(Node*, aOldNode->GetElementAt(i));
                delta += migrating->GetSubTreeSize();
            }

            newnode->SetElementAt(aOldNode->GetElementAt(i), j);
            ++i;
            ++j;
        }
        newnode->SetSubTreeSize(delta);
    }

    // Now we split the node.

    if (path.Length() == 0) {
        // We made it all the way up to the root! Ok, so, create a new
        // root
        Node* newroot;
        rv = Node::Create(Node::eType_Index, kIndexCapacity, &newroot);
        if (NS_FAILED(rv)) return rv;

        newroot->SetCount(2);
        newroot->SetElementAt(aOldNode, 0);
        newroot->SetElementAt(newnode, 1);
        newroot->SetSubTreeSize(aOldNode->GetSubTreeSize() + 1);
        SetRoot(newroot);
    }
    else {
        // Otherwise, use the "path" to pop off the next thing above us.
        Node* parent;
        PRInt32 indx;
        path.Pop(&parent, &indx);

        if (parent->GetCount() >= kIndexCapacity) {
            // Parent is full, too. Recursively split it.
            rv = Split(path, parent, newnode, indx);
            if (NS_FAILED(rv)) {
                Node::Destroy(newnode);
                return rv;
            }
        }
        else {
            // Room in the parent, so just smack it on up there.
            parent->InsertElementAt(newnode, indx);
            parent->SetSubTreeSize(parent->GetSubTreeSize() + 1);
        }
    }

    // Now, since all our operations that might fail have finished, we
    // can go ahead and monkey with the old node.

    if (aSplitIndex == capacity) {
        PRInt32 nodeslost = newnode->GetSubTreeSize() - 1;
        PRInt32 subtreesize = aOldNode->GetSubTreeSize() - nodeslost;
        aOldNode->SetSubTreeSize(subtreesize);
    }
    else {
        aOldNode->SetCount(aSplitIndex);
        aOldNode->InsertElementAt(aElementToInsert, aSplitIndex);
        PRInt32 subtreesize = aOldNode->GetSubTreeSize() - delta + 1;
        aOldNode->SetSubTreeSize(subtreesize);
    }

    return NS_OK;
}


PRInt32
nsVoidBTree::Verify(Node* aNode)
{
    // Sanity check the tree by verifying that the subtree sizes all
    // add up correctly.
    if (aNode->GetType() == Node::eType_Data) {
        NS_ASSERTION(aNode->GetCount() == aNode->GetSubTreeSize(), "corrupted");
        return aNode->GetCount();
    }

    PRInt32 childcount = 0;
    for (PRInt32 i = 0; i < aNode->GetCount(); ++i) {
        Node* child = NS_REINTERPRET_CAST(Node*, aNode->GetElementAt(i));
        childcount += Verify(child);
    }

    NS_ASSERTION(childcount == aNode->GetSubTreeSize(), "corrupted");
    return childcount;
}


void
nsVoidBTree::DestroySubtree(Node* aNode)
{
    PRInt32 count = aNode->GetCount() - 1;
    while (count >= 0) {
        if (aNode->GetType() == Node::eType_Index)
            DestroySubtree(NS_REINTERPRET_CAST(Node*, aNode->GetElementAt(count)));
        
        --count;
    }

    Node::Destroy(aNode);
}

#ifdef DEBUG
void
nsVoidBTree::Dump(Node* aNode, PRInt32 aIndent)
{
    for (PRInt32 i = 0; i < aIndent; ++i)
        printf("  ");

    if (aNode->GetType() == Node::eType_Data) {
        printf("data(%d/%d)\n", aNode->GetCount(), aNode->GetSubTreeSize());
    }
    else {
        printf("index(%d/%d)\n", aNode->GetCount(), aNode->GetSubTreeSize());
        for (PRInt32 j = 0; j < aNode->GetCount(); ++j)
            Dump(NS_REINTERPRET_CAST(Node*, aNode->GetElementAt(j)), aIndent + 1);
    }
}
#endif

//----------------------------------------------------------------------
//
// nsVoidBTree::ConstIterator and Iterator methods
//

void* nsVoidBTree::kDummyLast;

void
nsVoidBTree::ConstIterator::Next()
{
    if (mIsSingleton) {
        mIsExhausted = PR_TRUE;
        return;
    }

    // Otherwise we're a real b-tree iterator, and we need to pull and
    // pop our path stack appropriately to gyrate into the right
    // position.
    while (1) {
        Node* current;
        PRInt32 index;
        mPath.Pop(&current, &index);

        PRInt32 count = current->GetCount();

        NS_ASSERTION(index < count, "ran off the end, pal");

        if (++index >= count) {
            // XXXwaterson Oh, this is so ugly. I wish I was smart
            // enough to figure out a prettier way to do it.
            //
            // See if we've just iterated past the last element in the
            // b-tree, and now need to leave ourselves in the magical
            // state that is equal to nsVoidBTree::Last().
            if (current->GetType() == Node::eType_Data) {
                PRBool rightmost = PR_TRUE;
                for (PRInt32 slot = mPath.mTop - 1; slot >= 0; --slot) {
                    const Link& link = mPath.mLink[slot];
                    if (link.mIndex != link.mNode->GetCount() - 1) {
                        rightmost = PR_FALSE;
                        break;
                    }
                }

                if (rightmost) {
                    // It's the last one. Make the path look exactly
                    // like nsVoidBTree::Last().
                    mPath.Push(current, index);
                    return;
                }
            }

            // Otherwise, we just ran off the end of a "middling"
            // node. Loop around, to pop back up the b-tree to its
            // parent.
            continue;
        }

        // We're somewhere in the middle. Push the new location onto
        // the stack.
        mPath.Push(current, index);

        // If we're in a data node, we're done: break out of the loop
        // here leaving the top of the stack pointing to the next data
        // element in the b-tree.
        if (current->GetType() == Node::eType_Data)
            break;

        // Otherwise, we're still in an index node. Push next node
        // down onto the stack, starting "one off" to the left, and
        // continue around.
        mPath.Push(NS_STATIC_CAST(Node*, current->GetElementAt(index)), -1);
    }
}

void
nsVoidBTree::ConstIterator::Prev()
{
    if (mIsSingleton) {
        mIsExhausted = PR_FALSE;
        return;
    }

    // Otherwise we're a real b-tree iterator, and we need to pull and
    // pop our path stack appropriately to gyrate into the right
    // position. This is just like nsVoidBTree::ConstIterator::Next(),
    // but in reverse.
    while (1) {
        Node* current;
        PRInt32 index;
        mPath.Pop(&current, &index);

        NS_ASSERTION(index >= 0, "ran off the front, pal");

        if (--index < 0)
            continue;

        mPath.Push(current, index);

        if (current->GetType() == Node::eType_Data)
            break;

        current = NS_STATIC_CAST(Node*, current->GetElementAt(index));
        mPath.Push(current, current->GetCount());
    }
}

const nsVoidBTree::Path
nsVoidBTree::LeftMostPath() const
{
    Path path;
    Node* current = NS_REINTERPRET_CAST(Node*, mRoot & kRoot_PointerMask);

    while (1) {
        path.Push(current, 0);

        if (current->GetType() == Node::eType_Data)
            break;

        current = NS_STATIC_CAST(Node*, current->GetElementAt(0));
    }

    return path;
}


const nsVoidBTree::Path
nsVoidBTree::RightMostPath() const
{
    Path path;
    Node* current = NS_REINTERPRET_CAST(Node*, mRoot & kRoot_PointerMask);

    while (1) {
        PRInt32 count = current->GetCount();

        if (current->GetType() == Node::eType_Data) {
            path.Push(current, count);
            break;
        }

        path.Push(current, count - 1);
        current = NS_STATIC_CAST(Node*, current->GetElementAt(count - 1));
    }

    return path;
}
