#include "postgres.h"

#include <float.h>
#include <math.h>

#include "kokes.h"
#include "access/gist.h"
#include "access/skey.h"
#include "lib/stringinfo.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "libpq/pqformat.h"

PG_MODULE_MAGIC;

PG_FUNCTION_INFO_V1(kokes_in);
PG_FUNCTION_INFO_V1(kokes_out);
PG_FUNCTION_INFO_V1(kokes_send);
PG_FUNCTION_INFO_V1(kokes_recv);

Datum kokes_in(PG_FUNCTION_ARGS);
Datum kokes_out(PG_FUNCTION_ARGS);
Datum kokes_send(PG_FUNCTION_ARGS);
Datum kokes_recv(PG_FUNCTION_ARGS);

extern int	kokes_yyparse();
extern void kokes_scanner_init(const char *str);
extern void kokes_scanner_finish(void);
static void print_double_list(StringInfo str, double *data, unsigned long cntpnt);
static void print_short_list(StringInfo str, short int *data, unsigned long cntpnt);
static char *print_info(StringInfo str, char *buf);
static char *print_infos(StringInfo str, char *buf);
static char *print_atrs(StringInfo str, char *buf);

KokesData *kokes;

#define PORTABLE_SERIALIZATION

Datum
kokes_in(PG_FUNCTION_ARGS)
{
	char	*str;

	str = PG_GETARG_CSTRING(0);

	kokes_scanner_init(str);
	if (kokes_yyparse() != 0)
		ereport(ERROR, 
				(errcode(ERRCODE_SYNTAX_ERROR),
				 errmsg("bogus input")));

elog(NOTICE, "KOKES SIZE %d, pointer: %d, LEN: %d", VARSIZE( kokes), kokes, kokes->vl_len_);

	kokes_scanner_finish();

	PG_RETURN_KokesData(kokes);
}

Datum
kokes_out(PG_FUNCTION_ARGS)
{
    Kokes kokes;
    StringInfoData buf;
    char 	*readbuf;

    initStringInfo(&buf);

    kokes = PG_GETARG_KokesData(0);

elog(NOTICE, "1");

    appendStringInfo(&buf, "((%u ", kokes->status);
    
elog(NOTICE, "2");
    
    appendStringInfo(&buf, "(%ld %ld %ld %ld) ", kokes->OOO[0], kokes->OOO[1], 
						kokes->OOO[2], kokes->OOO[3]);
    appendStringInfo(&buf, "%lu %lu %d %d)", kokes->key, kokes->cntpnt,
						kokes->layer, kokes->alias);

elog(NOTICE, ">>> %s", buf.data);

    print_double_list(&buf, KOKES_X_ARRAY(kokes), kokes->cntpnt);
    print_double_list(&buf, KOKES_Y_ARRAY(kokes), kokes->cntpnt);
    print_short_list(&buf, KOKES_SP_ARRAY(kokes), kokes->cntpnt);

    readbuf = print_infos(&buf, KOKES_INF_ARRAY(kokes));
    print_atrs(&buf, readbuf);

elog(NOTICE, "3");

    appendStringInfoChar(&buf, ')');

    PG_FREE_IF_COPY(kokes, 0);
    PG_RETURN_CSTRING(buf.data);
}

static void 
print_double_list(StringInfo str, double *data, unsigned long cntpnt)
{
    int i;

    appendStringInfo(str, " (");
    
    for (i = 0; i < cntpnt; i++)
	    if (i > 0) 
		    appendStringInfo(str, " %g", data[i]);
	    else
		    appendStringInfo(str, "%g", data[i]);

    appendStringInfoChar(str, ')');
}

static void 
print_short_list(StringInfo str, short int *data, unsigned long cntpnt)
{
    int i;

    appendStringInfo(str, " (");
    
    for (i = 0; i < cntpnt; i++)
	    if (i > 0) 
		    appendStringInfo(str, " %d", data[i]);
	    else
		    appendStringInfo(str, "%d", data[i]);

    appendStringInfoChar(str, ')');
}



static char *
print_info(StringInfo str, char *buf)
{
	GINFLEN ginflen = *((GINFLEN *) buf);
	int processed_chars = 0;

	buf += sizeof(GINFLEN);
	appendStringInfoChar(str, '(');

	/* pokud ma bod informace zobraz je */
	if (*buf != '\0')
	{
		while (processed_chars < ginflen)
		{
			if (processed_chars != 0)
				appendStringInfoChar(str, ' ');
			appendStringInfo(str, "%c='", *buf++);
			processed_chars += 1;

			while (*buf != '\t' && *buf != '\0')
			{
				appendStringInfoChar(str, *buf++);
				processed_chars += 1;
			}

			appendStringInfoChar(str, '\'');

			if (*buf++ == '\0')
				break;

			processed_chars += 1;
		}
	}

	appendStringInfoChar(str, ')');
	return buf;
}

static char *
print_infos(StringInfo str, char *buf)
{
	GATRLEN gatrlen = *((GATRLEN *) buf);
	char	*endbuf;

	buf += sizeof(GATRLEN);
	endbuf = buf + gatrlen;
	appendStringInfo(str, " (");

	while (buf < endbuf)
	{
		buf = print_info(str, buf);
	}

	appendStringInfoChar(str, ')');
	return buf;
}

static char *
print_atrs(StringInfo str, char *buf)
{
	GATRLEN gatrlen = *((GATRLEN *) buf);
	int	processed_chars = 0;

	buf += sizeof(GATRLEN);
	appendStringInfo(str, " (");
	while (processed_chars++ < gatrlen)
	{
		char	c = *buf++;

		switch (c)
		{
			case '\0':
				appendStringInfoChar(str, '\'');
			        break;
			case '=':
				appendStringInfo(str, "='");
				break;
			case '\n':
				appendStringInfo(str, "' ");
				break;
			default:
				appendStringInfoChar(str, c);
		}
	}	

	appendStringInfoChar(str, ')');
	return buf;
}


/*
 * Serializace je jednoducha - prenese se pole bytu.
 * Tato serializace je funkci pouze pri shode uladani poradi bajtu!
 * tj. je nezbytne aby server bezel na stejne platforme (z pohledu
 * poradi ukladni bajtu) jako klient - pokud by tato podminka neplatila
 * musel by se polozky struktury serializovat samostatne!!
 * coz by nebyl az takovy problem a patrne by to nebylo nijak zvlast
 * poznat na vykonu a bylo by to prenositelne, tj. do dalsi verze
 * pridat variantu
 */
 
#define FILLINT(kokes, buf, field)	kokes->field = pq_getmsgint(buf, sizeof(kokes->field)) 
#define PUTINT(kokes, buf, field)	pq_sendint(&buf, kokes->field, sizeof(kokes->field))
 
Datum
kokes_recv(PG_FUNCTION_ARGS)
{
	StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
	Kokes		kokes;
#ifdef PORTABLE_SERIALIZATION
	unsigned long cntpnt;
	int	i;
	size_t size;
	double 	*aux_dp;
	short int	*aux_sp;
#endif

#ifndef PORTABLE_SERIALIZATION
	kokes = palloc(buf->len + VARHDRSZ);
	pq_copymsgbytes(buf, VARDATA(kokes), buf->len);
	SET_VARSIZE(kokes, buf->len + VARHDRSZ);
#else
	cntpnt = pq_getmsgint(buf, sizeof(unsigned long));		
	size = offsetof(KokesData, data[0])  + (2 * sizeof(double)*cntpnt) 
				+ sizeof(short int)*cntpnt;
	kokes = (Kokes) palloc(size);
	kokes->cntpnt = cntpnt;
	FILLINT(kokes, buf, status);
	FILLINT(kokes, buf, key);
	FILLINT(kokes, buf, layer);
	FILLINT(kokes, buf, alias);
	FILLINT(kokes, buf, reserva);
	
	FILLINT(kokes, buf, OOO[0]);
	FILLINT(kokes, buf, OOO[1]);
	FILLINT(kokes, buf, OOO[2]);
	FILLINT(kokes, buf, OOO[3]);

	aux_dp = KOKES_X_ARRAY(kokes);
	for (i = 0; i < kokes->cntpnt; i++)
		*aux_dp++ = pq_getmsgfloat8(buf);
		
	aux_dp = KOKES_Y_ARRAY(kokes);
	for (i = 0; i < kokes->cntpnt; i++)
		*aux_dp++ = pq_getmsgfloat8(buf);
		
	aux_sp = KOKES_SP_ARRAY(kokes);
	for (i = 0; i < kokes->cntpnt; i++)
		*aux_sp++ = pq_getmsgint(buf, sizeof(short int));
#endif

	PG_RETURN_KokesData(kokes);
}

Datum
kokes_send(PG_FUNCTION_ARGS)
{
	Kokes kokes = PG_GETARG_KokesData(0);
	StringInfoData		buf;
#ifdef PORTABLE_SERIALIZATION
	int	i;
	double 	*aux_dp;
	short int	*aux_sp;
#endif

	pq_begintypsend(&buf);
#ifndef PORTABLE_SERIALIZATION
	pq_sendbytes(&buf, VARDATA(kokes), 
				VARSIZE(kokes) - VARHDRSZ);
#else
	PUTINT(kokes, buf, cntpnt);
	PUTINT(kokes, buf, status);
	PUTINT(kokes, buf, key);
	PUTINT(kokes, buf, layer);
	PUTINT(kokes, buf, alias);
	PUTINT(kokes, buf, reserva);
	
	PUTINT(kokes, buf, OOO[0]);
	PUTINT(kokes, buf, OOO[1]);
	PUTINT(kokes, buf, OOO[2]);
	PUTINT(kokes, buf, OOO[3]);

	aux_dp = KOKES_X_ARRAY(kokes);
	for (i = 0; i < kokes->cntpnt; i++)
		pq_sendfloat8(&buf, *aux_dp++);
		
	aux_dp = KOKES_Y_ARRAY(kokes);
	for (i = 0; i < kokes->cntpnt; i++)
		pq_sendfloat8(&buf, *aux_dp++);
		
	aux_sp = KOKES_SP_ARRAY(kokes);
	for (i = 0; i < kokes->cntpnt; i++)
		pq_sendint(&buf, *aux_sp++, sizeof(short int));
#endif
	
	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}

