/*
    libmaus2
    Copyright (C) 2019 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/>.
*/
#include <libmaus2/parallel/SimpleThreadPool.hpp>

struct PanicPackage : public libmaus2::parallel::SimpleThreadWorkPackage
{
	PanicPackage()
	: libmaus2::parallel::SimpleThreadWorkPackage() {}
	PanicPackage(uint64_t const rpriority, uint64_t const rdispatcherid, uint64_t const rpackageid = 0, uint64_t const rsubid = 0)
	: libmaus2::parallel::SimpleThreadWorkPackage(rpriority,rdispatcherid,rpackageid,rsubid) {}
	PanicPackage(PanicPackage const & O)
	: libmaus2::parallel::SimpleThreadWorkPackage(O) {}

	PanicPackage & operator=(PanicPackage const & O)
	{
		if ( this != &O )
		{
			libmaus2::parallel::SimpleThreadWorkPackage::operator=(O);
		}
		return *this;
	}

	virtual ~PanicPackage() {}

	virtual char const * getPackageName() const { return "PanicPackage"; }
};

struct PanicDispatcher : public libmaus2::parallel::SimpleThreadWorkPackageDispatcher
{
	virtual ~PanicDispatcher() {}
	virtual void dispatch(libmaus2::parallel::SimpleThreadWorkPackage * /*P*/, libmaus2::parallel::SimpleThreadPoolInterfaceEnqueTermInterface & /*tpi*/)
	{
		libmaus2::exception::LibMausException lme;
		lme.getStream() << "[E] cause panic exception" << std::endl;
		lme.finish();
		throw lme;
	}
};

struct WaitPackage : public libmaus2::parallel::SimpleThreadWorkPackage
{
	libmaus2::parallel::StdMutex * mutex;

	WaitPackage()
	: libmaus2::parallel::SimpleThreadWorkPackage(), mutex(nullptr) {}
	WaitPackage(libmaus2::parallel::StdMutex * rmutex, uint64_t const rpriority, uint64_t const rdispatcherid, uint64_t const rpackageid = 0, uint64_t const rsubid = 0)
	: libmaus2::parallel::SimpleThreadWorkPackage(rpriority,rdispatcherid,rpackageid,rsubid), mutex(rmutex) {}
	WaitPackage(WaitPackage const & O)
	: libmaus2::parallel::SimpleThreadWorkPackage(O), mutex(O.mutex) {}

	WaitPackage & operator=(WaitPackage const & O)
	{
		if ( this != &O )
		{
			libmaus2::parallel::SimpleThreadWorkPackage::operator=(O);
			mutex = O.mutex;
		}
		return *this;
	}

	virtual ~WaitPackage() {}

	virtual char const * getPackageName() const { return "WaitPackage"; }
};


struct WaitDispatcher : public libmaus2::parallel::SimpleThreadWorkPackageDispatcher
{
	virtual ~WaitDispatcher() {}
	virtual void dispatch(libmaus2::parallel::SimpleThreadWorkPackage * P, libmaus2::parallel::SimpleThreadPoolInterfaceEnqueTermInterface & /*tpi*/)
	{
		WaitPackage * WP = dynamic_cast<WaitPackage *>(P);
		libmaus2::parallel::StdMutex::scope_lock_type slock(*(WP->mutex));
	}
};


int main()
{
	try
	{
		libmaus2::parallel::SimpleThreadPool STP(5);

		uint64_t const panicdispatcherid = STP.getNextDispatcherId();
		uint64_t const waitdispatcherid = STP.getNextDispatcherId();
		PanicDispatcher PD;
		STP.registerDispatcher(panicdispatcherid,&PD);
		WaitDispatcher WD;
		STP.registerDispatcher(waitdispatcherid,&WD);

		libmaus2::parallel::StdMutex mutex;
		libmaus2::parallel::StdMutex::scope_lock_type slock(mutex);

		WaitPackage WP[4];
		PanicPackage PP;

		try
		{
			for ( uint64_t i = 0; i < sizeof(WP)/sizeof(WP[0]); ++i )
			{
				WP[i] = WaitPackage(&mutex,0/*prio*/,waitdispatcherid);
				STP.enque(&WP[i]);
			}

			PP = PanicPackage(0,panicdispatcherid);
			STP.enque(&PP);

			while ( ! STP.isInPanicMode() )
			{
				::sleep(1);
			}

		}
		catch(std::exception const & ex)
		{
			std::cerr << "*" << ex.what() << std::endl;
		}

		if ( STP.isInPanicMode() )
			std::cerr << "[V] thread pool is in panic mode" << std::endl;

		std::cerr << "[V] running join." << std::endl;

		STP.join();
	}
	catch(std::exception const & ex)
	{
		std::cerr << ex.what() << std::endl;
	}
}
