/*
    libmaus2
    Copyright (C) 2020 German Tischler-Höhle

    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/>.
*/
#if ! defined(LIBMAUS2_PARALLEL_SIMPLETHREADPOOLTHREADIDNODE_HPP)
#define LIBMAUS2_PARALLEL_SIMPLETHREADPOOLTHREADIDNODE_HPP

#include <libmaus2/parallel/SimpleThreadPoolThreadId.hpp>
#include <libmaus2/autoarray/AutoArray.hpp>
#include <stack>

namespace libmaus2
{
	namespace parallel
	{
		struct SimpleThreadPoolThreadIdNode
		{
			SimpleThreadPoolThreadId data;
			SimpleThreadPoolThreadIdNode * left;
			SimpleThreadPoolThreadIdNode * right;

			SimpleThreadPoolThreadIdNode() : data(), left(nullptr), right(nullptr) {}
			SimpleThreadPoolThreadIdNode(SimpleThreadPoolThreadId const & rdata) : data(rdata), left(nullptr), right(nullptr) {}

			bool operator<(SimpleThreadPoolThreadIdNode const & O) const
			{
				return data < O.data;
			}

			SimpleThreadPoolThreadIdNode const * find(std::thread::id const & rid) const
			{
				if ( rid < data.id )
				{
					if ( left )
						return left->find(rid);
					else
						return nullptr;
				}
				else if ( data.id < rid )
				{
					if ( right )
						return right->find(rid);
					else
						return nullptr;
				}
				else
				{
					assert ( data.id == rid );

					return this;
				}
			}

			uint64_t count() const
			{
				uint64_t c = 1;

				if ( left )
					c += left->count();
				if ( right )
					c += right->count();

				return c;
			}

			libmaus2::autoarray::AutoArray<SimpleThreadPoolThreadId> enumerate() const
			{
				uint64_t const c = count();

				libmaus2::autoarray::AutoArray<SimpleThreadPoolThreadId> A(c);
				uint64_t o = 0;

				std::stack < std::pair<SimpleThreadPoolThreadIdNode const *, uint64_t > > S;
				S.push(std::make_pair(this,0));

				while ( !S.empty() )
				{
					std::pair<SimpleThreadPoolThreadIdNode const *, uint64_t > const P = S.top();
					S.pop();

					switch ( P.second )
					{
						case 0:
							S.push(std::make_pair(P.first,1));
							if ( P.first->left )
								S.push(std::make_pair(P.first->left,0));
							break;
						case 1:
							S.push(std::make_pair(P.first,2));
							A[o++] = P.first->data;
							break;
						case 2:
							S.push(std::make_pair(P.first,3));
							if ( P.first->right )
								S.push(std::make_pair(P.first->right,0));
							break;
					}
				}

				return A;
			}

			std::ostream & print(std::ostream & out) const
			{
				std::stack < std::pair<SimpleThreadPoolThreadIdNode const *, uint64_t > > S;
				S.push(std::make_pair(this,0));

				while ( !S.empty() )
				{
					std::pair<SimpleThreadPoolThreadIdNode const *, uint64_t > const P = S.top();
					S.pop();

					switch ( P.second )
					{
						case 0:
							S.push(std::make_pair(P.first,1));
							if ( P.first->left )
								S.push(std::make_pair(P.first->left,0));
							break;
						case 1:
							S.push(std::make_pair(P.first,2));
							out << P.first->data.toString();
							break;
						case 2:
							S.push(std::make_pair(P.first,3));
							if ( P.first->right )
								S.push(std::make_pair(P.first->right,0));
							break;
					}
				}

				return out;
			}
		};
	}
}
#endif
