//----------------------------------------------------------------------------------------------
// <copyright file="LqrSMACSemaphore.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 the Semaphore class, which wraps a Linux semaphore and provides support for
//  the Boost Lock concept
// </summary>
//
// <owner>Mike Cookson</owner>
//----------------------------------------------------------------------------------------------

#ifndef __LqrSMACSemaphore_H__
#define __LqrSMACSemaphore_H__

#include "LqrSMACDeclareException.h"
#include "LqrSMACLockableConcept.h"
#include "LqrSMACSharedLib.h"
#include <string>
#include <boost/thread/locks.hpp>
#include <boost/thread/thread_time.hpp>
#include <boost/utility.hpp>
#include <semaphore.h>
#include <time.h>
#include <sys/types.h>

namespace LiquidRobotics {
namespace SMACFramework {

class SHARED_LIB_PUBLIC Semaphore
: private boost::noncopyable
, public ILockableConcept
{
public:

	DECLARE_EXCEPTION(SystemError, std::runtime_error);
	DECLARE_EXCEPTION(Timeout, std::runtime_error);

	typedef boost::unique_lock<Semaphore> Lock;
	typedef std::string string;

	Semaphore(unsigned value = 1, bool shared = false);
	Semaphore(const string& semName, bool existing = false, bool overwriteExisting = false, unsigned value = 1, mode_t mode = 0666);

	virtual ~Semaphore();

	static void DeleteNamedInstance(const string& instanceName);
	int GetCount();
	void SetCount(int newCount);
	sem_t* GetSemPtr();
	void Post();
	bool TimedWait(unsigned millisec, bool throwOnTimeout = false);
    bool TimedWait(const timespec& to, bool throwOnTimeout = false);
	bool TryWait();
	void Wait();

	// Lockable concept used internally and usable by external actors
	virtual void lock();
	virtual bool try_lock();
	virtual void unlock();

    // TimedLockable concept
    virtual bool timed_lock(boost::system_time const& abs_time);

    template<typename DurationType>
    bool timed_lock(DurationType const& rel_time)
    {
        bool result;

        if (rel_time.is_special() || rel_time.is_negative())
            result = try_lock();
        else
        {
            using namespace boost;
            using namespace boost::posix_time;

            const system_time now = get_system_time();
            const system_time targetTime = now+rel_time;
            result = timed_lock(targetTime);
        }

        return result;
    }

private:

	static void DoThrow(int err, const string& api);
	static timespec GetTimeSpec(unsigned milliseconds);

	sem_t sem;
	sem_t* pSem;				// not null if semaphore was created using sem_open(), null if created using sem_init()
	bool semCreated;
};

}
}

#endif /* SEMAPHORE_H_ */
