/**
 * @file
 * @author Schlund + Partner AG
 *
 */
#ifndef UI_UTIL_QUOTAINFO_HPP
#define UI_UTIL_QUOTAINFO_HPP

// STDC++
#include <string>
#include <vector>

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

namespace UI {
namespace Util {

/** @brief Quota information abstraction.
 *
 * May be extended easily be adding new methods:
 *  - Add a new private methodXXX method (e.g. methodWinFake) that somehow
 *    sets all the quota values.
 *  - Add it to the method vector (see constructor of QuotaInfo::Methods).
 *
 * @todo: The interface is focused on linux "standard" quota
 * interface. Interface should rather be most powerful, with lesser
 * implementations falling back to sane defaults.
 *
 * @todo: Nicer way to find out what interface to use rather then
 * testing on by one (check "quota" source).
 */
class QuotaInfo
{
public:
	/** @brief Helper class to hold the device string and (optional) the
	 * fs type (to be able to do better selection on the quota method to
	 * use). */
	class FS
	{
		friend class QuotaInfo;
	public:
		FS(std::string const & dev, std::string const & fstype="")
			:dev_(dev)
			,fstype_(fstype)
		{}
		std::string const & getDevice() const { return dev_; }
		std::string const & getType() const { return fstype_; }
		std::string getMethods(std::string const & fstype="") const;
	private:
		std::string dev_;
		std::string fstype_;
	};
	/** @brief Get FS information from any file in that fs.
	 *
	 * @note: This method is potentially NOT thread-safe (uses getmntent under linux).
	 */
	static FS file2fs(std::string const & fName, std::string const & tab="/proc/mounts");

	/** @brief Type of quota information. */
	enum Type
	{
		Usr_, Grp_
	};

	/**
	 * @param fs   Device name (std::string - implicit) or file system (FS).
	 * @param id   ID as in quotactl(2), normally a user or group ID.
	 * @param type Type (user or group) of quota info to retrieve.
	 * @param strPath Extra parameter for windows "quota".
	 */
	QuotaInfo(FS const & fs, int const id=Sys::geteuid(), Type const type=Usr_, std::string const & strPath = "");

public:
	/** @brief Get method string of utilized method. */
	std::string getMethod() const;

	/** @brief Hard limit of disk blocks. */
	unsigned long getBlockHL() const;
	/** @brief Soft limit of disk blocks. */
	unsigned long getBlockSL() const;
	/** @brief Current block usage. */
	unsigned long getBlocks() const;

	/** @brief Hard limit of inodes. */
	unsigned long getINodeHL() const;
	/** @brief Soft limit of inodes. */
	unsigned long getINodeSL() const;
	/** @brief Current inode usage. */
	unsigned long getINodes() const;

	/** @brief Block timer limit. */
	time_t getBlockTL() const;
	/** @brief Inodes timer limit. */
	time_t getINodeTL() const;

	/** @brief Helper for getFree* methods. */
	bool getFree(unsigned long const limit, unsigned long const blocks, unsigned long & free) const;

	/** @brief Get free blocks from hard limit. False, if there is no hard limit. */
	bool getFreeBlocksHL(unsigned long & free) const;
	/** @brief Get free blocks from soft limit. False, if there is no soft limit. */
	bool getFreeBlocksSL(unsigned long & free) const;
	/** @brief Get free indoes from hard limit. False, if there is no hard limit. */
	bool getFreeINodesHL(unsigned long & free) const;
	/** @brief Get free inodes from soft limit. False, if there is no soft limit. */
	bool getFreeInodesSL(unsigned long & free) const;

	/** @brief QuotaInfo exceptions. */
	class Exception: public UI::Util::Exception
	{
	public:
		Exception(std::string const & what, std::string const & debug)
			:UI::Util::Exception(what, debug)
		{};
	};

private:
	typedef void (QuotaInfo::*Method) (std::string const &, uid_t const);

#ifdef HAVE_SYS_QUOTA_H
	/** @brief Method for linux quota version 1 (@e vfsold). */
	void methodLinuxV1(std::string const & dev, uid_t const uid);

	/** @brief Method for linux quota versions 1,2 (@e vfsold, @e vfsv0) (requires "recent" kernel). */
	void methodLinuxV2(std::string const & dev, uid_t const uid);
#endif

#ifdef HAVE_XFS_XQM_H
	/** @brief Method for XFS quota. */
	void methodLinuxXfs(std::string const & dev, uid_t const uid);
#endif

	/** @brief Method for (NFS) quota via rpc. */
	void methodLinuxRpc(std::string const & dev, uid_t const uid);

#ifdef WIN32
#ifndef W2K8
	void noQuota(std::string const & dev, uid_t const uid);
#else
	void w2k8Quota(std::string const & dev, uid_t const uid);
#endif
#endif

private:
	/** @brief: Block size used for all block values. */
	static unsigned long BlockSize_;

	class Methods
	{
	public:
		typedef std::map<std::string, Method> MethodMap;
		typedef std::vector<MethodMap::const_iterator> MethodVec;
		typedef std::map<std::string, MethodVec> FSMap;
		Methods();
		MethodMap m_;
		FSMap f_;
		MethodVec const & getMethods(std::string const & fstype) const;
	};
	static Methods const methods_;

	int const sub_;

	std::string const home_;

	std::string method_;
	unsigned long blockHL_;
	unsigned long blockSL_;
	unsigned long blocks_;

	unsigned long inodeHL_;
	unsigned long inodeSL_;
	unsigned long inodes_;

	time_t blockTL_;
	time_t inodeTL_;
};


/** @brief Variant of QuotaInfo from any file in FS.
 *
 * @note: Constructor potentially is NOT thread-safe: @see file2fs.
 *
 * @note Usually, you instantiate one FS object when your program
 * initializes, and then use that for all QuotaInfo() calls
 * thereafter. This is just a shortcut that you should not use if you
 * have several calls on the same FS.
 */
class FQuotaInfo: public QuotaInfo
{
public:
	/**
	 * @param fileName Any file names on the FS you want to run quota on.
	 * @param id       ID as in quotactl(2), normally a user or group ID.
	 * @param type     Type (user or group) of quota info to retrieve.
	 */
	FQuotaInfo(std::string const & fileName, int const id=Sys::geteuid(), Type const type=Usr_)
		:QuotaInfo(file2fs(fileName), id, type)
	{}
};

}}
#endif
