/*==LICENSE==*

CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011  Cyan Worlds, Inc.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

You can contact Cyan Worlds, Inc. by email legal@cyan.com
 or by snail mail at:
      Cyan Worlds, Inc.
      14617 N Newport Hwy
      Mead, WA   99021

*==LICENSE==*/
#include "hsStringTable.h"
#include <stdlib.h>
#if HS_BUILD_FOR_PS2 || __MWERKS__ || HS_BUILD_FOR_UNIX
#include <ctype.h>
#endif
//
// hsStringTable
//

hsStringTable::Node::Node(char c) : sib(0), kid(0), chr(c), data(0) {};
hsStringTable::Node::~Node() { };
void* hsStringTable::Node::GetData() { return data; }
void hsStringTable::Node::SetData(void* v) { data = v; }

hsStringTable::~hsStringTable() 
{
    RemoveNode(root.kid);
}

void hsStringTable::Reset() 
{
    RemoveNode(root.kid);
    root.kid = nil;
}


//
// Find a the Node associated with a given string
//
hsStringTable::Node* hsStringTable::Find(const char* str)
{
    return (str && *str) ? FindRecur(root.kid, str) : nil;
}

//
// Find the unique node matching the given prefix. Returns the node
// at the first point where a unique path can not be determined
// char* str is filled out with the matching path
//
hsStringTable::Node* hsStringTable::FindPartial(char* str, Int32 len) const
{
    return (str && *str) ? FindPartialRecur(root.kid, str, len) : nil;
}

//
// Find and Add if needs a node for str and register it with data
//
void hsStringTable::Register(const char* str, void* data) 
{
    Node* node = FindRecur(root.kid, str, true);
    if (!node) 
    {
        node = AddRecur(&root, str);
    }
    node->SetData(data);
}

//
// Iterate through the tree and call the callback function on each child node
// of the fromNode (or the root if fromNode is nil)
//
hsBool hsStringTable::Iterate(hsStringTableCallback* callback, Node* fromNode)
{
    if (!fromNode)
        fromNode = &root;
    return IterateRecur(fromNode->kid,callback);
}

//
// Recursively find node, create if needed?
//
hsStringTable::Node* hsStringTable::FindRecur(Node* root, const char* str, hsBool createIfNeeded)
{
    if (!root || !str) return nil;
    if (tolower(root->chr)==tolower(*str)) 
    {
        if (*(str+1)) 
        {
            Node* node = FindRecur(root->kid, str+1, createIfNeeded);
            if (!node && createIfNeeded) node = AddRecur(root, str+1);
            return node;
        } 
        else 
        {
            return root;
        }
    } 
    else 
    {
        return FindRecur(root->sib,str,createIfNeeded);
    }
}

//
// Recursively find the unique partial match
//
hsStringTable::Node* hsStringTable::FindPartialRecur(Node* root, char* str, Int32 len) const
{
    if (!root || !str) 
    {
        return nil;
    }

    if (tolower(root->chr)==tolower(*str)) 
    {
        if (len) 
        {
            *str = root->chr;
        }
        
        if (*(str+1)) 
        {
            Node* node = FindPartialRecur(root->kid,str+1,len-1);
            //if (!node) node = AddRecur(root,str+1);
            return node;
        } 
        else 
        {
            return FindLeafRecur(root, str, len);
        }
    } 
    else 
    {
        return FindPartialRecur(root->sib, str, len);
    }
}

//
// Follow the current node as far as possible towards a unique leaf
//
hsStringTable::Node* hsStringTable::FindLeafRecur(Node* root, char* str, Int32 len) const
{
    if (root->data || !root->kid || root->kid->sib) 
    {
        if (len) 
        {
            *(str+1) = 0;
        }
        return root;
    } 
    else 
    {
        if (len) 
        {
            *(str+1) = len>1 ? root->kid->chr : 0;
        }
        return FindLeafRecur(root->kid,str+1,len-1);
    }
}


//
// Add a string to the tree
//
hsStringTable::Node* hsStringTable::AddRecur(Node* root, const char* str) 
{
    Node* node = TRACKED_NEW Node(*str);
    node->sib = root->kid;
    root->kid = node;
    if (*(str+1)) return AddRecur(node,str+1);
    else return node;
}

//
// Remove a node recursive from the tree
//
void hsStringTable::RemoveNode(Node* root) 
{
    if (!root) return;
    RemoveNode(root->kid);
    RemoveNode(root->sib);
    delete root;
}

//
// Recurse through tree and call callback on each node
//
hsBool hsStringTable::IterateRecur(Node* root, hsStringTableCallback* callback)
{
    if (!root)
        return true;
    if (root->data)
    {
        if (!callback(root))
            return false;
    }
    if (root->kid)
    {
        if (!IterateRecur(root->kid,callback))
            return false;
    }
    if (root->sib)
    {
        if (!IterateRecur(root->sib,callback))
            return false;
    }
    return true;
}