/*
 *	A Tree Container class implementation
 *	Copyright(C) 2003-2020 Jinhao(cnjinhao@hotmail.com)
 *
 *	Distributed under the Boost Software License, Version 1.0.
 *	(See accompanying file LICENSE_1_0.txt or copy at
 *	http://www.boost.org/LICENSE_1_0.txt)
 *
 *	@file: nana/gui/widgets/detail/tree_cont.hpp
 */

#ifndef NANA_GUI_WIDGETS_DETAIL_TREE_CONT_HPP
#define NANA_GUI_WIDGETS_DETAIL_TREE_CONT_HPP
#include <stack>
#include <nana/push_ignore_diagnostic>

namespace nana
{
namespace widgets
{
namespace detail
{
		template<typename T>
		struct tree_node
		{
			typedef std::pair<std::string, T>	value_type;

			value_type	value;

			tree_node	*owner;
			tree_node	*next;
			tree_node	*child;

			tree_node(tree_node* owner)
				:owner(owner), next(nullptr), child(nullptr)
			{}

			~tree_node()
			{
				if(owner)
				{
					tree_node * t = owner->child;
					if(t != this)
					{
						while(t->next != this)
							t = t->next;
						t->next = next;
					}
					else
						owner->child = next;
				}

				tree_node * t = child;
				while(t)
				{
					tree_node * t_next = t->next;
					delete t;
					t = t_next;
				}
			}

			bool is_ancestor_of(const tree_node* child) const
			{
				while (child)
				{
					if (child == this)
						return true;

					child = child->owner;
				}
				return false;
			}

			tree_node * front() const
			{
				if (this->owner && (this != this->owner->child))
				{
					auto i = this->owner->child;
					while (i->next != this)
						i = i->next;

					return i;
				}
				return nullptr;
			}
		};

		template<typename UserData>
		class tree_cont
		{
			typedef tree_cont self_type;

		public:
			typedef UserData	element_type;
			typedef tree_node<element_type> node_type;
			typedef typename node_type::value_type	value_type;

			enum class enum_order
			{
				stop,		//Stop enumeration
				proceed_with_children,	
				proceed
			};

			tree_cont()
				:root_(nullptr)
			{}

			~tree_cont()
			{
				clear(&root_);
			}

			void clear(node_type* node)
			{
				while (node->child)
				{
					//If there is a sibling of child, the root_.child
					//will be assigned with the sibling.
					remove(node->child);
				}
			}

			bool verify(const node_type* node) const
			{
				if(node)
				{
					while(node->owner)
					{
						if(node->owner == &root_)
							return true;

						node = node->owner;
					}
				}
				return false;
			}

			node_type* get_root() const
			{
				return &root_;
			}

			node_type* get_owner(const node_type* node) const
			{
				return (verify(node) && (node->owner != &root_) ? node->owner : nullptr);
			}

			node_type * node(node_type* node, const std::string& key)
			{
				if(node)
				{
					for(node_type * child = node->child; child; child = child->next)
					{
						if(child->value.first == key)
							return child;
					}
				}
				return nullptr;
			}

			node_type* insert(node_type* node, const std::string& key, const element_type& elem)
			{
				if(nullptr == node)
					return insert(key, elem);
				
				if(verify(node))
				{
					node_type **new_node_ptr;
					if(node->child)
					{
						node_type* child = node->child;
						for(; child; child = child->next)
						{
							if(child->value.first == key)
							{
								child->value.second = elem;
								return child;
							}
						}

						child = node->child;
						while(child->next)
							child = child->next;

						new_node_ptr = &(child->next);
					}
					else
						new_node_ptr = &(node->child);

					*new_node_ptr = new node_type(node);

					(*new_node_ptr)->value.first = key;
					(*new_node_ptr)->value.second = elem;
					return (*new_node_ptr);
				}
				return nullptr;
			}

			node_type* insert(const std::string& key, const element_type& elem)
			{
				auto node = _m_locate<true>(key);

				//Doesn't return the root node
				if (node == &root_)
					return nullptr;

				if(node)
					node->value.second = elem;
				return node;
			}

			void remove(node_type* node)
			{
				if(verify(node))
					delete node;
			}

			node_type* find(const std::string& path) const
			{
				auto p = _m_locate(path);
				return (&root_ == p ? nullptr : p);
			}

			node_type* ref(const std::string& path)
			{
				auto p = _m_locate<true>(path);
				return (&root_ == p ? nullptr : p);
			}

			unsigned indent_size(const node_type* node) const
			{
				if(node)
				{
					unsigned indent = 0;
					for(;(node = node->owner); ++indent)
					{
						if(node == &root_)	return indent;
					}
				}
				return 0;
			}

			template<typename Functor>
			void for_each(node_type* node, Functor f) const
			{
				if(nullptr == node) node = root_.child;
				int state = 0;	//0: Sibling, the last is a sibling of node
								//1: Owner, the last is the owner of node
								//>= 2: Children, the last is is a child of the node that before this node.
				while(node)
				{
					enum_order order = f(*node, state);

					if (enum_order::stop == order)
					{
						return;
					}
					else if (node->child && (enum_order::proceed_with_children == order))
					{
						node = node->child;
						state = 1;
					}
					else if(node->next)
					{
						node = node->next;
						state = 0;
					}
					else
					{
						state = 1;
						if(node == &root_)	return;

						while(true)
						{
							++state;
							if(node->owner->next)
							{
								node = node->owner->next;
								break;
							}
							else
								node = node->owner;

							if(node == &root_)	return;
						}
					}
				}
			}

			template<typename PredAllowChild, typename PredAllowNode>
			unsigned child_size_if(const ::std::string& key, PredAllowChild pac, PredAllowNode pan) const
			{
				auto node = _m_locate(key);
				return (node ? child_size_if<PredAllowChild, PredAllowNode>(*node, pac, pan) : 0);
			}

			template<typename PredAllowChild, typename PredAllowNode>
			unsigned child_size_if(const node_type& node, PredAllowChild pac, PredAllowNode pan) const
			{
				unsigned size = 0;
				const node_type* pnode = node.child;
				while(pnode)
				{
					if (!pan(*pnode))
					{
						pnode = pnode->next;
						continue;
					}

					++size;
					if(pnode->child && pac(*pnode))
						size += child_size_if<PredAllowChild>(*pnode, pac, pan);

					pnode = pnode->next;
				}
				return size;
			}

			template<typename PredAllowChild, typename PredAllowNode>
			std::size_t distance_if(const node_type * node, PredAllowChild pac, PredAllowNode pan) const
			{
				if(nullptr == node)	return 0;
				const node_type * iterator = root_.child;

				std::size_t off = 0;
				std::stack<const node_type* > stack;

				while(iterator && iterator != node)
				{
					if (!pan(*iterator))
					{
						iterator = iterator->next;
						continue;
					}

					++off;

					if(iterator->child && pac(*iterator))
					{
						stack.push(iterator);
						iterator = iterator->child;
					}
					else
						iterator = iterator->next;

					while((nullptr == iterator) && stack.size())
					{
						iterator = stack.top()->next;
						stack.pop();
					}
				}
				return off;
			}

			template<typename PredAllowChild, typename PredAllowNode>
			node_type* advance_if(node_type* node, std::size_t off, PredAllowChild pac, PredAllowNode pan)
			{
				if(nullptr == node)	node = root_.child;

				std::stack<node_type* > stack;

				while(node && off)
				{
					if (!pan(*node))
					{
						node = node->next;
						continue;
					}

					--off;
					if(node->child && pac(*node))
					{
						stack.push(node);
						node = node->child;
					}
					else
						node = node->next;

					while(nullptr == node && stack.size())
					{
						node = stack.top();
						stack.pop();
						node = node->next;
					}
				}

				return node;
			}
		private:
			//Functor defintions

			struct each_make_node
			{
				each_make_node(self_type& self)
					:node(&(self.root_))
				{}

				bool operator()(const ::std::string& key_node)
				{
					node_type *child = node->child;
					node_type *tail = nullptr;
					while(child)
					{
						if(key_node == child->value.first)
						{
							node = child;
							return true;
						}
						tail = child;
						child = child->next;
					}

					child = new node_type(node);
					if(tail)
						tail->next = child;
					else
						node->child = child;

					child->value.first = key_node;
					node = child;
					return true;
				}

				node_type * node;
			};


			struct find_key_node
			{
				find_key_node(const self_type& self)
					:node(&self.root_)
				{}

				bool operator()(const ::std::string& key_node)
				{
					return ((node = _m_find(node->child, key_node)) != nullptr);
				}

				node_type *node;
			};
		private:
			static node_type* _m_find(node_type* node, const ::std::string& key_node)
			{
				while(node)
				{
					if(key_node == node->value.first)
						return node;

					node = node->next;
				}
				return nullptr;
			}

			template<typename Function>
			void _m_for_each(const ::std::string& key, Function function) const
			{
				//Ignores separaters at the begin of key.
				auto beg = key.find_first_not_of("\\/");
				if (key.npos == beg)
					return;

				auto end = key.find_first_of("\\/", beg);


				while(end != ::std::string::npos)
				{
					if(beg != end)
					{
						if(!function(key.substr(beg, end - beg)))
							return;
					}

					auto next = key.find_first_not_of("\\/", end);

					if ((next == ::std::string::npos) && end)
						return;

					if (0 == end)
					{
						if ((!function(key.substr(0, 1))) || (next == ::std::string::npos))
							return;
					}

					beg = next;
					end = key.find_first_of("\\/", beg);

				}

				function(key.substr(beg, key.size() - beg));
			}

			template<bool CreateIfNotExists>
			node_type* _m_locate(const ::std::string& key)
			{
				if(key.size())
				{
					if(CreateIfNotExists)
					{
						each_make_node emn(*this);
						_m_for_each<each_make_node&>(key, emn);
						return emn.node;
					}
					else
					{
						find_key_node fkn(*this);
						_m_for_each<find_key_node&>(key, fkn);
						return const_cast<node_type*>(fkn.node);
					}
				}
				return &root_;
			}

			node_type* _m_locate(const std::string& key) const
			{
				if(key.size())
				{
					find_key_node fkn(*this);
					_m_for_each<find_key_node&>(key, fkn);
					return fkn.node;
				}
				return &root_;
			}
		private:
			mutable node_type root_;
		};//end class tree_cont
}//end namespace detail
}//end namespace widgets
}//end namesace nana

#include <nana/pop_ignore_diagnostic>
#endif
