#include <ZUtil/ZKVTree.hpp>
#include <ZUtil/ZLog.hpp>

#include <ZSTL/ZBasicStringAlgo.hpp>
#include <ZSTL/ZArrayAlgo.hpp>

/*************************************************************************/

void ZKVTree::NodeCheckIn(Node* _node)
{
	for (size_t i = 0; i < _node->Children.Size(); i++)
		NodeCheckIn(_node->Children[i]);

	_node->Children.Clear();

	NodeAllocator.Deallocate(_node);
}

/*************************************************************************/

ZKVTree::ZKVTree()
	: RootNode()
{
	RootNode.ParentNode = NULL;
	RootNode.Index = 0;
}

/*************************************************************************/

ZKVTree::ZKVTree( const ZKVTree& _other )
	: RootNode()
{
	RootNode.ParentNode = NULL;
	RootNode.Index = 0;

	for (ZKVTree::Iterator itr = _other.Begin(); itr != _other.End(); ++itr) {
		Put(itr, itr.GetValue());
	}
}

/*************************************************************************/

ZKVTree::~ZKVTree()
{
	for (size_t i = 0; i < RootNode.Children.Size(); i++)
		NodeCheckIn(RootNode.Children[i]);
}

/*************************************************************************/

ZKVTree& ZKVTree::operator = ( const ZKVTree& _other )
{
	Clear();

	for (ZKVTree::Iterator itr = _other.Begin(); itr != _other.End(); ++itr) {
		Put(itr.GetPath(), itr.GetValue());
	}

	return *this;
}

/*************************************************************************/

// This will convert to a full path, with subscripts everywhere and path separators.  EXAMPLES:
//
//   A.B[2].C.d -> A[0].B[2].C[0].d[0]
//   A.B[0][1]	-> A[0].B[0].[1]
//
static bool ConvertPath(const ZString& _path, ZString& _out)
{
	const ZString zeroSubscript("[0]");

	size_t i = 0;
	size_t j = 0;
	size_t k = 0;
	
	//We are going to loop, looking for path separator and making sure we have subscripts and proper path separators
	while (i != ZSTL::InvalidPos) {
		// find the next path separator or the first occurrence of a nested array access
		j = ZStringAlgo::FindFirst(_path, ZKVTREE_PATH_SEPARATOR, i, _path.Length());
		k = ZStringAlgo::FindSub(_path, i, _path.Length(), "][", 0, 2);

		// if a nested array access occurs
		if (k < j) {
			// append the first part of it, a path separators, and then process from there
			ZStringAlgo::Append(_out, _path, i, k + 1);

			_out.PushBack(ZKVTREE_PATH_SEPARATOR);

			i = k + 1;
		} else if (i == j) {
			// this means we are dealing with a node without a name (potentially first node)
			if (_out.Empty() || _out.Back() != ']')
				ZStringAlgo::Append(_out, zeroSubscript);

			_out.PushBack(ZKVTREE_PATH_SEPARATOR);

			i = j + 1;
		} else if (j != ZSTL::InvalidPos) {
			// found a substring between two path separators
			ZStringAlgo::Append(_out, _path, i, j);

			if (_out.Back() != ']')
				ZStringAlgo::Append(_out, zeroSubscript);

			_out.PushBack(ZKVTREE_PATH_SEPARATOR);

			i = j + 1;
		} else {
			// we have reached the end
			ZStringAlgo::Append(_out, _path, i, _path.Length());

			if (_out.Empty() || _out.Back() != ']')
				ZStringAlgo::Append(_out, zeroSubscript);

			i = ZSTL::InvalidPos;
		} 
	}

	// this method may be expanded later for path verification via regex or some such
	return true;
}

//This will insert a child node at the given index and increment the indices on the other child values
static void InsertNode( ZKVTree::Node* _parent, ZKVTree::Node* _child, size_t _index )
{
	_parent->Children.Insert(_index, _child);

	for (size_t i = _index + 1; i < _parent->Children.Size(); i++)
		_parent->Children.Data()[i]->Index++;
}

//This will erase a child node at the given index and decrement the indices on the other child values
static ZKVTree::Node* EraseChildNode( ZKVTree::Node* _parent, size_t _index )
{
	for (size_t i = _index + 1; i < _parent->Children.Size(); i++)
		_parent->Children.Data()[i]->Index--;

	return _parent->Children.Erase(_index);
}

/*************************************************************************/

// gets the value from a completed path (returned from ConvertFullPath)
ZKVTree::Node* ZKVTree::GetFromPath(const ZString& _path) const
{
	size_t i, j, k;
	size_t subscript;

	const Node* node = &RootNode;

	// path[i...j] is our path window (current value we are looking for)
	//   path[subscript].nextpath
	//   ^   ^          ^
	//   i   j          k
	// or
	//   path.nextpath
	//   ^   ^
	//   i  j,k
	i = 0;
	j = 0;
	k = 0;
	
	//While we still have a node to look in
	while (node != NULL)
	{
		//See if we have reached the end
		if (i >= _path.Length())
			break;

		subscript = 0;

		j = ZStringAlgo::FindFirst(_path, ZKVTREE_PATH_SEPARATOR, i, _path.Length());

		//If we can't find a path separator, go to the end
		if (j == ZSTL::InvalidPos)
			j = _path.Length();

		//See if we have found the node
		if (i >= j)
			break;

		k = j;

		//Determine if it has a subscript
		if (_path[j - 1] == ']')
		{			
			j = ZStringAlgo::FindLast(_path, '[', i, j);

			//Make sure this is bounded correct
			if (j == ZSTL::InvalidPos || j < i)
			{
				node = NULL;
				break;
			}

			subscript = (size_t)ZStringAlgo::NumericInt(_path, j + 1, k - 1);
		}

		node = node->GetChildByName(_path, i, j, subscript);

		//Move up our path 'window'
		i = k + 1;
	}

	return const_cast<ZKVTree::Node*>(node);
}

/*************************************************************************/

ZKVTree::Node* ZKVTree::CreatePath(const ZString& _path)
{
	size_t i, j, k;
	size_t subscript;

	Node* node = &RootNode;

	// path[i...j] is our path window (current value we are looking for)
	//   path[subscript].nextpath
	//   ^   ^          ^
	//   i   j          k
	// or
	//   path.nextpath
	//   ^   ^
	//   i  j,k
	i = 0;
	j = 0;
	k = 0;
	
	// while we still have a node to look in
	while (node != NULL) {
		// see if we have reached the end
		if (i >= _path.Length()) {
			break;
		}

		subscript = 0;

		j = ZStringAlgo::FindFirst(_path, ZKVTREE_PATH_SEPARATOR, i, _path.Length());

		//If we can't find a path separator, go to the end
		if (j == ZSTL::InvalidPos) {
			j = _path.Length();
		}

		//See if we are done creating
		if (i == j) {
			break;
		}

		k = j;

		// determine if it has a subscript
		if (_path[j - 1] == ']') {
			j = ZStringAlgo::FindLast(_path, '[', i, j);

			// make sure this is bounded correct (if not, bail out)
			if (j == ZSTL::InvalidPos || j < i) {
				node = NULL;
				break;
			}

			subscript = (size_t)ZStringAlgo::NumericInt(_path, j + 1, k - 1);
		}

		Node* childNode = node->GetChildByName(_path, i, j, subscript);

		// create this node if it doesn't exist
		if (childNode == NULL) {
			// get the child count by name at the parent node
			size_t count = node->GetChildCountByName(_path, i, j);

			// see if the subscript at the end is the 'next' value (if not, bail out)
			if (count != subscript) {
				node = NULL;
				break;
			}

			// get a new node, set the parent node, name, index (leave out the value)
			childNode = NodeAllocator.Allocate();

			childNode->ParentNode = node;			
			childNode->Name.Resize(j - i);
			ZStringAlgo::Copy(childNode->Name, 0, childNode->Name.Length(), _path, i, j);

			if (count > 0) {
				childNode->Index = node->GetLastIndexByName(_path, i, j) + 1;

				// increment the index on the rest of the nodes
				for (size_t i = childNode->Index; i < node->Children.Size(); i++)
					node->Children[i]->Index++;

				node->Children.Insert(childNode->Index, childNode);
			} else {
				childNode->Index = node->Children.Size();
				node->Children.PushBack(childNode);
			}
		}

		// set our new node
		node = childNode;

		// move up our 'path window'
		i = k + 1;
	}
	
	return node;
}

/*************************************************************************/

ZKVTree::Iterator ZKVTree::Add( const ZString& _name, const ZString& _value )
{
	return Add(End(), _name, _value);
}

/*************************************************************************/

ZKVTree::Iterator ZKVTree::Add( const ZString& _parent, const ZString& _name, const ZString& _value )
{
	ZString path;

	if (!ConvertPath(_parent, path))
		return End();

	Node* node = CreatePath(path);

	return Add(Iterator(node, this), _name, _value);
}

/*************************************************************************/

ZKVTree::Iterator ZKVTree::Add( const Iterator& _itr, const ZString& _name, const ZString& _value )
{
	ZKVTree::Node* parentNode = NULL;
	ZKVTree::Node* childNode = NULL;

	if (_itr.CurrentNode == NULL) {
		return End();
	}

	if (_itr.KVTree == this) {
		parentNode = _itr.CurrentNode;

		// allocate a child node and set values
		childNode = NodeAllocator.Allocate();
		childNode->ParentNode = parentNode;
		childNode->Name = _name;
		childNode->Value = _value;

		// determine if any other children of the same name exist (grouped on insert)
		size_t count = parentNode->GetChildCountByName(_name, 0, _name.Length());

		if (count > 0) {
			childNode->Index = parentNode->GetLastIndexByName(_name, 0, _name.Length()) + 1;
		} else {
			childNode->Index = parentNode->Children.Size();
		}
		
		InsertNode(parentNode, childNode, childNode->Index);

		return Iterator(childNode, this);
	} else {
		// if this is from another tree, just add at the path
		return Add(_itr.GetPath(), _name, _value);
	}	
}


/*************************************************************************/

void ZKVTree::Clear()
{
	for (size_t i = 0; i < RootNode.Children.Size(); i++) {
		NodeCheckIn(RootNode.Children.Data()[i]);
	}

	RootNode.Children.Clear();
}

/*************************************************************************/

void ZKVTree::Erase( const ZString& _path )
{
	Erase(Find(_path));
}

/*************************************************************************/

void ZKVTree::Erase( const Iterator& _itr )
{
	ZASSERT_RUNTIME(_itr.CurrentNode != NULL, "ZKVTree: Unable to erase value from provided iterator!");
	ZASSERT_RUNTIME(_itr.CurrentNode != &RootNode, "ZKVTree: Unable to erase root node!");

	if (_itr.KVTree == this) {
		NodeCheckIn(EraseChildNode(_itr.CurrentNode->ParentNode, _itr.CurrentNode->Index));
	} else {
		Erase(_itr.GetPath());
	}
}

/*************************************************************************/

ZKVTree::Iterator ZKVTree::Find( const ZString& _path ) const
{
	ZString path;

	if (!ConvertPath(_path, path)) {
		return End();
	}

	Node* node = GetFromPath(path);

	if (node == NULL) {
		return End();
	}

	return ZKVTree::Iterator(node, const_cast<ZKVTree*>(this));
}

/*************************************************************************/

ZKVTree::Iterator ZKVTree::Find( const Iterator& _itr, const ZString& _path ) const
{
	ZString path;

	if (!ConvertPath(_path, path)) {
		return End();
	}

	Node* node = GetFromPath(_itr.GetPath() + ZKVTREE_PATH_SEPARATOR + path);

	if (node == NULL) {
		return End();
	}

	return ZKVTree::Iterator(node, const_cast<ZKVTree*>(this));

}
/*************************************************************************/

const ZString& ZKVTree::Get( const ZString& _path ) const
{
	ZKVTree::Iterator itr = Find(_path);

	ZASSERT_RUNTIME(itr != End(), "ZKVTree: No value mapped to path!");

	return itr.GetValue();
}

/*************************************************************************/

const ZString& ZKVTree::Get( const Iterator& _itr, const ZString& _path )
{
	ZKVTree::Iterator itr = Find(_itr, _path);

	ZASSERT_RUNTIME(itr != End(), "ZKVTree: No value mapped to path!");

	return itr.GetValue();
}
/*************************************************************************/

void ZKVTree::Merge( const ZKVTree& _other, bool _overwrite )
{
	for (ZKVTree::Iterator itr = _other.Begin(); itr != _other.End(); ++itr) {
		if (_overwrite) {
			Put(itr.GetPath(), itr.GetValue());
		} else {
			ZKVTree::Iterator found = Find(itr.GetPath());
			if (found == End()) {
				Put(itr.GetPath(), itr.GetValue());
			}
		}
	}
}

/*************************************************************************/

ZKVTree::Iterator ZKVTree::Put( const ZString& _path, const ZString& _value )
{
	ZString path;

	if (!ConvertPath(_path, path))
		return End();

	// create the node at the path given (will return the existing node if there)
	Node* node = CreatePath(path);

	// if we were able to successfully create the node, set its value
	if (node != NULL) {
		node->Value = _value;
		return Iterator(node, this);
	} else return End();
}

/*************************************************************************/

ZKVTree::Iterator ZKVTree::Put(const Iterator& _itr, const ZString& _value)
{
	// ensure this iterator belongs to us
	if (_itr.KVTree == this) {
		// it does, just set the value
		_itr.CurrentNode->Value = _value;
		return _itr;
	} else {
		// it does not, just create a new value using the path variant
		return Put(_itr.GetPath(), _value);
	}
}

/*************************************************************************/

ZKVTree::Iterator ZKVTree::Begin() const
{ 
	if (RootNode.Children.Empty()) {
		return End();
	} else return ZKVTree::Iterator(const_cast<Node*>(RootNode.Children.Front()), const_cast<ZKVTree*>(this)); 
}

/*************************************************************************/

const ZKVTree::Iterator ZKVTree::End() const
{
	return ZKVTree::Iterator(const_cast<Node*>(&RootNode), const_cast<ZKVTree*>(this));
}

/*************************************************************************/

static bool isInvalidKeyNameChar(const char _char)
{
        return  ( isspace(_char) != 0)				||
                ( iscntrl(_char) != 0)				||
                ( _char == ZKVTREE_PATH_SEPARATOR ) ||
                ( _char == '[' )					||
                ( _char == ']' );
}

bool ZKVTree::IsValidKeyName( const ZString& _name ) const
{
    for (size_t i = 0; i < _name.Length(); i++) {
        if (isInvalidKeyNameChar( _name.Data()[i])) {
            return false;
        }
    }

    return true;
}
