/*
 * This file and its contents are licensed under the Apache License 2.0.
 * Please see the included NOTICE for copyright information and
 * LICENSE-APACHE for a copy of the license.
 */
#pragma once

#include <postgres.h>
#include <nodes/primnodes.h>
#include <utils/array.h>

#include "dimension.h"
#include "export.h"
#include "scan_iterator.h"
#include "scanner.h"
#include "ts_catalog/catalog.h"
#include "ts_catalog/tablespace.h"

#define INVALID_HYPERTABLE_ID 0

typedef struct SubspaceStore SubspaceStore;
typedef struct Chunk Chunk;
typedef struct Hypercube Hypercube;
typedef struct ChunkRangeSpace ChunkRangeSpace;

/*
 * Remember to update ts_hypertable_status functions when adding new status flags
 */
#define HYPERTABLE_STATUS_DEFAULT 0
/* flag set when hypertable has an attached OSM chunk */
#define HYPERTABLE_STATUS_OSM 1
/*
 * Currently, the time slice range metadata is updated in
 * the timescaledb catalog with the min and max of the range managed by OSM.
 * However, this range has to be contiguous in order to
 * update our catalog with its min and max value. If it is not contiguous,
 * then we cannot store the min and max in our catalog because tuple routing
 * will not work properly with gaps in the range.
 * When attempting to insert into one of the gaps, which do not in fact contain
 * tiered data, we error out because this is perceived as an attempt to insert
 * into tiered chunks, which are immutable.
 * When the range is noncontiguous, we store [INT64_MAX - 1, INT64_MAX) and set
 * this flag.
 * This flag also serves to allow or block the ordered append optimization. When
 * the range covered by OSM is contiguous, then it is possible to do ordered
 * append.
 */
#define HYPERTABLE_STATUS_OSM_CHUNK_NONCONTIGUOUS 2
/* compression enabled on hypertable */
#define HYPERTABLE_STATUS_COMPRESSION 4

#define TS_HYPERTABLE_HAS_COMPRESSION_ENABLED(ht)                                                  \
	(((ht)->fd.status & HYPERTABLE_STATUS_COMPRESSION) != 0)

#define TS_HYPERTABLE_HAS_DIRECT_COMPRESS_ENABLED(ht)                                              \
	(((ht)->fd.status & HYPERTABLE_STATUS_DIRECT_COMPRESS) != 0)

typedef struct Hypertable
{
	FormData_hypertable fd;
	Oid main_table_relid;
	Hyperspace *space;
	SubspaceStore *chunk_cache;
	ChunkRangeSpace *range_space;
} Hypertable;

/* create_hypertable record attribute numbers */
enum Anum_create_hypertable
{
	Anum_create_hypertable_id = 1,
	Anum_create_hypertable_schema_name,
	Anum_create_hypertable_table_name,
	Anum_create_hypertable_created,
	_Anum_create_hypertable_max,
};

#define Natts_create_hypertable (_Anum_create_hypertable_max - 1)

/* Create a generic hypertable */
enum Anum_generic_create_hypertable
{
	Anum_generic_create_hypertable_id = 1,
	Anum_generic_create_hypertable_created,
	_Anum_generic_create_hypertable_max,
};

#define Natts_generic_create_hypertable (_Anum_generic_create_hypertable_max - 1)

extern TSDLLEXPORT Oid ts_rel_get_owner(Oid relid);

typedef enum HypertableCreateFlags
{
	HYPERTABLE_CREATE_DISABLE_DEFAULT_INDEXES = 1 << 0,
	HYPERTABLE_CREATE_IF_NOT_EXISTS = 1 << 1,
	HYPERTABLE_CREATE_MIGRATE_DATA = 1 << 2,
} HypertableCreateFlags;

extern TSDLLEXPORT bool ts_hypertable_create_from_info(Oid table_relid, int32 hypertable_id,
													   uint32 flags, DimensionInfo *time_dim_info,
													   DimensionInfo *closed_dim_info,
													   Name associated_schema_name,
													   Name associated_table_prefix);

extern TSDLLEXPORT Hypertable *ts_hypertable_get_by_id(int32 hypertable_id);
extern Hypertable *ts_hypertable_get_by_name(const char *schema, const char *name);
extern TSDLLEXPORT bool ts_hypertable_get_attributes_by_name(const char *schema, const char *name,
															 FormData_hypertable *form);
extern TSDLLEXPORT bool ts_hypertable_has_privs_of(Oid hypertable_oid, Oid userid);
extern TSDLLEXPORT Oid ts_hypertable_permissions_check(Oid hypertable_oid, Oid userid);

extern TSDLLEXPORT void ts_hypertable_permissions_check_by_id(int32 hypertable_id);
extern Hypertable *ts_hypertable_from_tupleinfo(const TupleInfo *ti);
extern Hypertable *ts_resolve_hypertable_from_table_or_cagg(Cache *hcache, Oid relid,
															bool allow_matht);
extern int ts_hypertable_scan_with_memory_context(const char *schema, const char *table,
												  tuple_found_func tuple_found, void *data,
												  LOCKMODE lockmode, MemoryContext mctx);
extern bool ts_hypertable_add_status(Hypertable *ht, int32 status);
extern bool ts_hypertable_clear_status(Hypertable *ht, int32 status);
extern int ts_hypertable_set_name(Hypertable *ht, const char *newname);
extern int ts_hypertable_set_schema(Hypertable *ht, const char *newname);
extern int ts_hypertable_set_num_dimensions(Hypertable *ht, int16 num_dimensions);
extern int ts_hypertable_delete_by_name(const char *schema_name, const char *table_name);
extern int ts_hypertable_delete_by_id(int32 hypertable_id);
extern TSDLLEXPORT ObjectAddress ts_hypertable_create_trigger(const Hypertable *ht,
															  CreateTrigStmt *stmt,
															  const char *query);
extern TSDLLEXPORT void ts_hypertable_drop_trigger(Oid relid, const char *trigger_name);
extern TSDLLEXPORT void ts_hypertable_drop(Hypertable *hypertable, DropBehavior behavior);

extern int ts_hypertable_reset_associated_schema_name(const char *associated_schema);
extern TSDLLEXPORT Oid ts_hypertable_id_to_relid(int32 hypertable_id, bool return_invalid);
extern TSDLLEXPORT int32 ts_hypertable_relid_to_id(Oid relid);
extern TSDLLEXPORT Chunk *ts_hypertable_find_chunk_for_point(const Hypertable *h,
															 const Point *point, LOCKMODE lockmode);
extern TSDLLEXPORT Chunk *ts_hypertable_chunk_store_add(const Hypertable *h,
														const Chunk *input_chunk);
extern TSDLLEXPORT Chunk *ts_hypertable_create_chunk_for_point(const Hypertable *h,
															   const Point *point,
															   LOCKMODE chunk_lockmode);
extern Oid ts_hypertable_relid(RangeVar *rv);
extern TSDLLEXPORT bool ts_is_hypertable(Oid relid);
extern bool ts_hypertable_has_tablespace(const Hypertable *ht, Oid tspc_oid);
extern Tablespace *ts_hypertable_select_tablespace(const Hypertable *ht, const Chunk *chunk);
extern const char *ts_hypertable_select_tablespace_name(const Hypertable *ht, const Chunk *chunk);
extern Tablespace *ts_hypertable_get_tablespace_at_offset_from(int32 hypertable_id,
															   Oid tablespace_oid, int16 offset);
extern TSDLLEXPORT bool ts_hypertable_has_chunks(Oid table_relid, LOCKMODE lockmode);
extern void ts_hypertables_rename_schema_name(const char *old_name, const char *new_name);
extern bool ts_is_partitioning_column(const Hypertable *ht, AttrNumber column_attno);
extern bool ts_is_partitioning_column_name(const Hypertable *ht, NameData column_name);
extern TSDLLEXPORT bool ts_hypertable_set_compression(Hypertable *ht);
extern TSDLLEXPORT bool ts_hypertable_unset_compression(Hypertable *ht);
extern TSDLLEXPORT bool ts_hypertable_set_direct_compress(Hypertable *ht);
extern TSDLLEXPORT bool ts_hypertable_unset_direct_compress(Hypertable *ht);
extern TSDLLEXPORT bool ts_hypertable_set_compress_interval(Hypertable *ht,
															int64 compress_interval);
extern TSDLLEXPORT int64 ts_hypertable_get_open_dim_max_value(const Hypertable *ht,
															  int dimension_index, bool *isnull);

extern TSDLLEXPORT bool ts_hypertable_has_continuous_aggregates(int32 hypertable_id);
extern TSDLLEXPORT void ts_hypertable_formdata_fill(FormData_hypertable *fd, const TupleInfo *ti);

#define hypertable_scan(schema, table, tuple_found, data, lockmode)                                \
	ts_hypertable_scan_with_memory_context(schema,                                                 \
										   table,                                                  \
										   tuple_found,                                            \
										   data,                                                   \
										   lockmode,                                               \
										   CurrentMemoryContext)
