#ifndef _KM3NET_DB_CLIENT_H_
#define _KM3NET_DB_CLIENT_H_

///
/// \file KM3NeTDBClient.h
/// \brief Library header for DB access in C++.
///
///	This header file has to be included in all source files using DB access through the Web server.
///	Remember to add the library file in the link step.
///

#include <string>
#include <sstream>
#include <memory>
#include <exception>
#include <vector>

namespace KM3NeT
{
	namespace DB
	{
		///
		/// \brief Exceptions specific to DB access.
		///
		/// These exceptions contain a built-in support to show the line where the error occurred, useful for debugging.			
		///
		class DBException : public std::exception
		{
		protected:
			///
			/// Container for the message string.
			///
			std::string _Message;
			///
			/// The line of the source file where the exception was generated.
			///
			int _FileLine;
		public:
			~DBException() throw() {}
			///
			/// Builds an empty exception generated at line 0.
			///
			DBException() { _Message = ""; _FileLine = 0; }
			///
			/// Builds an exception from a text and line number.
			///
			DBException(const char *msg, int fileline) { _Message = msg; _FileLine = fileline; }
			///
			/// Builds an exception from a text and line number.
			///
			DBException(const std::string msg, int fileline) { _Message = msg; _FileLine = fileline; }
			///
			/// Assignment operator. Used by stack unwinding to pass the exception to outer blocks.
			///
			DBException &operator=(DBException &x) { _Message = x._Message; _FileLine = x._FileLine; return *this; }
			///
			/// Accesses the message text.
			///
			const char* what() const throw() { return _Message.c_str(); }
			///
			/// Accesses the file line of the exception.
			///
			virtual inline int FileLine() const { return _FileLine; }
			///
			/// Represents the exception in text format.
			///
			virtual inline std::string ToString() const { std::ostringstream os; os << _Message << " " << " (fileline " << _FileLine << ")"; return os.str(); }
		};
		
		///
		/// \brief Server class for connection.
		///
		/// Provides definitions and constants to connect to DB web servers. This class is intended for derivation or usage through predefined constants.
		///
		class Server
		{
		protected:
			///
			/// Host name or IP of the web server.
			///
			std::string _HostName;
			///
			/// Port of the web server. Notice this is set independently of the usage of SSL.
			///
			unsigned short _Port;
			///
			/// Decides the usage of SSL/TLS or non-encrypted session (not implemented so far).
			///
			bool _UseSSL;

			
		public:
			///
			/// Host name or IP address of the web server.
			///
			inline const std::string& HostName() const { return _HostName; }
			///
			/// Port of the web server.
			///
			inline unsigned short Port() const { return _Port; }
			///
			/// Shows usage of SSL/TLS protocols or non-encrypted HTTP.
			///
			inline bool UseSSL() const { return _UseSSL; }
			///
			/// Builds an empty Server structure.
			///
			inline Server() { _HostName = ""; _Port = 0; _UseSSL = true; }
			
			///
			/// Predefined server constant to connect to the Lyon web server.
			///
			static Server Lyon;
			///
			/// Predefined server constant to connect to the secondary Lyon web server.
			///
			static Server Lyon2;
			///
			/// Predefined server constant to connect to the Napoli web server.
			///
			static Server Napoli;
			///
			/// Predefined server constant to connect to the central web Server. Retrieves the Lyon server in this version.
			/// 	
			static Server Default;		
			///
			/// Test server constant to connect to the central web Server. Retrieves the Lyon2 server in this version.
			/// 	
			static Server Test;
		};
		
		///
		/// \brief Result set in n-tuple form.
		///
		/// Represents the output of a query in an n-tuple form. This class is abstract and not intended for direct instantiation.
		///
		class ResultSet
		{
		public:
			///
			/// Advances to the next record if available and returns <c>false</c> if there are no more records.
			///
			virtual bool Next() = 0;
			///
			/// Retrieves the number of fields in the result set.
			///
			virtual unsigned int FieldCount() const = 0;
			///
			/// Retrieves the name of a field in the results set.
			///
			virtual const std::string& FieldName(unsigned int i) const = 0;
			///
			/// Retrieves the value of the i-th field of the current record as a string.
			///
			virtual const std::string& GetString(unsigned int i) const = 0;
			///
			/// Closes the ResultSet. Needed if one is going to issue another query on the same Client.
			///
			virtual void Close() = 0;
		};
		
		///
		/// \brief Selector object for queries with a variable number of parameters.
		///
		/// Represents a selection that applies to a query with a variable number of parameters.
		///
		struct Selector
		{
		public:
			///
			/// Name of the variable.
			///
			std::string Name;
			///
			/// Value of the variable.
			///
			std::string Value;

			///
			/// Selector relational operator
			///
			class RelOp
			{
			private:
				std::string _relop;

				RelOp(const char *ch) { _relop = ch; }
			public:
				inline std::string const &Render() const { return _relop; }

				static const RelOp Equal;
				static const RelOp Different;
				static const RelOp Less;
				static const RelOp Greater;
				static const RelOp LessEqual;
				static const RelOp GreaterEqual;
			};

			///
			/// The relational operator to use for comparison
			///
			RelOp const &RelationalOperator;

			///
			/// Builds a selector.
			///
			inline Selector(const char *name, const char *val, RelOp const &ro = RelOp::Equal) : Name(name), Value(val), RelationalOperator(ro) {}
			///
			/// Builds an empty selector.
			///
			inline Selector() : Name(""), Value(""), RelationalOperator(RelOp::Equal) {}
			///
			/// Copies a Selector.
			///
			inline Selector(const Selector &S) : Name(S.Name), Value(S.Value), RelationalOperator(S.RelationalOperator) {}

		};
				
		///
		/// \brief Client to issue queries on a web server.
		///
		/// A client class to connect to a web server and issue queries. 
		/// Only one resultset per client is supported. 
		/// When a new query is issued, the previous resultset is automatically closed and/or reset. 
		///
		class Client
		{
		public:
			///
			/// Creates a new Client with server, username and password.
			///
			static std::shared_ptr<Client> Create(const Server &srv, const char *usr, const char *pwd);
			///
			/// Creates a new Client with a persistent connection.
			///
			static std::shared_ptr<Client> Create(const Server &srv, const char *persistentcookie);
			///
			/// Creates a new Client using local machine defaults.
			///
			static std::shared_ptr<Client> Create();
			
			///
			/// Shows the username associated to this client.
			///
			virtual const std::string& User() const = 0;
			///
			/// Shows the server used by this client.
			///
			virtual const Server &ServerEndPoint() const = 0;
			///
			/// Closes the client and frees resources. 
			/// It is called automatically on destruction. 
			/// A closed client cannot be used any longer.
			///
			virtual void Close() = 0;
			
			
			/// 
			/// Deprecated. Reads a detx file from web server into a string specifying detector id (number), run and possibly a calibration set.
			///
			virtual std::shared_ptr<std::stringstream> DetX(int detector, int run, const char *t0set, const char *calibset) = 0;
			///
			/// Deprecated. Reads a detx file from web server into a string specifying detector id (string), run and possibly a calibration set.
			///
			virtual std::shared_ptr<std::stringstream> DetX(const char *detector, int run, const char *t0set, const char *calibset) = 0;
			/// 
			/// Reads a detx file from web server into a string specifying detector id (number) and a run.
			///
			virtual std::shared_ptr<std::stringstream> DetX(int detector, int run) = 0;
			///
			/// Reads a detx file from web server into a string specifying detector id (string) and a run.
			///
			virtual std::shared_ptr<std::stringstream> DetX(const char *detector, int run) = 0;					
			/// 
			/// Reads a detx file from web server into a string specifying detector id (number), a run and the version of the format.
			///
			virtual std::shared_ptr<std::stringstream> DetX(int detector, int run, int version) = 0;
			///
			/// Reads a detx file from web server into a string specifying detector id (string), a run and the version of the format.
			///
			virtual std::shared_ptr<std::stringstream> DetX(const char *detector, int run, int version) = 0;
			/// 
			/// Reads a detx file from web server into a string specifying detector id (number), run and time/position/rotation calibration.
			///
			virtual std::shared_ptr<std::stringstream> DetX(int detector, const char *tcal, const char *pcal, const char *rcal) = 0;
			///
			/// Reads a detx file from web server into a string specifying detector id (string), run and time/position/rotation calibration.
			///
			virtual std::shared_ptr<std::stringstream> DetX(const char *detector, const char *tcal, const char *pcal, const char *rcal) = 0;			
			/// 
			/// Reads a detx file from web server into a string specifying detector id (number), run, PMT time/position/rotation/acoustic time/compass rotation/status calibration and the format version.
			///
			virtual std::shared_ptr<std::stringstream> DetX(int detector, const char *tcal, const char *pcal, const char *rcal, const char *acal, const char *ccal, const char *scal, int version) = 0;
			///
			/// Reads a detx file from web server into a string specifying detector id (string), run, PMT time/position/rotation/acoustic time/compass rotation/status calibration and the format version.
			///
			virtual std::shared_ptr<std::stringstream> DetX(const char *detector, const char *tcal, const char *pcal, const char *rcal, const char *acal, const char *ccal, const char *scal, int version) = 0;			
			///
			/// Lists the Stream Data Services available on the connected server.
			///
			virtual ResultSet &StreamDS() = 0;
			///
			/// Reads a data stream into a result set using selectors.
			///
			virtual ResultSet &StreamDS(const char *streamname, const std::vector<Selector> &selectors) = 0;
			/// 
			/// Returns help on the specified entrypoint of APIv2 in JSON format.
			/// 
			virtual std::shared_ptr<std::stringstream> APIv2Help(const char *apiversion, const char *entrypoint) = 0;
			/// 
			/// Selects data using the APIv2 interface. The output is mostly in JSON format.
			/// 
			virtual std::shared_ptr<std::stringstream> APIv2Select(const char *apiversion, const char *entrypoint, const std::vector<Selector> &selectors) = 0;
		};
	}
}

	
#endif