#pragma once

#include <functional>
#include <string>
#include <libpq-fe.h>
#include <QString>

#include <memory>

#include "Pgsql_Canceller.h"
#include "Pgsql_Result.h"

namespace Pgsql {

	class Connection;
	/*
	This library has multiple layers.

	Layer 1 delivers lowlevel C++ wrappers for the basic libpq functionality. Adding
	automatic resource management.

	*/

	class Result;
	class Params;

	/** \brief Class for connecting to the database.
	 *
	 * The class isolates the programmer from the worst C style parts
	 * of the libpq API but is mostly a very thin wrapper.
	 */
	class Connection {
	public:
		Connection();
		~Connection();

		Connection(const Connection &rhs) = delete;
		Connection& operator=(const Connection &rhs) = delete;

		Connection(Connection &&rhs);
		Connection& operator=(Connection &&rhs);

		void connect(const char *params);
		void connect(const QString &params)
		{
			connect(params.toStdString().c_str());
		}

		void connect(const char *const * keywords, const char* const * values, int expand_dbname);
		bool connectStart(const char *params);
		bool connectStart(const std::string &params)
		{
			return connectStart(params.c_str());
		}

		bool connectStart(const QString &params)
		{
			return connectStart(params.toUtf8().data());
		}

		bool connectStart(const char * const *keywords,
						  const char * const *values);

		PostgresPollingStatusType connectPoll();
		ConnStatusType status();
		int socket();

		void close();

		Canceller getCancel();

		std::string getErrorMessage() const;

		Result query(const char * command);

		Result query(const QString &command)
		{
			return query(command.toUtf8().data());
		}

		Result queryParam(const char * command, const Params &params);
        Result queryParam(const std::string &command, const Params &params);
		Result queryParam(const QString &command, const Params &params);

        void sendQuery(const char * query);
        void sendQuery(const std::string &command)
		{
            sendQuery(command.c_str());
		}
        void sendQuery(const QString &command)
		{
            sendQuery(command.toUtf8().data());
		}

        void sendQueryParams(const char * command, const Params &params);

		std::shared_ptr<Result> getResult();
		std::shared_ptr<Result> getResultNoThrow();

		bool consumeInput();
		bool isBusy();

		void setNoticeReceiver(std::function<void(const PGresult *)> callback);

		std::string escapeLiteral(const std::string_view &literal);
		QString escapeLiteral(const QString &literal);
		std::string escapeIdentifier(const std::string_view &ident);
		QString escapeIdentifier(const QString &ident);

		QString getDBName() const;
	private:
		PGconn *conn = nullptr;
		std::function<void(const PGresult *)> notifyReceiver;

		static void testForConnectionError(PGconn *conn);
		void throwError(PGresult *result) const;
		static void notifyReceiveFunc(void *arg, const PGresult *result);
	};

}



