#include "NamespaceItemModel.h"
#include "catalog/PgNamespace.h"
#include "catalog/PgNamespaceContainer.h"
#include "ScopeGuard.h"

namespace NamespaceItemModel_impl {

	class Node {
	public:

		virtual ~Node() = default;
		virtual int getRowCount() const = 0;
		virtual Qt::CheckState getCheckState() const = 0;
		virtual void setChecked(NamespaceItemModel *model, const QModelIndex &index, bool checked) = 0;
		virtual QVariant data(const QModelIndex &index, int role) const = 0;
		virtual int columnCount() const = 0;

	};
	class GroupNode;

	class LeafNode: public Node {
	public:
		std::weak_ptr<GroupNode> parent;
		bool checked = false;

		PgNamespace ns;
		LeafNode(std::weak_ptr<GroupNode> p, const PgNamespace &nspace)
			: parent(p)
			, ns(nspace)
		{}

		int getRowCount() const override
		{
			return 0;
		}

		Qt::CheckState getCheckState() const override
		{
			return checked ? Qt::Checked : Qt::Unchecked;
		}

		void setChecked(NamespaceItemModel *model, const QModelIndex &index, bool chk) override
		{
			checked = chk;
			emit model->dataChanged(index, index);
			emit model->dataChanged(index.parent(), index.parent());
		}

		QVariant data(const QModelIndex &index, int role) const override
		{
			QVariant v;
			if (role == Qt::DisplayRole) {
				switch (index.column()) {
				case NamespaceItemModel::ColNamespaceName:
					return ns.objectName();
				case NamespaceItemModel::ColOwner:
					return ns.ownerName();
				case NamespaceItemModel::ColAcl:
					return ns.aclString();
				}
			}
			else if (role == Qt::CheckStateRole) {
				if (index.column() == 0)
					return getCheckState();
			}
			return {};
		}

		int columnCount() const override
		{
			return NamespaceItemModel::ColCount;
		}

		bool operator < (const LeafNode &rhs) const
		{
			return ns.objectName() < rhs.ns.objectName();
		}

	};

	class GroupNode: public Node {
	public:
		using LeafVec = QVector<std::shared_ptr<LeafNode>>;

		QString name;
		LeafVec leaves;

		GroupNode(QString n)
			: name(n)
		{}

		int getRowCount() const override
		{
			return leaves.size();
		}

		void sortLeaves()
		{
			std::sort(leaves.begin(), leaves.end(),
					  [] (auto l, auto r) -> bool { return *l < *r; });
		}

		Qt::CheckState getCheckState() const override
		{
			bool some_checked = false;
			bool some_unchecked = false;
			for (auto&& l : leaves) {
				if (l->checked)
					some_checked = true;
				else
					some_unchecked = true;
			}
			Qt::CheckState result;
			if (some_checked && some_unchecked)
				result = Qt::PartiallyChecked;
			else if (some_checked)
				result = Qt::Checked;
			else
				result = Qt::Unchecked;

			return result;
		}

		void setChecked(NamespaceItemModel *model, const QModelIndex &index, bool chk) override
		{
			if (chk)
				for (auto&& l : leaves)
					l->checked = true;
			if (!chk)
				for (auto&& l : leaves)
					l->checked = false;
			emit model->dataChanged(index, index);
			emit model->dataChanged(model->index(0, 0, index), model->index(leaves.size(), 0, index));
		}

		QVariant data(const QModelIndex &index, int role) const override
		{
			QVariant v;
			if (role == Qt::DisplayRole) {
				switch (index.column()) {
				case NamespaceItemModel::ColNamespaceName:
					return name;
				}
			}
			else if(role == Qt::CheckStateRole) {
				if (index.column() == 0)
					return getCheckState();
			}
			return {};
		}

		int columnCount() const override
		{
			return 1;
		}

	};

} // end of NamespaceItemModel_impl
using namespace NamespaceItemModel_impl;

NamespaceItemModel::NamespaceItemModel(QObject *parent)
	: QAbstractItemModel(parent)
{

}

void NamespaceItemModel::init(std::shared_ptr<const PgNamespaceContainer> ns)
{
	beginResetModel();
	SCOPE_EXIT { endResetModel(); };

	auto system = std::make_shared<GroupNode>("System");
	auto user = std::make_shared<GroupNode>("User");
	groups = { system, user };

	for (auto&& e : *ns)
		if (e.isSystemCatalog())
			system->leaves.push_back(std::make_shared<LeafNode>(system, e));
		else
			user->leaves.push_back(std::make_shared<LeafNode>(user, e));

	system->sortLeaves();
	user->sortLeaves();
	for (auto&& e : user->leaves)
		e->checked = true;
}

void NamespaceItemModel::setEnableCheckboxes(bool enable)
{
	if (m_enableCheckboxes != enable) {
		beginResetModel();
		SCOPE_EXIT { endResetModel(); };
		m_enableCheckboxes = enable;
	}

}

bool NamespaceItemModel::isEnableCheckboxes() const
{
	return m_enableCheckboxes;
}

QModelIndex NamespaceItemModel::index(int row, int column,
									  const QModelIndex &parent) const
{
	QModelIndex result;
	if (hasIndex(row, column, parent)) {

		// We only have two levels, the groups and the namespaces
		if (parent.isValid()) {	// namespace level
			auto grp = static_cast<GroupNode*>(parent.internalPointer());
			result = createIndex(row, column, grp->leaves[row].get());
		}
		else { // toplevel (groups)
			const auto grp = groups[row];

			result = createIndex(row, column, grp.get());
		}
	}
	return result;
}

QModelIndex NamespaceItemModel::parent(const QModelIndex &index) const
{
	QModelIndex result;
	if (index.isValid()) {
		auto n = static_cast<Node*>(index.internalPointer());
		auto ln = dynamic_cast<LeafNode*>(n);
		if (ln) { // leafnode
			auto grp = ln->parent.lock(); // Get the parent group
//			auto fr = std::find(groups.begin(), groups.end(), grp); // find it in the list
//			int row = fr - groups.begin(); // calculate index ie row
			int row = groups.indexOf(grp);
			result = createIndex(row, 0, grp.get()); // return index
		}

	}
	return result;
}

int NamespaceItemModel::rowCount(const QModelIndex &parent) const
{
	int count = 0;
	if (parent.isValid() && parent.column() <= 0) {
		auto node = static_cast<Node*>(parent.internalPointer());
		count = node->getRowCount();
	}
	else {
		count = groups.size();
	}
	return count;
}

int NamespaceItemModel::columnCount(const QModelIndex &/*index*/) const
{
//	if (index.isValid()) {
//		auto *n = static_cast<Node*>(index.internalPointer());
//		return n->columnCount();
//	}
	return ColCount;
}

QVariant NamespaceItemModel::data(const QModelIndex &index, int role) const
{
	QVariant v;
	auto *n = static_cast<Node*>(index.internalPointer());
	v = n->data(index, role);
	return v;
}

bool NamespaceItemModel::setData(const QModelIndex &index,
								const QVariant &value, int role)
{
	if(!index.isValid() || role != Qt::CheckStateRole)
		return false;

	auto *n = static_cast<Node*>(index.internalPointer());
	if (index.column() == 0)
		n->setChecked(this, index, value == Qt::Checked);

	return true;
}

Qt::ItemFlags NamespaceItemModel::flags(const QModelIndex &index) const
{
	Qt::ItemFlags flags = QAbstractItemModel::flags(index);
	if (index.column() == 0 && m_enableCheckboxes)
		flags.setFlag(Qt::ItemIsUserCheckable);
	return flags;
}



QVariant NamespaceItemModel::headerData(int section, Qt::Orientation orientation, int role) const
{
	if (orientation == Qt::Horizontal) {
		if (role == Qt::DisplayRole) {
			switch (section) {
			case ColNamespaceName: return tr("Name");
			case ColOwner: return tr("Owner");
			case ColAcl: return tr("ACL");
			}
		}
	}
	return {};
}
