/*
 * Operations for tsvector type
 * Teodor Sigaev <teodor@sigaev.ru>
 */
#include "postgres.h"

#include "access/gist.h"
#include "access/itup.h"
#include "utils/builtins.h"
#include "storage/bufpage.h"
#include "executor/spi.h"
#include "commands/trigger.h"
#include "nodes/pg_list.h"
#include "catalog/namespace.h"

#include "utils/pg_locale.h"

#include <ctype.h>				/* tolower */
#include "tsvector.h"
#include "query.h"
#include "ts_cfg.h"
#include "common.h"

PG_FUNCTION_INFO_V1(strip);
Datum		strip(PG_FUNCTION_ARGS);

PG_FUNCTION_INFO_V1(setweight);
Datum		setweight(PG_FUNCTION_ARGS);

PG_FUNCTION_INFO_V1(concat);
Datum		concat(PG_FUNCTION_ARGS);

Datum
strip(PG_FUNCTION_ARGS)
{
	tsvector	   *in = (tsvector *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
	tsvector	*out;
	int i,len=0;
	WordEntry *arrin=ARRPTR(in), *arrout;
	char *cur;

	for(i=0;i<in->size;i++) 
		len += SHORTALIGN( arrin[i].len );

	len = CALCDATASIZE(in->size, len);
	out=(tsvector*)palloc(len);
	memset(out,0,len);
	out->len=len;
	out->size=in->size;
	arrout=ARRPTR(out);
	cur=STRPTR(out);
	for(i=0;i<in->size;i++) {
		memcpy(cur, STRPTR(in)+arrin[i].pos, arrin[i].len);
		arrout[i].haspos = 0;
		arrout[i].len = arrin[i].len;
		arrout[i].pos = cur - STRPTR(out);
		cur += SHORTALIGN( arrout[i].len );
	}
		
	PG_FREE_IF_COPY(in, 0);
	PG_RETURN_POINTER(out);
}

Datum
setweight(PG_FUNCTION_ARGS)
{
	tsvector	   *in = (tsvector *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
	char	   cw = PG_GETARG_CHAR(1);
	tsvector	*out;
	int i,j;
	WordEntry *entry;
	WordEntryPos *p;
	int w=0;

	switch(tolower(cw)) {
		case 'a': w=3; break;
		case 'b': w=2; break;
		case 'c': w=1; break;
		case 'd': w=0; break;
		/* internal error */
		default: elog(ERROR,"unrecognized weight");
	}

	out=(tsvector*)palloc(in->len);
	memcpy(out,in,in->len);
	entry=ARRPTR(out);
	i=out->size;	
	while(i--) {
		if ( (j=POSDATALEN(out,entry)) != 0 ) {
			p=POSDATAPTR(out,entry);
			while(j--) {
				p->weight=w;
				p++;
			}
		}
		entry++;
	}
		
	PG_FREE_IF_COPY(in, 0);
	PG_RETURN_POINTER(out);
}

static int
compareEntry(char *ptra, WordEntry* a, char *ptrb, WordEntry* b)
{
        if ( a->len == b->len)
        {
                return strncmp(
                                           ptra + a->pos,
                                           ptrb + b->pos,
                                           a->len);
        }
        return ( a->len > b->len ) ? 1 : -1;
}

static int4
add_pos(tsvector *src, WordEntry *srcptr, tsvector *dest, WordEntry *destptr, int4 maxpos ) {
	uint16 *clen = (uint16*)_POSDATAPTR(dest,destptr);
	int i;
	uint16 slen = POSDATALEN(src, srcptr), startlen;
	WordEntryPos *spos=POSDATAPTR(src, srcptr), *dpos=POSDATAPTR(dest,destptr);

	if ( ! destptr->haspos ) 
		*clen=0;

	startlen = *clen;
	for(i=0; i<slen && *clen<MAXNUMPOS && ( *clen==0 || dpos[ *clen-1 ].pos != MAXENTRYPOS-1 ) ;i++) {
		dpos[ *clen ].weight = spos[i].weight; 
		dpos[ *clen ].pos    = LIMITPOS(spos[i].pos + maxpos);
		(*clen)++;
	}

	if ( *clen != startlen )
		destptr->haspos=1; 
	return  *clen - startlen;
}


Datum
concat(PG_FUNCTION_ARGS) {
	tsvector	   *in1 = (tsvector *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
	tsvector	   *in2 = (tsvector *) PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
	tsvector	   *out;
	WordEntry *ptr;
	WordEntry *ptr1,*ptr2;
	WordEntryPos *p;
	int	maxpos=0,i,j,i1,i2;
	char *cur;
	char *data,*data1,*data2;

	ptr=ARRPTR(in1);
	i=in1->size;
	while(i--) {
		if ( (j=POSDATALEN(in1,ptr)) != 0 ) {
			p=POSDATAPTR(in1,ptr);
			while(j--) {
				if ( p->pos > maxpos ) 
					maxpos = p->pos;
				p++;
			}
		}
		ptr++;
	}
	
	ptr1=ARRPTR(in1); ptr2=ARRPTR(in2);
	data1=STRPTR(in1); data2=STRPTR(in2);
	i1=in1->size;   i2=in2->size;
	out=(tsvector*)palloc( in1->len + in2->len );
	memset(out,0,in1->len + in2->len);
	out->len = in1->len + in2->len;
	out->size = in1->size + in2->size;
	data=cur=STRPTR(out);
	ptr=ARRPTR(out);
	while( i1 && i2 ) {
		int cmp=compareEntry(data1,ptr1,data2,ptr2);
		if ( cmp < 0 ) { /* in1 first */
			ptr->haspos = ptr1->haspos;
			ptr->len = ptr1->len;
			memcpy( cur, data1 + ptr1->pos, ptr1->len );
			ptr->pos = cur - data; 
			cur+=SHORTALIGN(ptr1->len);
			if ( ptr->haspos ) {
				memcpy(cur, _POSDATAPTR(in1, ptr1), POSDATALEN(in1, ptr1)*sizeof(WordEntryPos) + sizeof(uint16));
				cur+=POSDATALEN(in1, ptr1)*sizeof(WordEntryPos) + sizeof(uint16);
			}
			ptr++; ptr1++; i1--;
		} else if ( cmp>0 ) { /* in2 first */ 
			ptr->haspos = ptr2->haspos;
			ptr->len = ptr2->len;
			memcpy( cur, data2 + ptr2->pos, ptr2->len );
			ptr->pos = cur - data; 
			cur+=SHORTALIGN(ptr2->len);
			if ( ptr->haspos ) {
				int addlen = add_pos(in2, ptr2, out, ptr, maxpos );
				if ( addlen == 0 )
					ptr->haspos=0;
				else
					cur += addlen*sizeof(WordEntryPos) + sizeof(uint16); 
			}
			ptr++; ptr2++; i2--;
		} else {
			ptr->haspos = ptr1->haspos | ptr2->haspos;
			ptr->len = ptr1->len;
			memcpy( cur, data1 + ptr1->pos, ptr1->len );
			ptr->pos = cur - data; 
			cur+=SHORTALIGN(ptr1->len);
			if ( ptr->haspos ) {
				if ( ptr1->haspos ) {
					memcpy(cur, _POSDATAPTR(in1, ptr1), POSDATALEN(in1, ptr1)*sizeof(WordEntryPos) + sizeof(uint16));
					cur+=POSDATALEN(in1, ptr1)*sizeof(WordEntryPos) + sizeof(uint16);
					if ( ptr2->haspos )
						cur += add_pos(in2, ptr2, out, ptr, maxpos )*sizeof(WordEntryPos);
				} else if ( ptr2->haspos ) {
					int addlen = add_pos(in2, ptr2, out, ptr, maxpos );
					if ( addlen == 0 )
						ptr->haspos=0;
					else
						cur += addlen*sizeof(WordEntryPos) + sizeof(uint16); 
				}
			}
			ptr++; ptr1++; ptr2++; i1--; i2--;
		}
	}

	while(i1) {
		ptr->haspos = ptr1->haspos;
		ptr->len = ptr1->len;
		memcpy( cur, data1 + ptr1->pos, ptr1->len );
		ptr->pos = cur - data; 
		cur+=SHORTALIGN(ptr1->len);
		if ( ptr->haspos ) {
			memcpy(cur, _POSDATAPTR(in1, ptr1), POSDATALEN(in1, ptr1)*sizeof(WordEntryPos) + sizeof(uint16));
			cur+=POSDATALEN(in1, ptr1)*sizeof(WordEntryPos) + sizeof(uint16);
		}
		ptr++; ptr1++; i1--;
	}

	while(i2) {
		ptr->haspos = ptr2->haspos;
		ptr->len = ptr2->len;
		memcpy( cur, data2 + ptr2->pos, ptr2->len );
		ptr->pos = cur - data; 
		cur+=SHORTALIGN(ptr2->len);
		if ( ptr->haspos ) {
			int addlen = add_pos(in2, ptr2, out, ptr, maxpos );
			if ( addlen == 0 )
				ptr->haspos=0;
			else
				cur += addlen*sizeof(WordEntryPos) + sizeof(uint16); 
		}
		ptr++; ptr2++; i2--;
	}
	
	out->size=ptr-ARRPTR(out);
	out->len = CALCDATASIZE( out->size, cur-data );
	if ( data != STRPTR(out) )
		memmove( STRPTR(out), data, cur-data );

	PG_FREE_IF_COPY(in1, 0);
	PG_FREE_IF_COPY(in2, 1);
	PG_RETURN_POINTER(out);
}

