/*----------------------------------------------------------------------------
 *
 *     nvarchar2.c
 *     NVARCHAR2 type for PostgreSQL.
 *
 *----------------------------------------------------------------------------
 */

#include "postgres.h"
#include "access/hash.h"
#include "libpq/pqformat.h"
#include "nodes/nodeFuncs.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "mb/pg_wchar.h"
#include "fmgr.h"

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


PG_FUNCTION_INFO_V1(nvarchar2in);
PG_FUNCTION_INFO_V1(nvarchar2out);
PG_FUNCTION_INFO_V1(nvarchar2);
PG_FUNCTION_INFO_V1(nvarchar2recv);

/*
 * nvarchar2_input -- common guts of nvarchar2in and nvarchar2recv
 *
 * s is the input text of length len (may not be null-terminated)
 * atttypmod is the typmod value to apply
 *
 * If the input string is too long, raise an error
 *
 * Uses the C string to text conversion function, which is only appropriate
 * if VarChar and text are equivalent types.
 */

static VarChar *
nvarchar2_input(const char *s, size_t len, int32 atttypmod)
{
	VarChar		*result = NULL;		/* input data */
	size_t		maxlen;

	maxlen = atttypmod - VARHDRSZ;

	/*
	 * Perform the typmod check; error out if value too long for NVARCHAR2
	 */
	if (atttypmod >= (int32) VARHDRSZ && len > maxlen)
	{
		/* Verify that input length is within typmod limit.
		 *
		 * NOTE: blankspace is not truncated
		 */
		size_t		mbmaxlen = pg_mbstrlen(s);

		if (mbmaxlen > maxlen)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
						errmsg("input value length is %zd; too long for type nvarchar2(%zd)", mbmaxlen , maxlen)));
	}

	result = (VarChar *) cstring_to_text_with_len(s, size2int(len));
	return  result;
}

/*
 * Converts a C string to NVARCHAR2 internal representation.  atttypmod
 * is the declared length of the type plus VARHDRSZ.
 */
Datum
nvarchar2in(PG_FUNCTION_ARGS)
{
	char	*s = PG_GETARG_CSTRING(0);
#ifdef NOT_USED
	Oid		typelem = PG_GETARG_OID(1);
#endif
	int32	atttypmod = PG_GETARG_INT32(2);
	VarChar	*result;

	result = nvarchar2_input(s, strlen(s), atttypmod);
	PG_RETURN_VARCHAR_P(result);
}


/*
 * converts a NVARCHAR2 value to a C string.
 *
 * Uses the text to C string conversion function, which is only appropriate
 * if VarChar and text are equivalent types.
 */
Datum
nvarchar2out(PG_FUNCTION_ARGS)
{
	Datum   txt = PG_GETARG_DATUM(0);

	PG_RETURN_CSTRING(TextDatumGetCString(txt));
}

/*
 * converts external binary format to nvarchar
 */
Datum
nvarchar2recv(PG_FUNCTION_ARGS)
{
	StringInfo	buf = (StringInfo) PG_GETARG_POINTER(0);

#ifdef NOT_USED
	Oid			typelem = PG_GETARG_OID(1);
#endif
	int32		atttypmod = PG_GETARG_INT32(2);	/* typmod of the receiving column */
	VarChar		*result;
	char		*str;							/* received data */
	int			nbytes;							/* length in bytes of recived data */

	str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
	result = nvarchar2_input(str, nbytes, atttypmod);
	pfree(str);
	PG_RETURN_VARCHAR_P(result);
}


/*
 * nvarchar2send -- convert nvarchar2 to binary value
 *
 * just use varcharsend()
 */

/*
 * nvarchar2_transform()
 * Flatten calls to varchar's length coercion function that set the new maximum
 * length >= the previous maximum length.  We can ignore the isExplicit
 * argument, since that only affects truncation cases.
 *
 * just use varchar_transform()
 */

/*
 * Converts a NVARCHAR2 type to the specified size.
 *
 * maxlen is the typmod, ie, declared length plus VARHDRSZ bytes.
 * isExplicit is true if this is for an explicit cast to nvarchar2(N).
 *
 * Truncation rules: for an explicit cast, silently truncate to the given
 * length; for an implicit cast, raise error if length limit is exceeded
 */
Datum
nvarchar2(PG_FUNCTION_ARGS)
{
	VarChar		*source = PG_GETARG_VARCHAR_PP(0);
	int32		typmod = PG_GETARG_INT32(1);
	bool		isExplicit = PG_GETARG_BOOL(2);
	int32		len,
				maxlen;
	int		maxmblen;
	char		*s_data;

	len = VARSIZE_ANY_EXHDR(source);
	s_data = VARDATA_ANY(source);
	maxlen = typmod - VARHDRSZ;

	/* No work if typmod is invalid or supplied data fits it already */
	if (maxlen < 0 || len <= maxlen)
		PG_RETURN_VARCHAR_P(source);

	/* only reach here if string is too long... */

	/* truncate multibyte string preserving multibyte boundary */
	maxmblen = pg_mbcharcliplen(s_data, len, maxlen);

	/* error out if value too long unless it's an explicit cast */
	if (!isExplicit)
	{
		/* if there is still data beyond maxmblen, error out
		 *
		 * Remember - no blankspace truncation on implicit cast
		 */
		if (len > maxmblen)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
						errmsg("input value too long for type nvarchar2(%d)", maxlen)));
	}

	PG_RETURN_VARCHAR_P((VarChar *) cstring_to_text_with_len(s_data, size2int(maxmblen)));
}


/*
 * nvarchar2typmodin -- type modifier input function
 *
 * just use varchartypmodin()
 */

/*
 * nvarchar2typmodout -- type modifier output function
 *
 * just use varchartypmodout()
 */