/**
 * @file
 */
#ifndef UI_UTIL_MISC_HPP
#define UI_UTIL_MISC_HPP

// STDC++
#include <string>
#include <vector>
#include <map>
#include <list>
#include <memory>

// C++ libraries
#include <ui-utilcpp/Exception.hpp>
#include <ui-utilcpp/Sys.hpp>
#include <ui-utilcpp/Text.hpp>

namespace UI {
namespace Util {

/** @brief Run "delete" on all vector elements, and clear the vector. *>.
 *
 * I.e. empty whole vector && call destructor for each ElType * - element.
 */
template <typename ElType>
void delAnySeqContainer(std::vector<ElType *> & l) throw()
{
	for (typename std::vector<ElType *>::iterator i(l.begin()); i != l.end(); ++i)
	{
		delete *i;
	}
	l.clear();
}

/** @brief Run "std::free" on all vector elements, and clear the vector. *>.
 *
 * I.e. empty whole vector && call destructor for each ElType * - element.
 */
template <typename ElType>
void freeAnySeqContainer(std::vector<ElType *> & l) throw()
{
	for (typename std::vector<ElType *>::iterator i(l.begin()); i != l.end(); ++i)
	{
		std::free((void *) *i);
	}
	l.clear();
}

/** @brief Utility to delete any map<string, anytype>.
 *
 * I.e. empty whole map && call destuctor for each ElType * - element.
 */
template <typename ElType>
void delStringMap(std::map<std::string, ElType *> & l) throw()
{
	for (typename std::map<std::string, ElType *>::iterator i(l.begin()); i != l.end(); ++i)
	{
		delete i->second;
	}
	l.clear();
}

/** @brief Base adapter class to hold a pointer that can't be freed
 * using standard "delete".
 *
 * Derived classes should overwrite the destructor accordingly (and
 * add other tool methods at will).
 */
template <typename P>
class auto_base
{
public:
	/** @brief Construct from any pointer (may be 0). */
	auto_base(P * const p=0)
		:p_(p)
	{}

	/** @brief Set controlled pointer (must not be 0). */
	void set(P * const p)
	{
		assert(p);
		p_ = p;
	}

	/** @brief Get underlying pointer. */
	P * get() const
	{
		return p_;
	}

	/** @brief Smart dereferencing. */
	P * operator->() const
	{
		return p_;
	}
protected:
	/** @brief Controlled pointer. */
	P * p_;
};

/** @brief Control freeing of C vector pointers via delete[]. */
template<typename P>
class auto_cvec: public auto_base<P>
{
public:
	/** @brief Control this C vector pointer created via new P[n]. */
	auto_cvec(P * const p)
		:auto_base<P>(p)
	{}

	/** @brief Free memory held by C vector via delete[]. */
	~auto_cvec()
	{
		delete[] auto_base<P>::p_;
	}

	/** @brief Access an C vector element. */
	P & operator[](int i)
	{
		return auto_base<P>::p_[i];
	}
};

/** @brief Control freeing of memory via std::free. */
template <typename P>
class auto_free: public auto_base<P>
{
public:
	/** @brief Control this memory allocated via std::*alloc. */
	auto_free(P * const p)
		:auto_base<P>(p)
	{}

	/** @brief Free memory via std::free. */
	~auto_free()
	{
		std::free(auto_base<P>::p_);
	}
};

/** @brief Reverse "find" for lists.
 *
 * @note Why doesn't "find(list.rbegin(), list.rend(), key)" work?
 *
 * @param l The list to search.
 * @param key The key to find.
 * @return Iterator to found key, or l.end() if not found.
 */
template <typename K>
typename std::list<K>::iterator reverse_lfind(std::list<K> & l, K const & key)
{
	typename std::list<K>::iterator i(l.end());
	do
	{
		--i;
		if ((*i) == key)
		{
			return i;
		}
	}
	while (i != l.begin());
	return l.end();
}

/** @brief Get canonical absolute path name
 *
 * This effectively works like realpath(3), wrapped up for C++, w/ exception error handling.
 *
 * @see realpath(3)
 * @bug realpath(3) is "broken by design". We need to create a char buffer of size MAX_PATH
 *      each time we call this function.
 */
std::string realpath(std::string const & path);

/** @brief Mutex Lock class; an object of this class will guard a scope.
 *
 * The Mutex class must provide enterMutex(), tryEnterMutex() and leaveMutex() methods.
 */
template <typename Mutex>
class MutexLock
{
public:
	/** @brief Error codes for exceptions. */
	enum ErrorCode
	{
		Locked_ = 1
	};
	/** @brief This classes exceptions. */
	typedef CodeException<ErrorCode> Exception;

	/** @brief Constructor; locks the mutex.
	 *
	 * @param mutex Any Mutex providing enterMutex(), tryEnterMutex() and leaveMutex().
	 * @param wait Whether to wait until the lock can be set, or to return with exception LOCKED.
	 */
	MutexLock(Mutex & mutex, bool wait=true)
		:mutex_(&mutex)
	{
		if (wait)
		{
			mutex_->enterMutex();
		}
		else
		{
			if (!mutex_->tryEnterMutex())
			{
				UI_THROW_CODE(Locked_, "Mutex is locked");
			}
		}
	}

	/** @brief Destructor; unlocks the mutex. */
	~MutexLock()
	{
		mutex_->leaveMutex();
	}
private:
	Mutex * const mutex_;
};


/** @brief Get canonical absolute path name (mutex-protected)
 *
 * You should create one instance of this for one multi-threaded environment, and then
 * call the object's get() method. Purpose is to be faster than realpath() by not having
 * to create a buffer of MAX_PATH each time we call. Tests however suprisingly show
 * it's even somewhat slower.
 *
 * @param path Path to canonize.
 *
 * @attention Do not use more than one object in a multi-threaded environment.
 *
 * @see UI::Util::realpath().
 *
 */
template <typename M>
class RealPath
{
public:
	/** @brief Get canonical path, mutex-protected. */
	std::string get(std::string const & path)
	{
		MutexLock<M> lock(mutex_);
		Sys::realpath(path.c_str(), buffer_);
		return std::string(buffer_);
	};

private:
	M mutex_;
	char buffer_[PATH_MAX];
};

/** @brief Helper class to get a scope executed with some other effective uid. */
class EUIDSwap
{
public:
	/** @brief Error codes for exceptions. */
	enum ErrorCode
	{
		SetEuidErr_ = 1
	};
	/** @brief This classes exceptions. */
	typedef CodeException<ErrorCode> Exception;

	EUIDSwap(uid_t uid=Sys::getuid());
	~EUIDSwap();

	/** @brief Get the original effective user id. */
	uid_t getOrigUID() const;

private:
	uid_t const origEUID_;
};

}}
#endif
