#ifndef ASYNCDBCONNECTION_H
#define ASYNCDBCONNECTION_H

#include <QObject>

#include "Pgsql_Connection.h"
#include "Pgsql_Params.h"
#include "Pgsql_Result.h"
#include "Expected.h"
#include "ConnectionConfig.h"
#include <QElapsedTimer>
#include <mutex>
#include <memory>

class ASyncDBConnectionThread;
/** \brief Class that handles asynchronous execution of queries.
 *
 * Queries are passed to this class with a routine to call on completion
 * when the result is on that routine is called.
 */
class ASyncDBConnection: public QObject {
    Q_OBJECT
public:

	enum class State {
		NotConnected,
		Connecting,
		Connected, ///< connected and idle
		QuerySend, ///< connected query send expecting result
		CancelSend, ///< cancel send expecting result
		Terminating ///< shutting down
	};

    class StateData {
    public:
        State State;
        QString Message;

        StateData(enum State state, QString message = QString())
            : State(state)
            , Message(message)
        {}
    };

	using on_result_callback = std::function<void(Expected<std::shared_ptr<Pgsql::Result>>, qint64)>;

	explicit ASyncDBConnection();
    ~ASyncDBConnection();

	State state() const;
	void setupConnection(const ConnectionConfig &config);
	void closeConnection();

	/** Sends command to the server.

	When the result is in on_result will be	called directly within the thread.

	If the command gives multiple results on_result will be called for each result.
	*/
	bool send(const std::string &command, on_result_callback on_result);
	bool send(const std::string &command, Pgsql::Params params, on_result_callback on_result);

	bool cancel();
    
Q_SIGNALS:
    void onStateChanged();
    void onNotice(Pgsql::ErrorDetails notice);

private:
	Pgsql::Connection m_connection;
	std::unique_ptr<ASyncDBConnectionThread> m_threadData;
	std::thread m_thread;
	ConnectionConfig m_config;
	State m_state = State::NotConnected;
    Pgsql::Canceller m_canceller;
    
    QElapsedTimer m_timer;
    
    void doStateCallback(State state);
    void processNotice(const PGresult *result);

	friend class ASyncDBConnectionThread;
};

Q_DECLARE_METATYPE(ASyncDBConnection::State);
Q_DECLARE_METATYPE(Pgsql::ErrorDetails);
Q_DECLARE_METATYPE(std::shared_ptr<Pgsql::Result>);


#endif // ASYNCDBCONNECTION_H
