/**
 * \file
 * \brief MutexControlBlock class header
 *
 * \author Copyright (C) 2014-2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info
 *
 * \par License
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
 * distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#ifndef INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_MUTEXCONTROLBLOCK_HPP_
#define INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_MUTEXCONTROLBLOCK_HPP_

#include "distortos/internal/scheduler/ThreadList.hpp"

#include "distortos/internal/synchronization/MutexListNode.hpp"

#include "distortos/TickClock.hpp"

namespace distortos
{

namespace internal
{

/// MutexControlBlock class is a control block for Mutex
class MutexControlBlock : public MutexListNode
{
public:

	/// mutex protocols
	enum class Protocol : uint8_t
	{
		/// no protocol, similar to PTHREAD_PRIO_NONE
		none,
		/// priority inheritance protocol, similar to PTHREAD_PRIO_INHERIT
		priorityInheritance,
		/// priority protection protocol (Immediate Ceiling Priority Protocol), similar to PTHREAD_PRIO_PROTECT
		priorityProtect,
	};

	/**
	 * \brief MutexControlBlock constructor
	 *
	 * \param [in] protocol is the mutex protocol
	 * \param [in] priorityCeiling is the priority ceiling of mutex, ignored when protocol != Protocol::priorityProtect
	 */

	constexpr MutexControlBlock(const Protocol protocol, const uint8_t priorityCeiling) :
			MutexListNode{},
			blockedList_{},
			owner_{},
			protocol_{protocol},
			priorityCeiling_{priorityCeiling}
	{

	}

	/**
	 * \brief Blocks current thread, transferring it to blockedList_.
	 *
	 * \return 0 on success, error code otherwise:
	 * - values returned by Scheduler::block();
	 */

	int block();

	/**
	 * \brief Blocks current thread with timeout, transferring it to blockedList_.
	 *
	 * \param [in] timePoint is the time point at which the thread will be unblocked (if not already unblocked)
	 *
	 * \return 0 on success, error code otherwise:
	 * - values returned by Scheduler::blockUntil();
	 */

	int blockUntil(TickClock::time_point timePoint);

	/**
	 * \brief Gets "boosted priority" of the mutex.
	 *
	 * "Boosted priority" of the mutex depends on the selected priority protocol:
	 * - None - 0,
	 * - PriorityInheritance - effective priority of the highest priority thread blocked on this mutex or 0 if no
	 * threads are blocked,
	 * - PriorityProtect - priority ceiling.
	 *
	 * \return "boosted priority" of the mutex
	 */

	uint8_t getBoostedPriority() const;

	/**
	 * \return owner of the mutex, nullptr if mutex is currently unlocked
	 */

	ThreadControlBlock* getOwner() const
	{
		return owner_;
	}

	/**
	 * \return priority ceiling of mutex, valid only when protocol_ == Protocol::priorityProtect
	 */

	uint8_t getPriorityCeiling() const
	{
		return priorityCeiling_;
	}

	/**
	 * \return mutex protocol
	 */

	Protocol getProtocol() const
	{
		return protocol_;
	}

	/**
	 * \brief Performs actual locking of previously unlocked mutex.
	 *
	 * \attention mutex must be unlocked
	 */

	void lock();

	/**
	 * \brief Performs unlocking or transfer of lock from current owner to next thread on the list.
	 *
	 * Mutex is unlocked if blockedList_ is empty, otherwise the ownership is transfered to the next thread.
	 *
	 * \attention mutex must be locked
	 */

	void unlockOrTransferLock();

private:

	/**
	 * \brief Performs action required for priority inheritance before actually blocking on the mutex.
	 *
	 * This must be called in block() and blockUntil() before actually blocking of the calling thread.
	 *
	 * \attantion mutex's protocol must be PriorityInheritance
	 */

	void priorityInheritanceBeforeBlock() const;

	/**
	 * \brief Performs transfer of lock from current owner to next thread on the list.
	 *
	 * \attention mutex must be locked and blockedList_ must not be empty
	 */

	void transferLock();

	/**
	 * \brief Performs actual unlocking of previously locked mutex.
	 *
	 * \attention mutex must be locked and blockedList_ must be empty
	 */

	void unlock();

	/// ThreadControlBlock objects blocked on mutex
	ThreadList blockedList_;

	/// owner of the mutex
	ThreadControlBlock* owner_;

	/// mutex protocol
	Protocol protocol_;

	/// priority ceiling of mutex, valid only when protocol_ == Protocol::priorityProtect
	uint8_t priorityCeiling_;
};

}	// namespace internal

}	// namespace distortos

#endif	// INCLUDE_DISTORTOS_INTERNAL_SYNCHRONIZATION_MUTEXCONTROLBLOCK_HPP_