//-------------------------------------------------------------------------------
// <copyright file="LqrSMACTimer.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 for Timer class, which represents a single timer.  When used
//  stand-alone, it can be polled; when used with a TimerCollection class, it can
// </summary>
//
// <owner>Mike Cookson</owner>
//-------------------------------------------------------------------------------

#ifndef __LqrSMACTimer_H__
#define __LqrSMACTimer_H__

#include "LqrSMACSharedLib.h"
#include "LqrSMACLockableConcept.h"
#include <boost/shared_ptr.hpp>
#include <boost/signals2.hpp>
#include <boost/thread/locks.hpp>
#include <boost/thread/recursive_mutex.hpp>

namespace LiquidRobotics {
namespace SMACFramework {

class TimerCollectionBase;

class SHARED_LIB_PUBLIC Timer : public LockableConceptMutex
{
public:

	typedef boost::shared_ptr<Timer> Ptr;
	typedef unsigned TimeUnits;
	typedef boost::signals2::scoped_connection ScopedConnection;
	typedef boost::shared_ptr<ScopedConnection> ScopedConnectionPtr;
	typedef boost::shared_ptr<TimerCollectionBase> TimerCollectionBasePtr;
	typedef boost::signals2::signal<void (Timer& sender)> TimerExpiredSignalType;

	Timer(TimeUnits initialCounter = 0, bool _retriggers = false, bool running = true, TimerCollectionBasePtr _timerColl = TimerCollectionBasePtr());
	Timer(Timer& rhs);

	virtual ~Timer();

	Timer& operator=(const Timer& rhs);

	virtual int Compare(const Timer& rhs) const;

	TimeUnits Elapsed(float __now = -1.0f) const;

	bool Expired(float __now = -1.0f) const;

	bool FullCount() const;
	void FullCount(TimeUnits newValue);

	TimerCollectionBasePtr Owner() const;

	void Pause();

	TimeUnits Remaining(float __now = -1.0f) const;

	void Reset();

	void Restart(TimeUnits newValue, bool retrig = false, bool run = true);

	bool Retriggers() const;
	void Retriggers(bool newValue);

	void Resume();

	bool Running() const;

	bool UpdateAndFire(float __now = -1.0f);

	ScopedConnectionPtr SubscribeTimerExpired(TimerExpiredSignalType::slot_type slot);

protected:

	template<typename T>
	static int CompareT(const T& v1, const T& v2)
	{
		int result = 0;

		if (v1 < v2)
			result = -1;
		else if (v1 > v2)
			result = 1;

		return result;
	}

	template<typename T>
	static void CompareT(int& compareResult, const T& v1, const T& v2)
	{
		if (!compareResult)
		{
			if (v1 < v2)
				compareResult = -1;
			else if (v1 > v2)
				compareResult = 1;
		}
	}

	TimerCollectionBasePtr owner;					// timer collection that offers up events
	float startTime;								// system up time when run starts or -1f if not running
	TimeUnits remainingCount;						// time count remaining if not running
	TimeUnits fullCount;							// full count when then timer was set
	bool retriggers;								// true if the timer automatically retriggers
	TimerExpiredSignalType timerExpiredSignal;		// signal that informs subscribers of expiration
};

typedef boost::shared_ptr<Timer> TimerPtr;

template<typename TData>
class TimerWithData : public Timer
{
public:

	TimerWithData(const TData& initialDataValue, TimeUnits initialCounter = 0, bool _retriggers = false, bool running = true, TimerCollectionBasePtr _timerColl = TimerCollectionBasePtr())
	: Timer(initialCounter, _retriggers, running, _timerColl)
	, dataValue(initialDataValue)
	{
	}

	TimerWithData(TimerWithData<TData>& rhs)
	: Timer(rhs)
	, dataValue(rhs.dataValue)
	{
	}

	TimerWithData<TData>& operator=(const TimerWithData<TData>& rhs)
	{
		if (this != &rhs)
		{
			((Timer&) *this) = ((const Timer&) rhs);
			dataValue = rhs.dataValue;
		}

		return *this;
	}

	const TData& GetData() const
	{
		return dataValue;
	}

	void SetData(const TData& newDataValue)
	{
		dataValue = newDataValue;
	}

private:

	TData dataValue;
};

}
}

#endif
