//--------------------------------------------------------------------------------------------------------
// <copyright file="LqrSMACBackgroundThread.cpp" company="LiquidRobotics">
//
// Copyright (c) 2014 Liquid Robotics, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// This software is associated with a release.  Liquid Robotics makes no
// representation that the design, interfaces or component distribution will
// remain unchanged in future releases.
//
// </copyright>
//
// <summary>
//   Implementation of the BackgroundThread class, which encapsulates a thread that has
//   the following features:
//
//		- Uses boost signals2 library to call the background function(s)
//
//		- Has a Shutdown() method that signals the background function to abort
//
//		- Has a method (ShutdownQueued()) to poll whether the Shutdown() method was
//		  already called
//
//		- Has a virtual method (ThreadFunction()) that can be overridden to
//		  completely implement the background thread function.
//
//	Here's an example of its use:
//
//		#include "LqrSMACBackgroundThread.h"
//
//		class Calculator
//		{
//		public:
//
//			typedef LiquidRobotics::SMACFramework::BackgroundThread BackgroundThread;
//			typedef boost::signals2::scoped_connection ScopedConnection;
//			typedef boost::shared_ptr<ScopedConnection> ScopedConnectionPtr;
//
//			void Initialize()
//			{
//				bkConnection = bkThread.SubscribeCallback(boost::bind(&Calculator::IncrementalCalculator, this, _1);
//				bkThread.Initialize();
//			}
//
//			void Shutdown()
//			{
//				if (bkConnection)
//				{
//					LockableConceptMutex::Lock lock(bkThread.FiringMutex());
//					bkConnection.reset();
//				}
//
//				bkThread.Shutdown();
//			}
//
//		private:
//
//			bool IncrementalCalculator(BackgroundThread& sender)
//			{
//				// do some calculation work
//				return !calculationDone;
//			}
//
//			BackgroundThread bkThread;
//			ScopedConnectionPtr bkConnection;
//		};
//
// If desired, the callback function can remain active until completion and the
// state of the shutdown flag can be checked:
//
//			bool IncrementalCalculator(BackgroundThread& sender)
//			{
//				while(!sender.ShutdownQueued())
//				{
//					// do some calculation work
//				}
//
//				return false;
//			}
// </summary>
//
// <owner>Mike Cookson</owner>
//--------------------------------------------------------------------------------------------------------

#include "LqrSMACBackgroundThread.h"
#include <boost/make_shared.hpp>

using namespace std;
using namespace LiquidRobotics::SMACFramework;

namespace LiquidRobotics {
namespace SMACFramework {

BackgroundThread::BackgroundThread()
: initialized(false)
, shutdownSem(0)
{
}

BackgroundThread::~BackgroundThread()
{
}

void BackgroundThread::Initialize()
{
	Lock lock(*this);
	Lock lock2(firingMutex);

	if (!initialized)
		thdPtr = boost::make_shared<Thread>(boost::bind(&BackgroundThread::ThreadFunction, this));
}

void BackgroundThread::Shutdown()
{
	Lock lock(*this);

	if (initialized)
	{
		initialized = false;
		lock.unlock();
		shutdownSem.Post();
		thdPtr->join();
		thdPtr.reset();
	}
}

BackgroundThread::ScopedConnectionPtr BackgroundThread::SubscribeCallback(CallbackSignal::slot_type slot)
{
	ScopedConnectionPtr result = boost::make_shared<ScopedConnection>(callbackSignal.connect(slot));
	return result;
}

bool BackgroundThread::ShutdownQueued() const
{
	Lock lock(const_cast<BackgroundThread&>(*this));
	const bool queued = const_cast<Semaphore&>(shutdownSem).TryWait();

	if (queued)
		const_cast<Semaphore&>(shutdownSem).Post();

	return queued;
}

void BackgroundThread::ThreadFunction()
{
	// stop on queued shutdown or the caller's function returns false
	while(!ShutdownQueued())
	{
		Lock lock(firingMutex);

		if (!callbackSignal(*this))
			break;
	}

	// wait for queued shutdown synchronously
	Semaphore::Lock shutdownLock(shutdownSem);
}

ILockableConcept& BackgroundThread::FiringMutex() const
{
	return const_cast<ILockableConcept&>((const ILockableConcept&) firingMutex);
}

bool BackgroundThread::Initialized() const
{
	Lock lock(const_cast<BackgroundThread&>(*this));
	return !!initialized;
}

}
}
