/********************************************
libmawk (C) 2014, Tibor 'Igor2' Palinkas;

This is a source file for libmawk, an implementation of
the AWK programming language, fork of mawk.

Libmawk is distributed without warranty under the terms of
the GNU General Public License, version 2, 1991.
********************************************/
#include <stdio.h>
#include <libmawk.h>
#include "vio_hash.h"
#include "memory.h"

typedef struct mawk_vio_hash_s {
	/* this field is mandatory and must be the first*/
	mawk_vio_t vio_common_head;

	/* vio implementation-specific fields */
	mawk_zfifo_t fifo;
	int eof_from_awk;       /* 1 if there won't be more from awk or awk won't accept more data (close()) */
	int eof_from_app;       /* 1 if there won't be more from the app or the app won't accept more data */
	unsigned long int seed, value;
} mawk_vio_hash_t;

/* create a new hash vio */
mawk_vio_t *mawk_vio_hash_open(mawk_state_t *MAWK, const char *name, mawk_vio_open_mode_t mode)
{
	mawk_vio_hash_t *v;

	if ((mode != MAWK_VIO_O_APPEND) && (mode != MAWK_VIO_O_TRUNC))
		return NULL;

	v = mawk_zmalloc(MAWK, sizeof(mawk_vio_hash_t));
	v->vio_common_head.imp = &mawk_vio_hash_imp;
	v->vio_common_head.refco = 0;
	v->eof_from_awk = 0;
	v->eof_from_app = 0;
	v->seed = 0;
	v->value = 0;
	return (mawk_vio_t *)v;
}

/* add a chatacter to the hash */
static void hash_putc(mawk_vio_hash_t *v, int val)
{
	val <<= v->seed % 32;
	v->value ^= val;
	v->seed++;
}

/* putchar from awk */
int mawk_vio_hash_putc(mawk_state_t *MAWK, mawk_vio_t *vf, char c)
{
	mawk_vio_hash_t *v = (mawk_vio_hash_t *)vf;

	if (v->eof_from_app)
		return -1;

	hash_putc(v, c);
	return 1;
}

/* write string from awk */
int mawk_vio_hash_write_str(mawk_state_t *MAWK, mawk_vio_t *vf, const char *str)
{
	mawk_vio_hash_t *v = (mawk_vio_hash_t *)vf;
	int len;

	if (v->eof_from_app)
		return -1;

	for(len = 0; *str != '\0'; str++, len++)
		hash_putc(v, *str);
	return len;
}

/* write bin from awk */
int mawk_vio_hash_write(mawk_state_t *MAWK, mawk_vio_t *vf, const char *data, int len)
{
	mawk_vio_hash_t *v = (mawk_vio_hash_t *)vf;
	int n;

	if (v->eof_from_app)
		return -1;

	for(n = 0; n < len; n++)
		hash_putc(v, data[n]);
	return len;
}

/* do not implement printf as there's no clever way doing it; let libmawk
   do the sprintf() thing */
int mawk_vio_hash_printf(mawk_state_t *MAWK, mawk_vio_t *vf, const char *fmt, ...)
{
	mawk_vio_hash_t *v = (mawk_vio_hash_t *)vf;
	abort();
}

/* in case the file is read in awk, it should return the current hash value
   and an eof (one-shot file). Sending eof is done by putting the file in
   eof state in the first call and returning eof in the second.

   NOTE: this simple implementation won't allow the script to open the same
         file again; mawk state's vio_init.open should be hooked for that
         and a manual vf reopen shall be done.
   */
int mawk_vio_hash_read(mawk_state_t *MAWK, mawk_vio_t *vf, char *dst, long int size)
{
	mawk_vio_hash_t *v = (mawk_vio_hash_t *)vf;
	char buff[64];
	int l;

	if (v->eof_from_app)
		return 0;

	l = sprintf(buff, "%d\n", v->value);
	if (l <= size) {
		memcpy(dst, buff, l);
		v->eof_from_app = 1;
		return l;
	}
	return -1;
}

static void close_on_eof(mawk_state_t *MAWK, mawk_vio_hash_t *v)
{
	/* free the struct only if both sides closed it */
	if ((v->eof_from_app) && (v->eof_from_awk))
		mawk_zfree(MAWK, v, sizeof(mawk_vio_hash_t));
}

/* the script closes the file */
int mawk_vio_hash_close(mawk_state_t *MAWK, mawk_vio_t *vf)
{
	mawk_vio_hash_t *v = (mawk_vio_hash_t *)vf;

	v->eof_from_awk = 1;
	close_on_eof(MAWK, v);
	return 0;
}

int mawk_vio_hash_flush(mawk_state_t *MAWK, mawk_vio_t *vf)
{
	/* nothing to do on flush */
}

int mawk_vio_hash_error(mawk_state_t *MAWK, mawk_vio_t *vf)
{
	return (vf == NULL);
}

void mawk_vio_hash_mark_no_close(mawk_state_t *MAWK, mawk_vio_t *vf)
{
	/* fifos are always closed when both awk and the app closes them and
	   there are no inherited pipes anyway */
}

const mawk_vio_imp_t mawk_vio_hash_imp = {
	mawk_vio_hash_putc,
	mawk_vio_hash_write_str,
	mawk_vio_hash_write,
	mawk_vio_hash_printf,
	mawk_vio_hash_read,
	mawk_vio_hash_close,
	mawk_vio_hash_flush,
	mawk_vio_hash_error,
	mawk_vio_hash_mark_no_close
};


/* helper calls for the app */
unsigned long int mawk_vio_hash_val(mawk_state_t *MAWK, mawk_vio_t *vf)
{
	mawk_vio_hash_t *v = (mawk_vio_hash_t *)vf;
	return v->value;
}

/* the app wanted to close the file */
int mawk_vio_hash_eof_from_app(mawk_state_t *MAWK, mawk_vio_t *vf)
{
	mawk_vio_hash_t *v = (mawk_vio_hash_t *)vf;

	v->eof_from_app = 1;
	close_on_eof(MAWK, v);
	return 0;
}
