/* nutclient.h - definitions for nutclient C/C++ library

   Copyright (C) 2012  Emilien Kia <emilien.kia@gmail.com>

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/

#ifndef NUTCLIENT_HPP_SEEN
#define NUTCLIENT_HPP_SEEN

/* Begin of C++ nutclient library declaration */
#ifdef __cplusplus

#include <string>
#include <vector>
#include <map>
#include <set>
#include <exception>
#include <cstdint>
#include <ctime>

/* See include/common.h for details behind this */
#ifndef NUT_UNUSED_VARIABLE
#define NUT_UNUSED_VARIABLE(x) (void)(x)
#endif

namespace nut
{

namespace internal
{
class Socket;
} /* namespace internal */


class Client;
class TcpClient;
class Device;
class Variable;
class Command;

/**
 * Basic nut exception.
 */
class NutException : public std::exception
{
public:
	NutException(const std::string& msg):_msg(msg){}
	NutException(const NutException&) = default;
	NutException& operator=(NutException& rhs) = default;
	virtual ~NutException() noexcept override;
	virtual const char * what() const noexcept override {return this->_msg.c_str();}
	virtual std::string str() const noexcept {return this->_msg;}
private:
	std::string _msg;
};

/**
 * System error.
 */
class SystemException : public NutException
{
public:
	SystemException();
	SystemException(const SystemException&) = default;
	SystemException& operator=(SystemException& rhs) = default;
	virtual ~SystemException() noexcept override;
private:
	static std::string err();
};


/**
 * IO oriented nut exception.
 */
class IOException : public NutException
{
public:
	IOException(const std::string& msg):NutException(msg){}
	IOException(const IOException&) = default;
	IOException& operator=(IOException& rhs) = default;
	virtual ~IOException() noexcept override;
};

/**
 * IO oriented nut exception specialized for unknown host
 */
class UnknownHostException : public IOException
{
public:
	UnknownHostException():IOException("Unknown host"){}
	UnknownHostException(const UnknownHostException&) = default;
	UnknownHostException& operator=(UnknownHostException& rhs) = default;
	virtual ~UnknownHostException() noexcept override;
};

/**
 * IO oriented nut exception when client is not connected
 */
class NotConnectedException : public IOException
{
public:
	NotConnectedException():IOException("Not connected"){}
	NotConnectedException(const NotConnectedException&) = default;
	NotConnectedException& operator=(NotConnectedException& rhs) = default;
	virtual ~NotConnectedException() noexcept override;
};

/**
 * IO oriented nut exception when there is no response.
 */
class TimeoutException : public IOException
{
public:
	TimeoutException():IOException("Timeout"){}
	TimeoutException(const TimeoutException&) = default;
	TimeoutException& operator=(TimeoutException& rhs) = default;
	virtual ~TimeoutException() noexcept override;
};

/**
 * Cookie given when performing async action, used to redeem result at a later date.
 */
typedef std::string TrackingID;

/**
 * Result of an async action.
 */
typedef enum
{
	UNKNOWN,
	PENDING,
	SUCCESS,
	INVALID_ARGUMENT,
	FAILURE,
} TrackingResult;

typedef std::string Feature;

/**
 * A nut client is the starting point to dialog to NUTD.
 * It can connect to an NUTD then retrieve its device list.
 * Use a specific client class to connect to a NUTD.
 */
class Client
{
	friend class Device;
	friend class Variable;
	friend class Command;
public:
	virtual ~Client();

	/**
	 * Intend to authenticate to a NUTD server.
	 * Set the username and password associated to the connection.
	 * \param user User name.
	 * \param passwd Password.
	 * \todo Is his method is global to all connection protocol or is it specific to TCP ?
	 * \note Actually, authentication fails only if already set, not if bad values are sent.
	 */
	virtual void authenticate(const std::string& user, const std::string& passwd) = 0;

	/**
	 * Disconnect from the NUTD server.
	 * \todo Is his method is global to all connection protocol or is it specific to TCP ?
	 */
	virtual void logout() = 0;

	/**
	 * Device manipulations.
	 * \see nut::Device
	 * \{
	 */
	/**
	 * Retrieve a device from its name.
	 * If the device does not exist, a bad (not ok) device is returned.
	 * \param name Name of the device.
	 * \return The device.
	 */
	virtual Device getDevice(const std::string& name);
	/**
	 * Retrieve the list of all devices supported by UPSD server.
	 * \return The set of supported devices.
	 */
	virtual std::set<Device> getDevices();
	/**
	 * Test if a device is supported by the NUTD server.
	 * \param dev Device name.
	 * \return true if supported, false otherwise.
	 */
	virtual bool hasDevice(const std::string& dev);
	/**
	 * Retrieve names of devices supported by NUTD server.
	 * \return The set of names of supported devices.
	 */
	virtual std::set<std::string> getDeviceNames() = 0;
	/**
	 * Retrieve the description of a device.
	 * \param name Device name.
	 * \return Device description.
	 */
	virtual std::string getDeviceDescription(const std::string& name) = 0;
	/** \} */

	/**
	 * Variable manipulations.
	 * \see nut::Variable
	 * \{
	 */
	/**
	 * Retrieve names of all variables supported by a device.
	 * \param dev Device name
	 * \return Variable names
	 */
	virtual std::set<std::string> getDeviceVariableNames(const std::string& dev) = 0;
	/**
	 * Retrieve names of read/write variables supported by a device.
	 * \param dev Device name
	 * \return RW variable names
	 */
	virtual std::set<std::string> getDeviceRWVariableNames(const std::string& dev) = 0;
	/**
	 * Test if a variable is supported by a device.
	 * \param dev Device name
	 * \param name Variable name
	 * \return true if the variable is supported.
	 */
	virtual bool hasDeviceVariable(const std::string& dev, const std::string& name);
	/**
	 * Retrieve the description of a variable.
	 * \param dev Device name
	 * \param name Variable name
	 * \return Variable description if provided.
	 */
	virtual std::string getDeviceVariableDescription(const std::string& dev, const std::string& name) = 0;
	/**
	 * Retrieve values of a variable.
	 * \param dev Device name
	 * \param name Variable name
	 * \return Variable values (usually one) if available.
	 */
	virtual std::vector<std::string> getDeviceVariableValue(const std::string& dev, const std::string& name) = 0;
	/**
	 * Retrieve values of all variables of a device.
	 * \param dev Device name
	 * \return Variable values indexed by variable names.
	 */
	virtual std::map<std::string,std::vector<std::string> > getDeviceVariableValues(const std::string& dev);
	/**
	 * Retrieve values of all variables of a set of devices.
	 * \param devs Device names
	 * \return Variable values indexed by variable names, indexed by device names.
	 */
	virtual std::map<std::string,std::map<std::string,std::vector<std::string> > > getDevicesVariableValues(const std::set<std::string>& devs);
	/**
	 * Intend to set the value of a variable.
	 * \param dev Device name
	 * \param name Variable name
	 * \param value Variable value
	 */
	virtual TrackingID setDeviceVariable(const std::string& dev, const std::string& name, const std::string& value) = 0;
	/**
	 * Intend to set the value of a variable.
	 * \param dev Device name
	 * \param name Variable name
	 * \param values Vector of variable values
	 */
	virtual TrackingID setDeviceVariable(const std::string& dev, const std::string& name, const std::vector<std::string>& values) = 0;
	/** \} */

	/**
	 * Instant command manipulations.
	 * \see nut::Command
	 * \{
	 */
	/**
	 * Retrieve names of all commands supported by a device.
	 * \param dev Device name
	 * \return Command names
	 */
	virtual std::set<std::string> getDeviceCommandNames(const std::string& dev) = 0;
	/**
	 * Test if a command is supported by a device.
	 * \param dev Device name
	 * \param name Command name
	 * \return true if the command is supported.
	 */
	virtual bool hasDeviceCommand(const std::string& dev, const std::string& name);
	/**
	 * Retrieve the description of a command.
	 * \param dev Device name
	 * \param name Command name
	 * \return Command description if provided.
	 */
	virtual std::string getDeviceCommandDescription(const std::string& dev, const std::string& name) = 0;
	/**
	 * Intend to execute a command.
	 * \param dev Device name
	 * \param name Command name
	 * \param param Additional command parameter
	 */
	virtual TrackingID executeDeviceCommand(const std::string& dev, const std::string& name, const std::string& param="") = 0;
	/** \} */

	/**
	 * Device specific commands.
	 * \{
	 */
	/**
	 * Log the current user (if authenticated) for a device.
	 * \param dev Device name.
	 */
	virtual void deviceLogin(const std::string& dev) = 0;
	/**
	 * Retrieve the number of user logged-in for the specified device.
	 * \param dev Device name.
	 * \return Number of logged-in users.
	 */
	virtual int deviceGetNumLogins(const std::string& dev) = 0;
	/**
	 * Who did a deviceLogin() to this dev?
	 * \param dev Device name.
	 * \return List of clients e.g. {'127.0.0.1', 'admin-workstation.local.domain'}
	 */
	virtual std::set<std::string> deviceGetClients(const std::string& dev) = 0;
	/* NOTE: "master" is deprecated since NUT v2.8.0 in favor of "primary".
	 * For the sake of old/new server/client interoperability,
	 * practical implementations should try to use one and fall
	 * back to the other, and only fail if both return "ERR".
	 */
	virtual void deviceMaster(const std::string& dev) = 0;
	virtual void devicePrimary(const std::string& dev) = 0;
	virtual void deviceForcedShutdown(const std::string& dev) = 0;

	/**
	 * Lists all clients of all devices (which have at least one client)
	 * \return Map with device names vs. list of their connected clients
	 */
	virtual std::map<std::string, std::set<std::string>> listDeviceClients(void) = 0;

	/**
	 * Retrieve the result of a tracking ID.
	 * \param id Tracking ID.
	 */
	virtual TrackingResult getTrackingResult(const TrackingID& id) = 0;

	virtual bool hasFeature(const Feature& feature);
	virtual bool isFeatureEnabled(const Feature& feature) = 0;
	virtual void setFeature(const Feature& feature, bool status) = 0;

	static const Feature TRACKING;

protected:
	Client();
};

/**
 * TCP NUTD client.
 * It connect to NUTD with a TCP socket.
 */
class TcpClient : public Client
{
	/* We have a number of direct-call methods we do not expose
	 * generally, but still want covered with integration tests
	 */
	friend class NutActiveClientTest;

public:
	/**
	 * Construct a nut TcpClient object.
	 * You must call one of TcpClient::connect() after.
	 */
	TcpClient();

	/**
	 * Construct a nut TcpClient object then connect it to the specified server.
	 * \param host Server host name.
	 * \param port Server port.
	 */
	TcpClient(const std::string& host, uint16_t port = 3493);
	~TcpClient() override;

	/**
	 * Connect it to the specified server.
	 * \param host Server host name.
	 * \param port Server port.
	 */
	void connect(const std::string& host, uint16_t port = 3493);

	/**
	 * Connect to the server.
	 * Host name and ports must have already set (useful for reconnection).
	 */
	void connect();

	/**
	 * Enable or disable std::cerr tracing of internal Socket operations
	 * during connect() processing. Primarily for developer troubleshooting.
	 */
	void setDebugConnect(bool d);

	/**
	 * Test if the connection is active.
	 * \return tru if the connection is active.
	 */
	bool isConnected()const;
	/**
	 * Force the deconnection.
	 */
	void disconnect();

	/**
	 * Set the timeout in seconds.
	 * \param timeout Timeout n seconds, negative to block operations.
	 */
	void setTimeout(time_t timeout);

	/**
	 * Retrieve the timeout.
	 * \returns Current timeout in seconds.
	 */
	time_t getTimeout()const;

	/**
	 * Retriueve the host name of the server the client is connected to.
	 * \return Server host name
	 */
	std::string getHost()const;
	/**
	 * Retriueve the port of host of the server the client is connected to.
	 * \return Server port
	 */
	uint16_t getPort()const;

	virtual void authenticate(const std::string& user, const std::string& passwd) override;
	virtual void logout() override;

	virtual Device getDevice(const std::string& name) override;
	virtual std::set<std::string> getDeviceNames() override;
	virtual std::string getDeviceDescription(const std::string& name) override;

	virtual std::set<std::string> getDeviceVariableNames(const std::string& dev) override;
	virtual std::set<std::string> getDeviceRWVariableNames(const std::string& dev) override;
	virtual std::string getDeviceVariableDescription(const std::string& dev, const std::string& name) override;
	virtual std::vector<std::string> getDeviceVariableValue(const std::string& dev, const std::string& name) override;
	virtual std::map<std::string,std::vector<std::string> > getDeviceVariableValues(const std::string& dev) override;
	virtual std::map<std::string,std::map<std::string,std::vector<std::string> > > getDevicesVariableValues(const std::set<std::string>& devs) override;
	virtual TrackingID setDeviceVariable(const std::string& dev, const std::string& name, const std::string& value) override;
	virtual TrackingID setDeviceVariable(const std::string& dev, const std::string& name, const std::vector<std::string>& values) override;

	virtual std::set<std::string> getDeviceCommandNames(const std::string& dev) override;
	virtual std::string getDeviceCommandDescription(const std::string& dev, const std::string& name) override;
	virtual TrackingID executeDeviceCommand(const std::string& dev, const std::string& name, const std::string& param="") override;

	virtual void deviceLogin(const std::string& dev) override;
	/* FIXME: Protocol update needed to handle master/primary alias
	 * and probably an API bump also, to rename/alias the routine.
	 */
	virtual void deviceMaster(const std::string& dev) override;
	virtual void devicePrimary(const std::string& dev) override;
	virtual void deviceForcedShutdown(const std::string& dev) override;
	virtual int deviceGetNumLogins(const std::string& dev) override;
	virtual std::set<std::string> deviceGetClients(const std::string& dev) override;

	virtual std::map<std::string, std::set<std::string>> listDeviceClients(void) override;

	virtual TrackingResult getTrackingResult(const TrackingID& id) override;

	virtual bool isFeatureEnabled(const Feature& feature) override;
	virtual void setFeature(const Feature& feature, bool status) override;

protected:
	std::string sendQuery(const std::string& req);
	void sendAsyncQueries(const std::vector<std::string>& req);
	static void detectError(const std::string& req);
	TrackingID sendTrackingQuery(const std::string& req);

	std::vector<std::string> get(const std::string& subcmd, const std::string& params = "");

	std::vector<std::vector<std::string> > list(const std::string& subcmd, const std::string& params = "");

	std::vector<std::vector<std::string> > parseList(const std::string& req);

	static std::vector<std::string> explode(const std::string& str, size_t begin=0);
	static std::string escape(const std::string& str);

private:
	std::string _host;
	uint16_t _port;
	time_t _timeout;
	internal::Socket* _socket;
};

/**
 * Device attached to a client.
 * Device is a lightweight class which can be copied easily.
 */
class Device
{
	friend class Client;
	friend class TcpClient;
	friend class TcpClientMock;
#ifdef _NUTCLIENTTEST_BUILD
	friend class NutClientTest;
#endif
public:
	~Device();
	Device(const Device& dev);
	Device& operator=(const Device& dev);

	/**
	 * Retrieve the name of the device.
	 * The name is the unique id under which NUTD known the device.
	 */
	std::string getName()const;
	/**
	 * Retrieve the client to which the device is attached.
	 */
	const Client* getClient()const;
	/**
	 * Retrieve the client to which the device is attached.
	 */
	Client* getClient();

	/**
	 * Test if the device is valid (has a name and is attached to a client).
	 */
	bool isOk()const;
	/**
	 * Test if the device is valid (has a name and is attached to a client).
	 * @see Device::isOk()
	 */
	operator bool()const;
	/**
	 * Test if the device is not valid (has no name or is not attached to any client).
	 * @see Device::isOk()
	 */
	bool operator!()const;
	/**
	 * Test if the two devices are sames (same name ad same client attached to).
	 */
	bool operator==(const Device& dev)const;
	/**
	 * Comparison operator.
	 */
	bool operator<(const Device& dev)const;

	/**
	 * Retrieve the description of the devce if specified.
	 */
	std::string getDescription();

	/**
	 * Intend to retrieve the value of a variable of the device.
	 * \param name Name of the variable to get.
	 * \return Value of the variable, if available.
	 */
	std::vector<std::string> getVariableValue(const std::string& name);
	/**
	 * Intend to retrieve values of all variables of the devices.
	 * \return Map of all variables values indexed by their names.
	 */
	std::map<std::string,std::vector<std::string> > getVariableValues();
	/**
	 * Retrieve all variables names supported by the device.
	 * \return Set of available variable names.
	 */
	std::set<std::string> getVariableNames();
	/**
	 * Retrieve all Read/Write variables names supported by the device.
	 * \return Set of available Read/Write variable names.
	 */
	std::set<std::string> getRWVariableNames();
	/**
	 * Intend to set the value of a variable of the device.
	 * \param name Variable name.
	 * \param value New variable value.
	 */
	void setVariable(const std::string& name, const std::string& value);
	/**
	 * Intend to set values of a variable of the device.
	 * \param name Variable name.
	 * \param values Vector of new variable values.
	 */
	void setVariable(const std::string& name, const std::vector<std::string>& values);

	/**
	 * Retrieve a Variable object representing the specified variable.
	 * \param name Variable name.
	 * \return Variable object.
	 */
	Variable getVariable(const std::string& name);
	/**
	 * Retrieve Variable objects representing all variables available for the device.
	 * \return Set of Variable objects.
	 */
	std::set<Variable> getVariables();
	/**
	 * Retrieve Variable objects representing all Read/Write variables available for the device.
	 * \return Set of Variable objects.
	 */
	std::set<Variable> getRWVariables();

	/**
	 * Retrieve names of all commands supported by the device.
	 * \return Set of available command names.
	 */
	std::set<std::string> getCommandNames();
	/**
	 * Retrieve objects for all commands supported by the device.
	 * \return Set of available Command objects.
	 */
	std::set<Command> getCommands();
	/**
	 * Retrieve an object representing a command of the device.
	 * \param name Command name.
	 * \return Command object.
	 */
	Command getCommand(const std::string& name);
	/**
	 * Intend to execute a command on the device.
	 * \param name Command name.
	 * \param param Additional command parameter
	 */
	TrackingID executeCommand(const std::string& name, const std::string& param="");

	/**
	 * Login current client's user for the device.
	 */
	void login();
	/**
	 * Who did a login() to this dev?
	 */
	std::set<std::string> getClients();
	/* FIXME: Protocol update needed to handle master/primary alias
	 * and probably an API bump also, to rename/alias the routine.
	 */
	void master();
	void primary();
	void forcedShutdown();
	/**
	 * Retrieve the number of logged user for the device.
	 * \return Number of users.
	 */
	int getNumLogins();

protected:
	Device(Client* client, const std::string& name);

private:
	Client* _client;
	std::string _name;
};

/**
 * Variable attached to a device.
 * Variable is a lightweight class which can be copied easily.
 */
class Variable
{
	friend class Device;
	friend class TcpClient;
	friend class TcpClientMock;
#ifdef _NUTCLIENTTEST_BUILD
	friend class NutClientTest;
#endif
public:
	~Variable();

	Variable(const Variable& var);
	Variable& operator=(const Variable& var);

	/**
	 * Retrieve variable name.
	 */
	std::string getName()const;
	/**
	 * Retrieve the device to which the variable is attached to.
	 */
	const Device* getDevice()const;
	/**
	 * Retrieve the device to which the variable is attached to.
	 */
	Device* getDevice();

	/**
	 * Test if the variable is valid (has a name and is attached to a device).
	 */
	bool isOk()const;
	/**
	 * Test if the variable is valid (has a name and is attached to a device).
	 * @see Variable::isOk()
	 */
	operator bool()const;
	/**
	 * Test if the variable is not valid (has no name or is not attached to any device).
	 * @see Variable::isOk()
	 */
	bool operator!()const;
	/**
	 * Test if the two variables are sames (same name ad same device attached to).
	 */
	bool operator==(const Variable& var)const;
	/**
	 * Less-than operator (based on variable name) to allow variable sorting.
	 */
	bool operator<(const Variable& var)const;

	/**
	 * Intend to retrieve variable value.
	 * \return Value of the variable.
	 */
	std::vector<std::string> getValue();
	/**
	 * Intend to retireve variable description.
	 * \return Variable description if provided.
	 */
	std::string getDescription();

	/**
	 * Intend to set a value to the variable.
	 * \param value New variable value.
	 */
	void setValue(const std::string& value);
	/**
	 * Intend to set (multiple) values to the variable.
	 * \param values Vector of new variable values.
	 */
	void setValues(const std::vector<std::string>& values);

protected:
	Variable(Device* dev, const std::string& name);

private:
	Device* _device;
	std::string _name;
};

/**
 * Command attached to a device.
 * Command is a lightweight class which can be copied easily.
 */
class Command
{
	friend class Device;
	friend class TcpClient;
	friend class TcpClientMock;
#ifdef _NUTCLIENTTEST_BUILD
	friend class NutClientTest;
#endif
public:
	~Command();

	Command(const Command& cmd);
	Command& operator=(const Command& cmd);

	/**
	 * Retrieve command name.
	 */
	std::string getName()const;
	/**
	 * Retrieve the device to which the command is attached to.
	 */
	const Device* getDevice()const;
	/**
	 * Retrieve the device to which the command is attached to.
	 */
	Device* getDevice();

	/**
	 * Test if the command is valid (has a name and is attached to a device).
	 */
	bool isOk()const;
	/**
	 * Test if the command is valid (has a name and is attached to a device).
	 * @see Command::isOk()
	 */
	operator bool()const;
	/**
	 * Test if the command is not valid (has no name or is not attached to any device).
	 * @see Command::isOk()
	 */
	bool operator!()const;
	/**
	 * Test if the two commands are sames (same name ad same device attached to).
	 */
	bool operator==(const Command& var)const;

	/**
	 * Less-than operator (based on command name) to allow comand sorting.
	 */
	bool operator<(const Command& var)const;

	/**
	 * Intend to retireve command description.
	 * \return Command description if provided.
	 */
	std::string getDescription();

	/**
	 * Intend to execute the instant command on device.
	 * \param param Optional additional command parameter
	 */
	void execute(const std::string& param="");

protected:
	Command(Device* dev, const std::string& name);

private:
	Device* _device;
	std::string _name;
};

} /* namespace nut */

#endif /* __cplusplus */
/* End of C++ nutclient library declaration */




/* Begin of C nutclient library declaration */
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */


/**
 * Array of string manipulation functions.
 * \{
 */
/** Array of string.*/
typedef char** strarr;
/**
 * Alloc an array of string.
 */
strarr strarr_alloc(size_t count);

/**
 * Free an array of string.
 */
void strarr_free(strarr arr);

/**
 * Convert C++ types into an array of string.
 */
strarr stringvector_to_strarr(const std::vector<std::string>& strset);
strarr stringset_to_strarr(const std::set<std::string>& strset);


/**
 * Nut general client types and functions.
 * \{
 */
/** Hidden structure representing a connection to NUTD. */
typedef void* NUTCLIENT_t;

/**
 * Destroy a client.
 * \param client Nut client handle.
 */
void nutclient_destroy(NUTCLIENT_t client);

/**
 * Authenticate into the server.
 * \param client Nut client handle.
 * \param login User name.
 * \param passwd Password.
 */
void nutclient_authenticate(NUTCLIENT_t client, const char* login, const char* passwd);

/**
 * Log out from server.
 * \param client Nut client handle.
 */
void nutclient_logout(NUTCLIENT_t client);

/**
 * Register current user on the device.
 * \param client Nut client handle.
 * \param dev Device name to test.
 */
void nutclient_device_login(NUTCLIENT_t client, const char* dev);

/**
 * Retrieve the number of users registered on a device.
 * \param client Nut client handle.
 * \param dev Device name to test.
 */
int nutclient_get_device_num_logins(NUTCLIENT_t client, const char* dev);

/**
 * Set current user as master user of the device.
 * \param client Nut client handle.
 * \param dev Device name to test.
 */
/* FIXME: Protocol update needed to handle master/primary alias
 * and probably an API bump also, to rename/alias the routine.
 */
void nutclient_device_master(NUTCLIENT_t client, const char* dev);
void nutclient_device_primary(NUTCLIENT_t client, const char* dev);

/**
 * Set the FSD flag for the device.
 * \param client Nut client handle.
 * \param dev Device name to test.
 */
void nutclient_device_forced_shutdown(NUTCLIENT_t client, const char* dev);

/**
 * Retrieve the list of devices of a client.
 * \param client Nut client handle.
 * \return Array of string containing device names. Must be freed with strarr_free(strarr).
 */
strarr nutclient_get_devices(NUTCLIENT_t client);

/**
 * Test if a device is supported by the client.
 * \param client Nut client handle.
 * \param dev Device name to test.
 * \return 1 if supported, 0 otherwise.
 */
int nutclient_has_device(NUTCLIENT_t client, const char* dev);

/**
 * Intend to retrieve device description.
 * \param client Nut client handle.
 * \param dev Device name to test.
 * \return Description of device. Must be freed after use.
 */
char* nutclient_get_device_description(NUTCLIENT_t client, const char* dev);

/**
 * Intend to retrieve device variable names.
 * \param client Nut client handle.
 * \param dev Device name.
 * \return Array of string containing variable names. Must be freed with strarr_free(strarr).
 */
strarr nutclient_get_device_variables(NUTCLIENT_t client, const char* dev);

/**
 * Intend to retrieve device read/write variable names.
 * \param client Nut client handle.
 * \param dev Device name.
 * \return Array of string containing read/write variable names. Must be freed with strarr_free(strarr).
 */
strarr nutclient_get_device_rw_variables(NUTCLIENT_t client, const char* dev);

/**
 * Test if a variable is supported by the device and the client.
 * \param client Nut client handle.
 * \param dev Device name.
 * \param var Variable name.
 * \return 1 if supported, 0 otherwise.
 */
int nutclient_has_device_variable(NUTCLIENT_t client, const char* dev, const char* var);

/**
 * Intend to retrieve device variable description.
 * \param client Nut client handle.
 * \param dev Device name.
 * \param var Variable name.
 * \return Description of device variable. Must be freed after use.
 */
char* nutclient_get_device_variable_description(NUTCLIENT_t client, const char* dev, const char* var);

/**
 * Intend to retrieve device variable values.
 * \param client Nut client handle.
 * \param dev Device name.
 * \param var Variable name.
 * \return Array of string containing variable values. Must be freed with strarr_free(strarr).
 */
strarr nutclient_get_device_variable_values(NUTCLIENT_t client, const char* dev, const char* var);

/**
 * Intend to set device variable value.
 * \param client Nut client handle.
 * \param dev Device name.
 * \param var Variable name.
 * \param value Value to set.
 */
void nutclient_set_device_variable_value(NUTCLIENT_t client, const char* dev, const char* var, const char* value);

/**
 * Intend to set device variable  multiple values.
 * \param client Nut client handle.
 * \param dev Device name.
 * \param var Variable name.
 * \param values Values to set. The cller is responsible to free it after call.
 */
void nutclient_set_device_variable_values(NUTCLIENT_t client, const char* dev, const char* var, const strarr values);

/**
 * Intend to retrieve device command names.
 * \param client Nut client handle.
 * \param dev Device name.
 * \return Array of string containing command names. Must be freed with strarr_free(strarr).
 */
strarr nutclient_get_device_commands(NUTCLIENT_t client, const char* dev);

/**
 * Test if a command is supported by the device and the client.
 * \param client Nut client handle.
 * \param dev Device name.
 * \param cmd Command name.
 * \return 1 if supported, 0 otherwise.
 */
int nutclient_has_device_command(NUTCLIENT_t client, const char* dev, const char* cmd);

/**
 * Intend to retrieve device command description.
 * \param client Nut client handle.
 * \param dev Device name.
 * \param cmd Command name.
 * \return Description of device command. Must be freed after use.
 */
char* nutclient_get_device_command_description(NUTCLIENT_t client, const char* dev, const char* cmd);

/**
 * Intend to execute device command.
 * \param client Nut client handle.
 * \param dev Device name.
 * \param cmd Command name.
 */
void nutclient_execute_device_command(NUTCLIENT_t client, const char* dev, const char* cmd, const char* param="");

/** \} */


/**
 * Nut TCP client dedicated types and functions
 * \{
 */
/**
 * Hidden structure representing a TCP connection to NUTD.
 * NUTCLIENT_TCP_t is back compatible to NUTCLIENT_t.
 */
typedef NUTCLIENT_t NUTCLIENT_TCP_t;

/**
 * Create a client to NUTD using a TCP connection.
 * \param host Host name to connect to.
 * \param port Host port.
 * \return New client or nullptr if failed.
 */
NUTCLIENT_TCP_t nutclient_tcp_create_client(const char* host, uint16_t port);
/**
 * Test if a nut TCP client is connected.
 * \param client Nut TCP client handle.
 * \return 1 if connected, 0 otherwise.
 */
int nutclient_tcp_is_connected(NUTCLIENT_TCP_t client);
/**
 * Disconnect a nut TCP client.
 * \param client Nut TCP client handle.
 */
void nutclient_tcp_disconnect(NUTCLIENT_TCP_t client);
/**
 * Intend to reconnect a nut TCP client.
 * \param client Nut TCP client handle.
 * \return 0 if correctly connected.
 * \todo Implement different error codes.
 */
int nutclient_tcp_reconnect(NUTCLIENT_TCP_t client);
/**
 * Set the timeout value for the TCP connection.
 * \param timeout Timeout in seconds, negative for blocking.
 */
void nutclient_tcp_set_timeout(NUTCLIENT_TCP_t client, time_t timeout);
/**
 * Retrieve the timeout value for the TCP connection.
 * \return Timeout value in seconds.
 */
time_t nutclient_tcp_get_timeout(NUTCLIENT_TCP_t client);

/** \} */

#ifdef __cplusplus
}
#endif /* __cplusplus */
/* End of C nutclient library declaration */


#endif	/* NUTCLIENT_HPP_SEEN */
