#include "PgClassContainer.h"
#include "Pgsql_Col.h"
#include "PgDatabaseCatalog.h"
#include <iterator>

std::string PgClassContainer::getLoadQuery() const
{
    std::string q = "SELECT pg_class.oid, relname, relnamespace, reltype, reloftype, "
		"  relowner, relam, relfilenode, reltablespace, relpages, "
		"  reltuples, reltoastrelid, relisshared, relpersistence, "
		"  relkind, relispopulated, relfrozenxid, relminmxid, "
        "  reloptions, d.description, "
        "  relacl, pg_get_viewdef(pg_class.oid)";

    if (lessThenVersion(120000))
		q += ", relhasoids ";
    if (minimumVersion(100000))
        q +=
            ", pg_get_expr(relpartbound, pg_class.oid)"
            ", partstrat, partnatts, partattrs, partclass, partcollation "
            ", pg_get_expr(partexprs, partrelid) AS partexprs";
    if (minimumVersion(110000))
        q += ", partdefid";

    q +=
        "\nFROM pg_catalog.pg_class \n"
        "    LEFT JOIN pg_catalog.pg_description AS d ON (objoid=pg_class.oid AND objsubid=0) \n";

    if (minimumVersion(100000))
        q += "    LEFT JOIN pg_partitioned_table AS pt ON (partrelid=pg_class.oid) \n";

    q +=
		"WHERE relkind IN ('r', 'i', 'p', 'I', 'v', 'm', 'f')";

	return q;
}

namespace {

    class PartitionedTableKeyItemsBuilder {
    public:
        int16_t attCount;
        std::vector<int16_t> attNums;
        std::vector<Oid> attOpClass;
        std::vector<Oid> attCollation;
        std::vector<QString> expressions;

        PartitioningKeyItems Build()
        {
            int expr_idx = 0;
            PartitioningKeyItems result(attCount);
            for (int attIdx = 0; attIdx < attCount; ++attIdx)
            {
                auto& item = result[attIdx];
                item.attNum = attNums[attIdx];
                item.opClass = attOpClass[attIdx];
                item.collation = attCollation[attIdx];
                if (item.attNum == 0)
                    item.expression = expressions[expr_idx++];
            }
            return result;
        }
    };

}

PgClass PgClassContainer::loadElem(const Pgsql::Row &row)
{
	Pgsql::Col col(row);
	Oid	class_oid = col.nextValue();
	QString name  = col.nextValue();
	Oid schema_oid = col.nextValue();

	PgClass v(m_catalog, class_oid, name, schema_oid);
    Oid owner;
	col >> v.type >> v.oftype
		>> owner >> v.am >> v.filenode >> v.tablespace >> v.pages_est
		>> v.tuples_est >> v.toastrelid >> v.isshared >> v.persistence
        >> v.kind >> v.ispopulated >> v.frozenxid >> v.minmxid
        >> v.options >> v.description;

	v.setOwnerOid(owner);

	AclList acl_list;
	col >> acl_list;
	v.setAcls(std::move(acl_list));

	auto vd = col.nextValue();
	if (!vd.null())
		v.viewdef = vd.asQString();

	if (lessThenVersion(120000))
		col >> v.hasoids;

    PgPartitionedTable &pt = v.partitionedTable;
    if (minimumVersion(100000))
    {
        PartitionedTableKeyItemsBuilder kibuilder;

        col >> v.partitionBoundaries;

        auto strategy = col.nextValue();
        if (strategy.null())
        {
            int s = minimumVersion(110000) ? 5 : 4;
            col.skip(s);
        }
        else
        {
            pt.strategy << strategy;
            col >> kibuilder.attCount;
            col.getAsVector<int16_t>(std::back_inserter(kibuilder.attNums));
            col.getAsVector<Oid>(std::back_inserter(kibuilder.attOpClass));
            col.getAsVector<Oid>(std::back_inserter(kibuilder.attCollation));
            col.getAsVector<QString>(std::back_inserter(kibuilder.expressions));

            pt.keyColumns = kibuilder.Build();
            if (minimumVersion(110000))
                col >> pt.defaultPartition;
        }
    }

    return v;
}
