/**
 * @file
 *
 * @brief Implementation of get/set and error plugins
 *
 * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
 *
 */


#ifndef TOOLS_PLUGINS_HPP
#define TOOLS_PLUGINS_HPP

#include <plugin.hpp>
#include <toolexcept.hpp>

#include <map>
#include <string>
#include <vector>

#include <kdb.hpp>

namespace kdb
{

namespace tools
{


struct Place
{
	int current;
	int max;

	Place () : current (-1), max (0)
	{
	}

	Place (int current_, int max_) : current (current_), max (max_)
	{
	}
};

/**
 * @brief A collection of plugins (either get, set or error)
 */
class Plugins
{
protected:
	std::vector<Plugin *> plugins;

	std::vector<std::string> needed;
	std::vector<std::string> recommended;
	std::vector<std::string> alreadyProvided;
	std::vector<std::string> alreadyConflict;

	int nrStoragePlugins;
	int nrResolverPlugins;

	int revPostGet;

	std::map<std::string, Place> placementInfo;

public:
	Plugins ();

	/** Add needed, provided and recommend information */
	void addInfo (Plugin & plugin);
	void addPlugin (Plugin & plugin, std::string which);

	/** Validate needed, and provided information.
	 * (Recommended ignored, @see getRecommendedMissing(),
	 * @see getNeededMissing() */
	bool validateProvided () const;
	std::vector<std::string> getNeededMissing () const;
	std::vector<std::string> getRecommendedMissing () const;

	bool checkPlacement (Plugin & plugin, std::string which);
	void checkStorage (Plugin & plugin);
	void checkResolver (Plugin & plugin);
	void checkOrdering (Plugin & plugin);
	void checkConflicts (Plugin & plugin);
};

/**
 * @brief Plugins to get configuration
 */
class GetPlugins : private Plugins
{
public:
	/**
	 * Returns true if GetPlugins are valid afterwards.
	 *
	 * Will throw an exception if plugin could not
	 * be added.
	 */
	void tryPlugin (Plugin & plugin);
	void addPlugin (Plugin & plugin);
	bool validated () const;

	void serialise (kdb::Key & baseKey, kdb::KeySet & ret);
};


/**
 * @brief Plugins to set configuration
 */
class SetPlugins : private Plugins
{
public:
	void tryPlugin (Plugin & plugin);
	void addPlugin (Plugin & plugin);
	bool validated () const;

	void serialise (kdb::Key & baseKey, kdb::KeySet & ret);
};

/**
 * @brief Plugins to handle errors during configuration access
 */
class ErrorPlugins : private Plugins
{
public:
	void status (std::ostream & os) const;

	void tryPlugin (Plugin & plugin);
	void addPlugin (Plugin & plugin);
	bool validated () const;

	void serialise (kdb::Key & baseKey, kdb::KeySet & ret);
};
} // namespace tools
} // namespace kdb

#endif
