#ifndef PGSQLDATABASECATALOGUE_H
#define PGSQLDATABASECATALOGUE_H

#include <libpq-fe.h>
#include <QObject>
#include <QString>
#include <functional>
#include <bitset>
#include <memory>
#include <vector>

namespace Pgsql {

	class Connection;

}

class PgAttributeContainer;
class PgAuthIdContainer;
class PgClassContainer;
class PgConstraintContainer;
class PgDatabaseContainer;
class PgIndexContainer;
class PgNamespaceContainer;
class PgAmContainer;
class PgTablespaceContainer;
class PgTriggerContainer;
class PgTypeContainer;
class PgProcContainer;
class PgCollationContainer;
class PgInheritsContainer;
class PgLanguageContainer;
class PgSequenceContainer;

/// Manages all the catalog data for the database so the program
/// can efficiently retrieve information from memory.
///
/// All the containers are created once during the first load. After
/// that they are reused on reload so signals will stay connected.
class PgDatabaseCatalog: public QObject, public std::enable_shared_from_this<PgDatabaseCatalog> {
	Q_OBJECT
public:
	PgDatabaseCatalog();
	PgDatabaseCatalog(const PgDatabaseCatalog&) = delete;
	PgDatabaseCatalog& operator = (const PgDatabaseCatalog&) = delete;
	~PgDatabaseCatalog();

	void loadAll(Pgsql::Connection &conn,
		std::function<bool(int, int)> progress_callback);

	void loadInfo(Pgsql::Connection &conn);

	const QString& serverVersionString() const;
	int serverVersion() const;
	const QString& getDBName() const { return m_dbName; }

	std::shared_ptr<const PgAttributeContainer> attributes() const;
	std::shared_ptr<const PgAuthIdContainer> authIds() const;
	std::shared_ptr<const PgClassContainer> classes() const;
	std::shared_ptr<const PgConstraintContainer> constraints() const;
	std::shared_ptr<const PgDatabaseContainer> databases() const;
	std::shared_ptr<const PgIndexContainer> indexes() const;
	std::shared_ptr<const PgAmContainer> ams() const;
	std::shared_ptr<const PgNamespaceContainer> namespaces() const;
	std::shared_ptr<const PgTablespaceContainer> tablespaces() const;
	std::shared_ptr<const PgTriggerContainer> triggers() const;
	std::shared_ptr<const PgTypeContainer> types() const;
	std::shared_ptr<const PgProcContainer> procs() const;
	std::shared_ptr<const PgCollationContainer> collations() const;
	std::shared_ptr<const PgInheritsContainer> inherits() const;
	std::shared_ptr<const PgLanguageContainer> languages() const;
	std::shared_ptr<const PgSequenceContainer> sequences() const;

	enum RefreshFlag {
		Attributes = 1,
		AuthIds = (1 << 1),
		Classes = (1 << 2),
		Constraints = (1 << 3),
		Databases = (1 << 4),
		Indexes = (1 << 5),
		Ams = (1 << 6),
		Namespaces = (1 << 7),
		Tablespaces = (1 << 8),
		Triggers = (1 << 9),
		Types = (1 << 10),
		Proc = (1 << 11),
		All = 0xffffffff
	};
	using RefreshFlags = int;

Q_SIGNALS:
	void refreshed(const PgDatabaseCatalog *catalog, RefreshFlags flags);
private:
	QString m_serverVersionString;
	int m_serverVersion;
	QString m_dbName;

	std::shared_ptr<PgAttributeContainer> m_attributes;
	std::shared_ptr<PgAuthIdContainer> m_authIds;
	std::shared_ptr<PgClassContainer> m_classes;
	std::shared_ptr<PgConstraintContainer> m_constraints;
	std::shared_ptr<PgDatabaseContainer> m_databases;
	std::shared_ptr<PgIndexContainer> m_indexes;
	std::shared_ptr<PgAmContainer> m_ams;
	std::shared_ptr<PgNamespaceContainer> m_namespaces;
	std::shared_ptr<PgTablespaceContainer> m_tablespaces;
	std::shared_ptr<PgTriggerContainer> m_triggers;
	std::shared_ptr<PgTypeContainer> m_types;
	std::shared_ptr<PgProcContainer> m_procs;
	std::shared_ptr<PgCollationContainer> m_collations;
	std::shared_ptr<PgInheritsContainer> m_inherits;
	std::shared_ptr<PgLanguageContainer> m_languages;
	std::shared_ptr<PgSequenceContainer> m_sequences;

	template <typename T>
	void load2(std::shared_ptr<T> &ptr, Pgsql::Connection &conn)
	{
		if (!ptr)
			ptr = std::make_shared<T>(*this);

		ptr->loadAll(conn);
	}

};

QString getRoleNameFromOid(const PgDatabaseCatalog &cat, Oid oid);
QString getRoleDisplayString(const PgDatabaseCatalog &cat, Oid oid);
QString getTablespaceDisplayString(const PgDatabaseCatalog &cat, Oid oid);
QString getTypeDisplayString(const PgDatabaseCatalog &cat, Oid oid, int32_t typmod = -1);
QString getIndexDisplayString(const PgDatabaseCatalog &cat, Oid oid);
QString getClassDisplayString(const PgDatabaseCatalog &cat, Oid oid);


#endif // PGSQLDATABASECATALOGUE_H
