/*
    libmaus2
    Copyright (C) 2016 German Tischler

    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_AIO_SIZEMONITORTHREAD_HPP)
#define LIBMAUS2_AIO_SIZEMONITORTHREAD_HPP

#include <libmaus2/parallel/StdThread.hpp>
#include <libmaus2/util/GetFileSize.hpp>
#include <libmaus2/parallel/StdSpinLock.hpp>
#include <ctime>
#include <libmaus2/util/UnitNum.hpp>
#include <libmaus2/aio/StreamLock.hpp>

namespace libmaus2
{
	namespace aio
	{
		struct SizeMonitorThreadCallable : public libmaus2::parallel::StdThreadCallable
		{
			std::string fn;
			uint64_t sleeptime;
			uint64_t maxsize;
			std::atomic<int> stop;
			libmaus2::parallel::StdSpinLock stoplock;
			std::ostream * sizestream;

			bool getStop()
			{
				int lstop;
				{
					libmaus2::parallel::ScopeStdSpinLock slock(stoplock);
					lstop = stop;
				}
				return lstop;
			}

			void setStop(int const rstop)
			{
				libmaus2::parallel::ScopeStdSpinLock slock(stoplock);
				stop = rstop;
			}

			SizeMonitorThreadCallable(std::string const & rfn, uint64_t const rsleeptime = 5, std::ostream * rsizestream = 0)
			: fn(rfn), sleeptime(rsleeptime), maxsize(0), stop(0), sizestream(rsizestream)
			{

			}

			void printSize(std::ostream & stream)
			{
				libmaus2::aio::StreamLock::lock_type::scope_lock_type slock(libmaus2::aio::StreamLock::cerrlock);
				stream << "[S]\t" << fn << "\t" << libmaus2::util::UnitNum::unitNum(maxsize) << std::endl;
			}

			virtual void run()
			{
				while ( ! getStop() )
				{
					uint64_t const s = libmaus2::util::GetFileSize::getDirSize(fn);
					// (*sizestream) << "[S]\t" << fn << "\t" << s << std::endl;

					if ( s > maxsize )
					{
						maxsize = s;
						if ( sizestream )
							printSize(*sizestream);
					}

					struct timespec req, rem;

					req.tv_sec = sleeptime;
					req.tv_nsec = 0;
					rem.tv_sec = 0;
					rem.tv_nsec = 0;

					nanosleep(&req,&rem);
				}
			}
		};

		struct SizeMonitorThreadCallableContainer
		{
			libmaus2::parallel::StdThreadCallable::unique_ptr_type ptr;

			SizeMonitorThreadCallableContainer(std::string const & rfn, uint64_t const rsleeptime = 5, std::ostream * rsizestream = 0)
			: ptr(new SizeMonitorThreadCallable(rfn,rsleeptime,rsizestream))
			{

			}
		};

		struct SizeMonitorCancel
		{
			SizeMonitorThreadCallable & callable;

			SizeMonitorCancel(SizeMonitorThreadCallable & rcallable)
			: callable(rcallable)
			{

			}

			~SizeMonitorCancel()
			{
				callable.setStop(1);
			}
		};

		struct SizeMonitorThread : public SizeMonitorThreadCallableContainer, public libmaus2::parallel::StdThread
		{
			SizeMonitorThreadCallable & callable;
			SizeMonitorCancel smcancel;

			SizeMonitorThread(std::string const & rfn, uint64_t const rsleeptime = 5, std::ostream * rsizestream = 0)
			:
				SizeMonitorThreadCallableContainer(rfn,rsleeptime,rsizestream),
				libmaus2::parallel::StdThread(SizeMonitorThreadCallableContainer::ptr),
				callable(
					dynamic_cast<SizeMonitorThreadCallable &>(libmaus2::parallel::StdThread::getCallable())
				),
				smcancel(callable)
			{

			}

			void cancel()
			{
				callable.setStop(1);
				tryJoin();
			}

			void printSize(std::ostream & stream)
			{
				callable.printSize(stream);
			}
		};
	}
}
#endif
