#include "postgres.h"
#include "storage/lwlock.h"
#include "storage/shmem.h"
#include "utils/guc.h"

#if PG_VERSION_NUM >=  160000

#include "utils/guc_hooks.h"

#else

#include "commands/variable.h"

#endif

#include "orafce.h"
#include "builtins.h"
#include "pipe.h"

#if PG_VERSION_NUM >= 150000

#include "miscadmin.h"

#endif

/*  default value */
char  *nls_date_format = NULL;
char  *orafce_timezone = NULL;

bool  orafce_initialized = false;

static const struct config_enum_entry orafce_compatibility_options[] = {
	{"warning_oracle", ORAFCE_COMPATIBILITY_WARNING_ORACLE, false},
	{"warning_orafce", ORAFCE_COMPATIBILITY_WARNING_ORAFCE, false},
	{"oracle", ORAFCE_COMPATIBILITY_ORACLE, false},
	{"orafce", ORAFCE_COMPATIBILITY_ORAFCE, false},
	{NULL, 0, false}
};

#if PG_VERSION_NUM >= 150000

static shmem_request_hook_type prev_shmem_request_hook = NULL;

#endif

#if PG_VERSION_NUM >= 150000

static void
orafce_shmem_request(void)
{
	if (prev_shmem_request_hook)
		prev_shmem_request_hook();

	RequestAddinShmemSpace(SHMEMMSGSZ);
}

#endif

static bool
check_sys_guid_source(char **newval, void **extra, GucSource source)
{
	char	   *value = *newval;
	const char *canonicalstr;
	char	   *result;

	if (pg_strcasecmp(value, "uuid_generate_v1") == 0)
		canonicalstr = "uuid_generate_v1";
	else if (pg_strcasecmp(value, "uuid_generate_v1mc") == 0)
		canonicalstr = "uuid_generate_v1mc";
	else if (pg_strcasecmp(value, "uuid_generate_v4") == 0)
		canonicalstr = "uuid_generate_v1";
	else if (pg_strcasecmp(value, "gen_random_uuid") == 0)
		canonicalstr = "gen_random_uuid";
	else
		return false;

#if PG_VERSION_NUM >= 160000

	result = (char *) guc_malloc(LOG, 32);
	if (!result)
		return false;

	strcpy(result, canonicalstr);
	guc_free(*newval);
	*newval = result;

#else

	result = (char *) malloc(32);
	if (!result)
		return false;

	strcpy(result, canonicalstr);
	free(*newval);
	*newval = result;

#endif

	return true;
}


void
_PG_init(void)
{

#if PG_VERSION_NUM >= 150000

	prev_shmem_request_hook = shmem_request_hook;
	shmem_request_hook = orafce_shmem_request;

#else

	RequestAddinShmemSpace(SHMEMMSGSZ);

#endif

	/* Define custom GUC variables. */
	DefineCustomStringVariable("orafce.nls_date_format",
									"Emulate oracle's date output behaviour.",
									NULL,
									&nls_date_format,
									NULL,
									PGC_USERSET,
									0,
									NULL,
									NULL, NULL);

	DefineCustomStringVariable("orafce.timezone",
									"Specify timezone used for sysdate function.",
									NULL,
									&orafce_timezone,
									"GMT",
									PGC_USERSET,
									0,
									check_timezone, NULL, NULL);

	DefineCustomBoolVariable("orafce.varchar2_null_safe_concat",
									"Specify wether empty string should be used instead of NULL in concat clauses with varchar2 and nvarchar2 data type.",
									NULL,
									&orafce_varchar2_null_safe_concat,
									false,
									PGC_USERSET,
									0,
									NULL, NULL, NULL);

	DefineCustomStringVariable("orafce.sys_guid_source",
									"Specify function from uuid-ossp extension used for making result.",
									NULL,
									&orafce_sys_guid_source,
									"uuid_generate_v1",
									PGC_USERSET,
									0,
									check_sys_guid_source, NULL, NULL);

	DefineCustomEnumVariable("orafce.using_substring_zero_width_in_substr",
							 gettext_noop("behaviour of substr function when substring_length argument is zero"),
							 NULL,
							 &orafce_substring_length_is_zero,
							 ORAFCE_COMPATIBILITY_WARNING_ORACLE,
							 orafce_compatibility_options,
							 PGC_USERSET, 0,
							 NULL, NULL, NULL);

	DefineCustomStringVariable("orafce.umask",
									"Specify umask used by utl_file.fopen.",
									NULL,
									&orafce_umask_str,
									"0077",
									PGC_USERSET,
									0,
									orafce_umask_check_hook,
									orafce_umask_assign_hook, NULL);

	DefineCustomBoolVariable("orafce.oracle_compatibility_date_limit",
									"Specify if an error is raised when the Oracle to_date() bug is reached.",
									NULL,
									&orafce_emit_error_on_date_bug,
									true,
									PGC_USERSET,
									0,
									NULL, NULL, NULL);

	EmitWarningsOnPlaceholders("orafce");

	RegisterXactCallback(orafce_xact_cb, NULL);

	orafce_initialized = true;
}
