#pragma once

#include <postgres.h>

#include <access/genam.h>
#include <access/tableam.h>
#include <storage/shmem.h>

#ifndef SO_NONE
#	define SO_NONE 0
#endif

#ifdef _WIN32
#	define PRId64 "I64d"
#	define PRIu64 "I64u"
#	define PGRN_PRIdSIZE "Id"
#	define PGRN_PRIuSIZE "Iu"
#else
#	include <inttypes.h>
#	define PGRN_PRIdSIZE "zd"
#	define PGRN_PRIuSIZE "zu"
#endif

#ifdef PGRN_HAVE_MSGPACK
#	define PGRN_SUPPORT_WAL
#endif

#if PG_VERSION_NUM >= 150000
#	define PGRN_SUPPORT_WAL_RESOURCE_MANAGER
#endif

#if PG_VERSION_NUM >= 160000
#	define PGRN_HAVE_VARATT_H
#endif

#if PG_VERSION_NUM >= 170000
#	define PGRN_SUPPORT_PARALLEL_INDEX_BUILD
#endif

#if PG_VERSION_NUM >= 180000
#	define PGRN_SUPPORT_AMTRANSLATE_ROUTINE
#	define PGRN_SUPPORT_FUNCEXPR_STRICT_1_2
#endif

#if PG_VERSION_NUM >= 160000
#	define PGRN_RELATION_GET_LOCATOR(relation) ((relation)->rd_locator)
#	define PGRN_RELATION_GET_LOCATOR_NUMBER(relation)                         \
		((relation)->rd_locator.relNumber)
#	define PGRN_RELATION_GET_LOCATOR_SPACE(relation)                          \
		((relation)->rd_locator.spcOid)
#	include <common/relpath.h>
typedef RelFileNumber PGrnRelFileNumber;
#	define PGrnRelidByRelfilenumber(tablespaceOid, fileNumber)                \
		RelidByRelfilenumber((tablespaceOid), (fileNumber))
#	define pgrn_pg_tablespace_ownercheck(tablespaceOid, userOid)              \
		object_ownercheck(TableSpaceRelationId, (tablespaceOid), (userOid))
#	define pgrn_pg_class_ownercheck(relationOid, userOid)                     \
		object_ownercheck(RelationRelationId, (relationOid), (userOid))
#else
#	define PGRN_RELATION_GET_LOCATOR(relation) ((relation)->rd_node)
#	define PGRN_RELATION_GET_LOCATOR_NUMBER(relation)                         \
		((relation)->rd_node.relNode)
#	define PGRN_RELATION_GET_LOCATOR_SPACE(relation)                          \
		((relation)->rd_node.spcNode)
typedef Oid PGrnRelFileNumber;
#	define PGrnRelidByRelfilenumber(tablespaceOid, fileNumber)                \
		RelidByRelfilenode((tablespaceOid), (fileNumber))
#	define pgrn_pg_tablespace_ownercheck(tablespaceOid, userOid)              \
		pg_tablespace_ownercheck((tablespaceOid), (userOid))
#	define pgrn_pg_class_ownercheck(relationOid, userOid)                     \
		pg_class_ownercheck((relationOid), (userOid))
#endif

#if PG_VERSION_NUM >= 150000
#	define PGRN_RELKIND_HAS_PARTITIONS(relkind) RELKIND_HAS_PARTITIONS(relkind)
#	define PGRN_RELKIND_HAS_TABLE_AM(relkind) RELKIND_HAS_TABLE_AM(relkind)
#else
#	define PGRN_RELKIND_HAS_PARTITIONS(relkind)                               \
		((relkind) == RELKIND_PARTITIONED_TABLE ||                             \
		 (relkind) == RELKIND_PARTITIONED_INDEX)
#	define PGRN_RELKIND_HAS_TABLE_AM(relkind)                                 \
		((relkind) == RELKIND_RELATION || (relkind) == RELKIND_TOASTVALUE ||   \
		 (relkind) == RELKIND_MATVIEW)
#endif

#if PG_VERSION_NUM >= 180000
#	define PGRN_PARALLEL_SCAN_GET_PS_OFFSET_AM(parallelScan)                  \
		((parallelScan)->ps_offset_am)
#else
#	define PGRN_PARALLEL_SCAN_GET_PS_OFFSET_AM(parallelScan)                  \
		((parallelScan)->ps_offset)
#endif

#if PG_VERSION_NUM < 180000
#	define pg_noreturn
#endif

#if PG_VERSION_NUM < 180000
#	define COMPARE_LT BTLessStrategyNumber
#	define COMPARE_GT BTGreaterStrategyNumber
#	define pk_cmptype pk_strategy
#endif

#include <utils/jsonb.h>
#if PG_VERSION_NUM >= 190000
typedef JsonbInState PGrnJsonState;
#else
typedef JsonbParseState *PGrnJsonState;
#endif

#if PG_VERSION_NUM >= 190000
#	define PGRN_SIG_DFL PG_SIG_DFL
#else
#	define PGRN_SIG_DFL SIG_DFL
#endif

static inline HTAB *
PGrnShmemInitHash(const char *name,
				  long init_size,
				  long max_size,
				  HASHCTL *infoP,
				  int hash_flags)
{
#if PG_VERSION_NUM >= 190000
	(void) init_size;
	return ShmemInitHash(name, max_size, infoP, hash_flags);
#else
	return ShmemInitHash(name, init_size, max_size, infoP, hash_flags);
#endif
}

static inline IndexScanDesc
pgrn_index_beginscan(Relation heapRelation,
					 Relation indexRelation,
					 Snapshot snapshot,
#if PG_VERSION_NUM >= 180000
					 IndexScanInstrumentation *instrument,
#else
					 void *instrument,
#endif
					 int nKeys,
					 int nOrderBys,
					 uint32 flags)
{

	return index_beginscan(heapRelation,
						   indexRelation,
						   snapshot,
#if PG_VERSION_NUM >= 180000
						   instrument,
#endif
						   nKeys,
						   nOrderBys
#if PG_VERSION_NUM >= 190000
						   ,
						   flags
#endif
	);
}

static inline TableScanDesc
pgrn_table_beginscan(Relation relation,
					 Snapshot snapshot,
					 int nKeys,
					 ScanKeyData *scanKeys,
					 uint32 flags)
{
	return table_beginscan(relation,
						   snapshot,
						   nKeys,
						   scanKeys
#if PG_VERSION_NUM >= 190000
						   ,
						   flags
#endif
	);
}

static inline TableScanDesc
pgrn_table_beginscan_parallel(Relation relation,
							  ParallelTableScanDesc pscan,
							  uint32 flags)
{
	return table_beginscan_parallel(relation,
									pscan
#if PG_VERSION_NUM >= 190000
									,
									flags
#endif
	);
}

#if PG_VERSION_NUM >= 180000
#	include <utils/lsyscache.h>
#	define pgrn_get_opfamily_name(opfid, missing_ok)                          \
		get_opfamily_name((opfid), (missing_ok))
#else
/* Borrowed from src/backend/utils/cache/lsyscache.c in PostgreSQL.
 *
 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 */
#	include <access/htup_details.h>
#	include <catalog/pg_opfamily.h>
#	include <utils/syscache.h>

static inline void *
pgrn_GETSTRUCT(const HeapTupleData *tuple)
{
	return ((char *) (tuple->t_data) + tuple->t_data->t_hoff);
}

static inline char *
pgrn_get_opfamily_name(Oid opfid, bool missing_ok)
{
	HeapTuple tup;
	char *opfname;
	Form_pg_opfamily opfform;

	tup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfid));

	if (!HeapTupleIsValid(tup))
	{
		if (!missing_ok)
			elog(ERROR, "cache lookup failed for operator family %u", opfid);
		return NULL;
	}

	opfform = (Form_pg_opfamily) pgrn_GETSTRUCT(tup);
	opfname = pstrdup(NameStr(opfform->opfname));

	ReleaseSysCache(tup);

	return opfname;
}
#endif

static inline Jsonb *
PGrnJsonbValueToJsonb(PGrnJsonState state)
{
#if PG_VERSION_NUM >= 190000
	pushJsonbValue(&state, WJB_END_ARRAY, NULL);
	return JsonbValueToJsonb(state.result);
#else
	JsonbValue *objects;
	objects = pushJsonbValue(&state, WJB_END_ARRAY, NULL);
	return JsonbValueToJsonb(objects);
#endif
}
