%{

#include "postgres.h"

#include "kokes.h"
#include <stdlib.h>
#include <limits.h>
#include <errno.h>

extern int kokes_yylex(void);
extern char *kokes_yytext;
static char *scanbuf;
static int	scanbuflen;

void kokes_yyerror(const char *message);
int kokes_yyparse();

extern KokesData *kokes;

static void read_double_list(double *data, unsigned long cntpnt);
static void read_short_list(short int *data, unsigned long cntpnt);

static long to_long(const char *str);
static unsigned long to_ulong(const char *str);
static unsigned short to_ushort(const char *str);
static short int to_sint(const char *str);
static double to_double(const char *str);
static char *normalize_attr(char *str);


#define INFO_RESERVED_SIZE	1024

%}

/* BISON Declarations */
%name-prefix="kokes_yy"

%union
{
	long			lval;
	unsigned long		ulval;
	unsigned short		ushort;
	int			ival;
	short int		sival;
	float			fval;
	char		*str;
	KokesData	*kokes;
	_atr			*atr;
	_inf			*inf;
	char		c;
	struct
	{
		_inf	**inf;
		int	idx;
		int	fields;
	}		       inf_array;
}

%type <kokes>	rec 
%type <kokes>	kokes_data
%type <lval>	lval
%type <ulval>	ulong
%type <ushort>	ushort
%type <inf>	info info_list info_pt
%type <atr>	atribut atribut_list atributs
%type <c>	iname
%type <str>	kname
%type <str>	qstr
%type <inf_array>	infos


%token KO_FLOAT
%token KO_INTEGER
%token KO_SCONST
%token KO_ASCCHR
%token KO_IDENT

%start rec

/* Grammar follows */
%%

rec:	'(' '(' ushort '(' lval lval lval lval ')' kokes_data  '(' infos ')'  atributs ')'
	    {
		    unsigned int	inf_atr_size;
		    Kokes		kdp = $10;
		    size_t		size;
		    /* 
		     * Klasicke cteni seznamu, ktere se typicky pouziva v yaccu neni pouzito,
		     * z duvodu efektivnejsi alokace pameti - dopredu znam pocet prvku,
		     * a mohu alokovat dopredu a nacitat v cyklu
		     */

		    /*
		     * v teto verzi neresim zapis x1,y1,x2,y2 a plneni polozek 
		     * layer, alias - nejsou specifikovany ve formatu!
		     */
		
		    kdp->status = $3;
		    kdp->OOO[0] = $5;
		    kdp->OOO[1] = $6;
		    kdp->OOO[2] = $7;
		    kdp->OOO[3] = $8;
		    kokes = kdp;

		    /*
		     * zjisti skutecne potrebnou pamet, pro atributy
		     * a pokud je nedostacujici, realokuje pamet.
		     */
		    inf_atr_size = alloclen_info_atr($12.inf, $14, kdp->cntpnt);
		    size = offsetof(KokesData, data[0])  + (2 * sizeof(double)*kdp->cntpnt) 
				+ sizeof(short int)*kdp->cntpnt + inf_atr_size;
		    
		    if (inf_atr_size > INFO_RESERVED_SIZE)
		    {
			    size = offsetof(KokesData, data[0])  + (2 * sizeof(double)*kdp->cntpnt) 
				+ sizeof(short int)*kdp->cntpnt + inf_atr_size;
			    kdp = repalloc(kdp, size);
		    }
		    
		    /* novy prostor je pripraven pro zapis, zapis a aktualizuj delku */
		    copy_and_add_info($12.inf, $14, kdp);
		    SET_VARSIZE(kdp, size);


		    // ToDo odstranit globalni promennou!
		    $$ = kdp;
	    }
	;	

kokes_data: ulong ulong ushort ushort ')' 
	    {
		    unsigned long cntpnt = $2;
		    Kokes rec;
		    size_t 		size;

		    /*
		     * V tuto chvili jeste neni mozne dohledat velikost prostoru
		     * nezbytneho pro ulozeni informaci a atributu, resim to tedy
		     * prealokaci - pokud pozdeji zjistim, ze bylo alokovano
		     * prilis malo pameti, provedu repalloc.
		     */
		    size = offsetof(KokesData, data[0])  + (2 * sizeof(double)*cntpnt) 
				+ sizeof(short int)*cntpnt;

		    rec = (Kokes) palloc(size + INFO_RESERVED_SIZE);

		    SET_VARSIZE(rec, size);
		    rec->key = $1;
		    rec->layer = $3;
		    rec->alias = $4;
		    rec->reserva = 0;
		    rec->cntpnt = cntpnt;

		    read_double_list(KOKES_X_ARRAY(rec), cntpnt);
		    read_double_list(KOKES_Y_ARRAY(rec), cntpnt);
		    read_short_list(KOKES_SP_ARRAY(rec), cntpnt); 
		    
		    $$ = rec;
	    }
	;
	
	
infos:
	info_pt
	    {
	    	    $$.fields = 1024;
	    	    $$.idx = 0;
	    	    $$.inf = palloc(sizeof(_inf *) * $$.fields);
	    	    $$.inf[$$.idx++] = $1;
	    }
	| infos info_pt
	    {
	    	    if ($$.idx == $$.fields)
	    	    {
	    	    	    $$.fields = $$.fields * 2;
	    	    	    $$.inf = repalloc($$.inf, $$.fields * sizeof(_inf *));
	    	    }
	    	    $$.inf[$$.idx++] = $2;
	    }
	;
	
info_pt:
	'(' ')'
	    {
	    	    $$ = NULL;
	    }
	| '(' info_list ')'
	    {
	    	    $$ = $2;
	    }
	;

info_list:
	info
	    {
	    	    $$ = $1;
	    }
	| info_list info
	    {
	    	    _inf *inf = $1;
	    	    
	    	    while (inf->next != NULL)
	    	    	    inf = inf->next;
	    	    inf->next = $2;
	    	    $$ = $1;
	    }
	;



info:	iname '=' qstr
	    {
		    _inf *inf = palloc(sizeof(_inf));
		    inf->name = $1;
		    inf->value = $3;
		    inf->next = NULL;
		    $$ = inf;
	    }
	;
	
atributs: '(' ')'
	    {
		    $$ = NULL;
	    }
	| '(' atribut_list ')'
	    {
	    	    $$ = $2;
	    }
	;   
	
atribut_list: 
	atribut
	    {
	    	    $$ = $1;
	    }
	 | atribut_list atribut
	    {
	    	    _atr *atr = $1;
	    	    
	    	    while (atr->next != NULL)
	    	    	    atr = atr->next;
	    	    atr->next = $2;
	    	    $$ = $1;
	    }
	;
	
atribut: kname '=' qstr
	    {
	    	    _atr *atr = palloc(sizeof(_atr));
	    	    atr->name = $1;
	    	    atr->value = normalize_attr($3);
	    	    atr->next = NULL;
	    	    $$ = atr;
	    }
	;

lval:	KO_INTEGER
	    {
		    $$ = to_long(yylval.str);
	    }
	;

ulong:	KO_INTEGER
	    {
		    $$ = to_ulong(yylval.str);	
	    }
	;

ushort: KO_INTEGER
	    {
		    $$ = to_ushort(kokes_yylval.str);
	    }
	;

qstr:	KO_SCONST
	    {
	    	    $$ = kokes_yylval.str;
	    }
	;

kname:	KO_IDENT
	    {
	    	    $$ = pstrdup(kokes_yylval.str);
	    }
	| KO_ASCCHR
	    {
	    	    $$ = pstrdup(kokes_yylval.str);
	    }
	| KO_INTEGER
	    {
	    	    $$ = pstrdup(kokes_yylval.str);
	    }
	;
	
iname:	KO_ASCCHR
	    {
	    	    $$ = kokes_yylval.str[0];
	    }
	| '_'
	    {
	    	    $$ = '_';
	    }
	;
	

%%

static long 
to_long(const char *str)
{
	long val;
	char	*endptr;

	errno = 0;
	val = strtol(str, &endptr, 10);
	if (*endptr != '\0' || errno == ERANGE)
		ereport(ERROR,
				(errcode(ERRCODE_SYNTAX_ERROR),
				 errmsg("expected long, got %s", str)));

	return val;		    
}

static unsigned long 
to_ulong(const char *str)
{
	unsigned long val;
	char *endptr;

	errno = 0;
	val = strtoul(str, &endptr, 10);
	if (*endptr != '\0' || errno == ERANGE)
		ereport(ERROR,
				(errcode(ERRCODE_SYNTAX_ERROR),
				 errmsg("expected unsigned long, got %s", str)));

	return val;		    
}

static unsigned short 
to_ushort(const char *str)
{
	unsigned long val;
	char *endptr;

	errno = 0;
	val = strtoul(str, &endptr, 10);
	if (*endptr != '\0' || errno == ERANGE)
		ereport(ERROR,
				(errcode(ERRCODE_SYNTAX_ERROR),
				 errmsg("expected unsigned long, got %s", str)));
	if (val > USHRT_MAX)
		ereport(ERROR,
				(errcode(ERRCODE_SYNTAX_ERROR),
				 errmsg("value is too big %s", str)));

	return (unsigned short) val;		    
}

static short int 
to_sint(const char *str)
{
	long val;
	char *endptr;

	errno = 0;
	val = strtol(str, &endptr, 10);
	if (*endptr != '\0' || errno == ERANGE)
		ereport(ERROR,
				(errcode(ERRCODE_SYNTAX_ERROR),
				 errmsg("expected short integer, got %s", str)));
	if (val < SHRT_MIN || val > SHRT_MAX)
		ereport(ERROR,
				(errcode(ERRCODE_SYNTAX_ERROR),
				 errmsg("value is out of range %s", str)));

	return (short int) val;		    
}

static double
to_double(const char *str)
{
	double val;
	char *endptr;

	errno = 0;
	val = strtod(str, &endptr);
	if (*endptr != '\0' || errno == ERANGE)
		ereport(ERROR,
				(errcode(ERRCODE_SYNTAX_ERROR),
				 errmsg("expected double, got %s", str)));

	return val;
}



static void 
read_double_list(double *data, unsigned long cntpnt)
{
	unsigned long i;

	if (kokes_yylex() != '(')
		ereport(ERROR, 
				(errcode(ERRCODE_SYNTAX_ERROR),
				 errmsg("expected \"(\"")));
	for (i = 0; i < cntpnt; i++)
	{
		int	tok = kokes_yylex();
		if (tok != KO_FLOAT && tok != KO_INTEGER)
			ereport(ERROR,
				    (errcode(ERRCODE_SYNTAX_ERROR),
				     errmsg("expected number %s", kokes_yylval.str)));
		data[i] = to_double(kokes_yylval.str);
	}

	if (kokes_yylex() != ')')
		ereport(ERROR,
				(errcode(ERRCODE_SYNTAX_ERROR),
				 errmsg("expected \")\"")));
}


static void 
read_short_list(short int *data, unsigned long cntpnt)
{
	unsigned long i;

	if (kokes_yylex() != '(')
		ereport(ERROR, 
				(errcode(ERRCODE_SYNTAX_ERROR),
				 errmsg("expected \"(\"")));
	for (i = 0; i < cntpnt; i++)
	{
		if (kokes_yylex() != KO_INTEGER)
			ereport(ERROR,
				    (errcode(ERRCODE_SYNTAX_ERROR),
				     errmsg("expected number")));
		data[i] = to_sint(kokes_yylval.str);
	}

	if (kokes_yylex() != ')')
		ereport(ERROR,
				(errcode(ERRCODE_SYNTAX_ERROR),
				 errmsg("expected \")\"")));
}

/*
 * drop strange chars from string
 */
static char *
normalize_attr(char *str)
{
	char *w = str;
	char	c;
	char	*result = str;
	
	while ((c = *str++) != '\0')
	{
		switch (c)
		{
			case '\n':
				break;
			default:
				*w++ = c;
		}
	}
	*w = '\0';
	return result;
}

#undef yylex

#include "scan.c"

