#include "postgres.h"
#include "fmgr.h"

#include "query_util.h"

PG_FUNCTION_INFO_V1(tsquery_numnode);
Datum           tsquery_numnode(PG_FUNCTION_ARGS);

Datum
tsquery_numnode(PG_FUNCTION_ARGS) {
	QUERYTYPE  *query = (QUERYTYPE *) DatumGetPointer(PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0)));
	int nnode = query->size;
	PG_FREE_IF_COPY(query,0);
	PG_RETURN_INT32(nnode);
}

static QTNode* 
join_tsqueries(QUERYTYPE *a, QUERYTYPE *b) {
	QTNode  *res=(QTNode*)palloc0( sizeof(QTNode) );

	res->flags |= QTN_NEEDFREE;

	res->valnode = (ITEM*)palloc0( sizeof(ITEM) );
	res->valnode->type = OPR;

	res->child = (QTNode**)palloc0( sizeof(QTNode*)*2 );
	res->child[0] = QT2QTN( GETQUERY(b), GETOPERAND(b) );
	res->child[1] = QT2QTN( GETQUERY(a), GETOPERAND(a) );
	res->nchild = 2;

	return res;
}

PG_FUNCTION_INFO_V1(tsquery_and);
Datum           tsquery_and(PG_FUNCTION_ARGS);

Datum
tsquery_and(PG_FUNCTION_ARGS) {
	QUERYTYPE  *a = (QUERYTYPE *) DatumGetPointer(PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0)));
	QUERYTYPE  *b = (QUERYTYPE *) DatumGetPointer(PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(1)));
	QTNode	*res;
	QUERYTYPE  *query;

	if ( a->size == 0 ) {
		PG_FREE_IF_COPY(a,1);
		PG_RETURN_POINTER(b);
	} else if ( b->size == 0 ) {
		PG_FREE_IF_COPY(b,1);
		PG_RETURN_POINTER(a);
	}  	

	res = join_tsqueries(a, b);

	res->valnode->val = '&';

	query = QTN2QT( res, PlainMemory );

	QTNFree(res);
	PG_FREE_IF_COPY(a,0);
	PG_FREE_IF_COPY(b,1);

	PG_RETURN_POINTER(query);
}

PG_FUNCTION_INFO_V1(tsquery_or);
Datum           tsquery_or(PG_FUNCTION_ARGS);

Datum
tsquery_or(PG_FUNCTION_ARGS) {
	QUERYTYPE  *a = (QUERYTYPE *) DatumGetPointer(PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0)));
	QUERYTYPE  *b = (QUERYTYPE *) DatumGetPointer(PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(1)));
	QTNode	*res;
	QUERYTYPE  *query;

	if ( a->size == 0 ) {
		PG_FREE_IF_COPY(a,1);
		PG_RETURN_POINTER(b);
	} else if ( b->size == 0 ) {
		PG_FREE_IF_COPY(b,1);
		PG_RETURN_POINTER(a);
	}  	

	res = join_tsqueries(a, b);

	res->valnode->val = '|';

	query = QTN2QT( res, PlainMemory );

	QTNFree(res);
	PG_FREE_IF_COPY(a,0);
	PG_FREE_IF_COPY(b,1);

	PG_RETURN_POINTER(query);
}

PG_FUNCTION_INFO_V1(tsquery_not);
Datum           tsquery_not(PG_FUNCTION_ARGS);

Datum
tsquery_not(PG_FUNCTION_ARGS) {
	QUERYTYPE  *a = (QUERYTYPE *) DatumGetPointer(PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0)));
	QTNode	*res;
	QUERYTYPE  *query;

	if ( a->size == 0 ) 
		PG_RETURN_POINTER(a);

	res=(QTNode*)palloc0( sizeof(QTNode) );

	res->flags |= QTN_NEEDFREE;

	res->valnode = (ITEM*)palloc0( sizeof(ITEM) );
	res->valnode->type = OPR;
	res->valnode->val = '!';

	res->child = (QTNode**)palloc0( sizeof(QTNode*) );
	res->child[0] = QT2QTN( GETQUERY(a), GETOPERAND(a) );
	res->nchild = 1;

	query = QTN2QT( res, PlainMemory );

	QTNFree(res);
	PG_FREE_IF_COPY(a,0);

	PG_RETURN_POINTER(query);
}

static int
CompareTSQ( QUERYTYPE *a, QUERYTYPE *b ) {
	if ( a->size != b->size ) {
		return ( a->size < b->size ) ? -1 : 1;
	} else if ( a->len != b->len ) {
		return ( a->len < b->len ) ? -1 : 1;
	} else {
		QTNode	*an = QT2QTN( GETQUERY(a), GETOPERAND(a) ); 
		QTNode	*bn = QT2QTN( GETQUERY(b), GETOPERAND(b) );
		int res = QTNodeCompare(an, bn);

		QTNFree(an);
		QTNFree(bn);

		return res; 
	}

	return 0;
}

PG_FUNCTION_INFO_V1(tsquery_cmp);                                                                              \
Datum   tsquery_cmp(PG_FUNCTION_ARGS);         

Datum
tsquery_cmp(PG_FUNCTION_ARGS) {
	QUERYTYPE  *a = (QUERYTYPE *) DatumGetPointer(PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0)));
	QUERYTYPE  *b = (QUERYTYPE *) DatumGetPointer(PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(1)));
	int res = CompareTSQ(a,b);

	PG_FREE_IF_COPY(a,0);
	PG_FREE_IF_COPY(b,1);

	PG_RETURN_INT32(res);
}

#define CMPFUNC( NAME, ACTION ) 									\
PG_FUNCTION_INFO_V1(NAME);										\
Datum	NAME(PG_FUNCTION_ARGS);										\
													\
Datum													\
NAME(PG_FUNCTION_ARGS) { 										\
	QUERYTYPE  *a = (QUERYTYPE *) DatumGetPointer(PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0)));	\
	QUERYTYPE  *b = (QUERYTYPE *) DatumGetPointer(PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(1)));	\
	int res = CompareTSQ(a,b);									\
													\
	PG_FREE_IF_COPY(a,0);										\
	PG_FREE_IF_COPY(b,1);										\
													\
	PG_RETURN_BOOL( ACTION );									\
}

CMPFUNC( tsquery_lt, res <0 ); 
CMPFUNC( tsquery_le, res<=0 ); 
CMPFUNC( tsquery_eq, res==0 ); 
CMPFUNC( tsquery_ge, res>=0 ); 
CMPFUNC( tsquery_gt, res >0 );
CMPFUNC( tsquery_ne, res!=0 );



