/*
    libmaus2
    Copyright (C) 2009-2015 German Tischler
    Copyright (C) 2011-2013 Genome Research Limited

    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_STDTERMINATABLESYNCHRONOUSHEAP_HPP)
#define LIBMAUS2_PARALLEL_STDTERMINATABLESYNCHRONOUSHEAP_HPP

#include <queue>
#include <libmaus2/parallel/StdSemaphore.hpp>
#include <libmaus2/parallel/StdMutex.hpp>
#include <atomic>
#include <cassert>

namespace libmaus2
{
        namespace parallel
        {
                /**
                 * semaphore variable based version
                 **/
                template<typename _value_type, typename _compare = ::std::less<_value_type> >
                struct StdTerminatableSynchronousHeap
                {
                        typedef _value_type value_type;
                        typedef _compare compare;
                        typedef StdTerminatableSynchronousHeap<value_type,compare> this_type;

                        libmaus2::parallel::StdSemaphore semaphore;
                        libmaus2::parallel::StdMutex lock;

                        std::atomic<std::size_t> numwait;
                        std::priority_queue<value_type, std::vector<value_type>, compare > Q;
                        std::atomic<uint64_t> terminated;
                        uint64_t const terminatedthreshold;

                        StdTerminatableSynchronousHeap(uint64_t const rterminatedthreshold = 1)
                        : semaphore(), lock(), numwait(0), Q(), terminated(0), terminatedthreshold(rterminatedthreshold)
                        {

                        }

                        StdTerminatableSynchronousHeap(compare const & comp, uint64_t const rterminatedthreshold = 1)
                        : semaphore(), lock(), numwait(0), Q(comp), terminated(0), terminatedthreshold(rterminatedthreshold)
                        {

                        }

                        ~StdTerminatableSynchronousHeap()
                        {

                        }

                        // enque an element
                        void enque(value_type const v)
                        {
                        	libmaus2::parallel::ScopeStdMutex M(lock);
                                Q.push(v);
                                semaphore.post();
                        }

                        // get number of elements in the heap
                        size_t getFillState()
                        {
                                libmaus2::parallel::ScopeStdMutex M(lock);
                                uint64_t f = Q.size();
                                return f;
                        }

                        // is heap terminated?
                        bool isTerminated()
                        {
                        	libmaus2::parallel::ScopeStdMutex M(lock);
                                return terminated >= terminatedthreshold;
                        }

                        // terminated the heap
                        void terminate()
                        {
                        	libmaus2::parallel::ScopeStdMutex M(lock);
                                terminated += 1;
                        }

                        // get an element from the queue, throws an exception if the queue is terminated
                        value_type deque()
                        {
                                while ( true )
                                {
                                	bool const ok = semaphore.timedWait();

					if ( isTerminated() )
					{
						throw std::runtime_error("Heap is terminated");
					}
					else if ( ok )
					{
						libmaus2::parallel::ScopeStdMutex M(lock);

						// we have received a signal on the semaphore, the queue should be none empty
						assert ( Q.size() );

						value_type v = Q.top();
						Q.pop();
						return v;
                                	}
                                }
                        }

                        /*
                         * get current contents
                         */
                        std::vector<value_type> pending()
                        {
	                        std::priority_queue < value_type, std::vector<value_type>, compare > C;
	                        {
	                        	libmaus2::parallel::ScopeStdMutex M(lock);
	                        	C = Q;
				}
	                        std::vector<value_type> V;
	                        while ( ! C.empty() )
	                        {
	                        	V.push_back(C.top());
	                        	C.pop();
				}

				return V;
                        }
                };
        }
}
#endif
