//----------------------------------------------------------------------------------
// <copyright file="SensorConcreteCommand.h" 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>
//   Declarations/implementation of SensorConcreteCommand template class, from which
//	 all commands are derived.  The purpose is to make some decisions about command
//	 implementation that are common to all derived commands.  The template parameters
//	 are:
//
//		- TControllerBase, the controller interface that is made available to all
//		  commands (usually abstract)
//
//		- TCmdBase, the abstract base class that defines command operations
//
//		- TCmdID, the type if command ID that is used to track commands
//
//		- TExceptions, a class-based namespaces for exceptions that can be thrown
// </summary>
//
// <owner>Mike Cookson</owner>
//----------------------------------------------------------------------------------

#ifndef __EventLib_SensorConcreteCommand_h__
#define __EventLib_SensorConcreteCommand_h__

#include "LqrSMACSharedLib.h"
#include "LqrSMACSemaphore.h"
#include <stdexcept>
#include <sstream>
#include <typeinfo>
#include <boost/algorithm/string.hpp>
#include <boost/shared_ptr.hpp>

namespace LiquidRobotics {
namespace EventLib {

template<class TControllerBase, class TCmdBase, class TCmdID, class TExceptions>
class SHARED_LIB_PUBLIC SensorConcreteCommand : public TCmdBase
{
public:

	typedef TControllerBase TCon;
	typedef TCmdBase TCmd;
	typedef TCmdID TCID;
	typedef TExceptions TEx;
	typedef boost::shared_ptr<TCmdBase> CmdBasePtr;
	typedef SensorConcreteCommand<TControllerBase,TCmdBase,TCmdID,TExceptions> SensorConcreteCommandT;
	typedef LiquidRobotics::SMACFramework::Semaphore Semaphore;
	typedef std::string string;
	typedef LiquidRobotics::SMACFramework::CmdOutputFormatter CmdOutputFormatter;

	SensorConcreteCommand(TControllerBase& _intf)
	: intf(_intf)
	, startSem(0)
	, completeSem(0)
	, exType(TExceptions::NoException)
	, successFlag(true)
	{
	}

	virtual void Abort(const string& reasonCode)
	{
		typename TExceptions::Aborted ex("");
		const int exNum = TExceptions::ExceptionToNumber(ex);

		SetException(exNum, reasonCode);

	}

	virtual void ActionStart()
	{
		Semaphore::Lock semLock(startSem);
		semLock.unlock();
		DoThrow();
	}

	virtual bool ActionWait(string& result)
	{
		Semaphore::Lock semLock(completeSem);
		semLock.unlock();

		if (formatter)
			result = formatter->ToString();

		DoThrow();
		return true;
	}

	virtual void Execute()
	{
		typename TExceptions::NotReady ex("");
		const int exNum = TExceptions::ExceptionToNumber(ex);

		SetException(exNum, "The execution class is invalidly constructed with a no-op Execute method");
	}

	virtual const TCmdID& GetCompletionParams() const
	{
		return cmdID;
	}

	virtual CmdOutputFormatter::Ptr GetOutputFormatter()
	{
		return formatter;
	}

	virtual string GetResult()
	{
		using namespace std;

		if (formatter)
			reasonCode = formatter->ToString();

		DoThrow();
		return reasonCode;
	}

	virtual bool IsBlocking() const
	{
		return cmdID.IsBlocking();
	}

	virtual void SetCompletionParams(const TCmdID& newValue)
	{
		cmdID = newValue;
	}

	virtual void SetOutputFormatter(CmdOutputFormatter::Ptr _formatter)
	{
		formatter =_formatter;
	}

	virtual void SignalStarted()
	{
		if (formatter) formatter->FlagStarted();
		startSem.Post();
	}

	virtual void SignalComplete()
	{
		if (formatter && !formatter->FlaggedCompleted())
			formatter->FlagCompleted();

		startSem.Post();
		completeSem.Post();
	}

	virtual void SetSuccessFlag(bool newState)
	{
		successFlag = newState;
	}

	virtual bool GetSuccessFlag() const
	{
		return successFlag;
	}

protected:

	void DoThrow()
	{
		TExceptions::ThrowException(exType, reasonCode);
	}

	void SetException(int _exType, const string& _reasonCode)
	{
		exType = _exType;
		reasonCode = _reasonCode;

		if (formatter && !formatter->FlaggedCompleted())
		{
			try							{ DoThrow(); }
			catch(std::exception& ex)	{ formatter->FlagCompleted(ex); }
		}
	}

	TControllerBase& intf;
	TCmdID cmdID;
	Semaphore startSem;
	Semaphore completeSem;
	int exType;
	string reasonCode;
	CmdOutputFormatter::Ptr formatter;
	bool successFlag;
};

}
}

#endif
