#include "postgres.h"
#include "executor/spi.h"
#include "query_util.h"

QTNode* 
QT2QTN( ITEM *in, char *operand ) {
	QTNode	*node = (QTNode*)palloc0( sizeof(QTNode) );

	node->valnode = in;
	
	if (in->type == OPR) {
		node->child = (QTNode**)palloc0( sizeof(QTNode*) * 2 );
		node->child[0] = QT2QTN( in + 1, operand );
		node->sign = node->child[0]->sign;
		if (in->val == (int4) '!')
			node->nchild = 1;
		else {
			node->nchild = 2;
			node->child[1] = QT2QTN( in + in->left, operand );
			node->sign |= node->child[1]->sign;
		}
	} else if ( operand ) {
		node->word = operand + in->distance;
		node->sign = 1 << ( in->val % 32 );
	}
	
	return node; 
}

void 
QTNFree( QTNode* in ) {
	if ( !in )
		return;

	if ( in->valnode->type == VAL && in->word && (in->flags & QTN_WORDFREE) !=0  )
		pfree( in->word );

	if ( in->child ) {
		if ( in->valnode ) {
			if ( in->valnode->type == OPR && in->nchild > 0 ) { 
				int i;
				for (i=0;i<in->nchild;i++)
					QTNFree( in->child[i] );
			}
			if ( in->flags & QTN_NEEDFREE )
				pfree( in->valnode );
		}
		pfree( in->child );
	}

	pfree( in );
}

int
QTNodeCompare( QTNode  *an, QTNode  *bn ) {
	if ( an->valnode->type != bn->valnode->type ) 
		return ( an->valnode->type > bn->valnode->type ) ? -1 : 1;
	else if ( an->valnode->val != bn->valnode->val )
		return ( an->valnode->val > bn->valnode->val ) ? -1 : 1;
	else if ( an->valnode->type == VAL ) {
		if ( an->valnode->length == bn->valnode->length )
			return strncmp( an->word, bn->word, an->valnode->length );
		else 
			return ( an->valnode->length > bn->valnode->length ) ? -1 : 1;
	} else if ( an->nchild != bn->nchild ) {
		return ( an->nchild > bn->nchild ) ? -1 : 1;
	} else {
		int i,res;
		
		for( i=0; i<an->nchild; i++ ) 
			if ( (res=QTNodeCompare(an->child[i], bn->child[i]))!=0 )
				return res;
	} 
	
	return 0;
}

static int
cmpQTN( const void *a, const void *b ) {
	return QTNodeCompare( *(QTNode**)a, *(QTNode**)b );
}
 
void 
QTNSort( QTNode* in ) {
	int i;

	if ( in->valnode->type != OPR )
		return;
 
	for (i=0;i<in->nchild;i++)
		QTNSort( in->child[i] );
	if ( in->nchild > 1 )
		qsort((void *) in->child, in->nchild, sizeof(QTNode*), cmpQTN); 
}

bool 
QTNEq( QTNode* a, QTNode* b ) {
	uint32	sign = a->sign & b->sign;
	if ( !(sign == a->sign && sign == b->sign) )
		return 0;

	return ( QTNodeCompare(a,b) == 0 ) ? true : false;
}

void 
QTNTernary( QTNode* in ) {
	int i;

	if ( in->valnode->type != OPR )
		return;

	for (i=0;i<in->nchild;i++)
		QTNTernary( in->child[i] );

	for (i=0;i<in->nchild;i++) {
		if ( in->valnode->type == in->child[i]->valnode->type && in->valnode->val == in->child[i]->valnode->val ) {
			QTNode*	cc = in->child[i];
			int oldnchild = in->nchild; 

			in->nchild += cc->nchild-1;
			in->child = (QTNode**)repalloc( in->child, in->nchild * sizeof(QTNode*) );
			
			if ( i+1 != oldnchild ) 
				memmove( in->child + i + cc->nchild, in->child + i + 1, 
					(oldnchild-i-1)*sizeof(QTNode*) );

			memcpy( in->child + i, cc->child, cc->nchild * sizeof(QTNode*) ); 
			i += cc->nchild-1;

			pfree(cc);
		} 
	}
}

void 
QTNBinary( QTNode* in ) {
	int i;

	if ( in->valnode->type != OPR )
		return;

	for (i=0;i<in->nchild;i++)
		QTNBinary( in->child[i] );

	if ( in->nchild <= 2 )
		return; 

	while( in->nchild > 2 ) {
		QTNode *nn = (QTNode*)palloc0( sizeof(QTNode) );
		nn->valnode = (ITEM*)palloc0( sizeof(ITEM) );
		nn->child = (QTNode**)palloc0( sizeof(QTNode*) * 2 );

		nn->nchild = 2;
		nn->flags = QTN_NEEDFREE;

		nn->child[0] = in->child[0];
		nn->child[1] = in->child[1];
		nn->sign = nn->child[0]->sign | nn->child[1]->sign;

		nn->valnode->type = in->valnode->type;
		nn->valnode->val = in->valnode->val;

		in->child[0] = nn;
		in->child[1] = in->child[ in->nchild-1 ];
		in->nchild--;
	}	
}

static void
cntsize(QTNode *in, int4 *sumlen, int4 *nnode) {
	*nnode += 1;
	if ( in->valnode->type == OPR ) {
		int i;
		for (i=0;i<in->nchild;i++)
			cntsize(in->child[i], sumlen, nnode);
	} else {
		*sumlen += in->valnode->length+1;
	}
}

typedef struct {
	ITEM	*curitem;
	char	*operand;
	char	*curoperand;
} QTN2QTState;

static void
fillQT( QTN2QTState *state, QTNode *in ) {
	*(state->curitem) = *(in->valnode);
 
	if ( in->valnode->type == VAL ) {
		memcpy( state->curoperand, in->word, in->valnode->length );
		state->curitem->distance = state->curoperand - state->operand;
		state->curoperand[ in->valnode->length ] = '\0';
		state->curoperand += in->valnode->length + 1; 
		state->curitem++;
	} else {
		ITEM    *curitem = state->curitem;

		Assert( in->nchild<=2 );
		state->curitem++;

		fillQT( state, in->child[0] );

		if ( in->nchild==2 ) {
			curitem->left = state->curitem - curitem;
			fillQT( state, in->child[1] );
		} 
	}
} 

QUERYTYPE*      
QTN2QT( QTNode* in, MemoryType memtype  ) {
	QUERYTYPE	*out;
	int	len;
	int sumlen=0, nnode=0;
	QTN2QTState	state;	

	cntsize(in, &sumlen, &nnode);
	len = COMPUTESIZE( nnode, sumlen );

	out = (QUERYTYPE*)MEMALLOC(memtype, len);
	out->len = len;	
	out->size = nnode;	

	state.curitem = GETQUERY( out );	
	state.operand = state.curoperand  = GETOPERAND( out );

	fillQT( &state, in );
	return out;	
}

QTNode *
QTNCopy( QTNode* in, MemoryType memtype ) {
	QTNode	*out = (QTNode*)MEMALLOC( memtype, sizeof(QTNode) );

	*out = *in;
	out->valnode = (ITEM*)MEMALLOC( memtype, sizeof(ITEM) );
	*(out->valnode) = *(in->valnode);
	out->flags |= QTN_NEEDFREE;
	
	if ( in->valnode->type == VAL ) {
		out->word = MEMALLOC( memtype, in->valnode->length + 1 );
		memcpy( out->word, in->word, in->valnode->length );
		out->word[ in->valnode->length ] = '\0'; 
		out->flags |= QTN_WORDFREE;
	} else {
		int i;

		out->child = (QTNode**)MEMALLOC( memtype, sizeof(QTNode*) * in->nchild );

		for(i=0;i<in->nchild;i++)
			out->child[i] = QTNCopy( in->child[i], memtype );
	} 

	return out;
}
