/* $Id$ */
#include "bid_control.h"

HeapTuple bid_control() {
	TupleDesc	tupdesc;
	HeapTuple	rettuple;
	char query[500];
	bool isnull;
	int	ret, i, bid_quant, auc_quant, auction_id, stop_date, quant0, quant1,
			buyer_id, seller_id, last_buyer_id, id0;
	Datum price_datum;
	double next_price, next_incr, bid_price, price0, price1;
/*	float64 new_price_ptr = (float64)palloc(sizeof(float64data));*/
/*	char * buyer_login, * seller_login, * last_buyer_login, * login0, */
	char  * seller_mail, * seller_locale;

	if (!CurrentTriggerData)
		elog(NOTICE, "bid_control.c: triggers are not initialized");

	/* tuple to return to Executor */
	if (TRIGGER_FIRED_BY_INSERT(CurrentTriggerData->tg_event)) {
		rettuple = CurrentTriggerData->tg_trigtuple;
	} else {
		elog(ERROR, "bid_control.c: trigger should only be called on INSERT");
	}

	/* Connect to SPI manager */
	if ((ret = SPI_connect()) < 0)
		elog(NOTICE, "bid_control.c: SPI_connect returned %d", ret);

	tupdesc = CurrentTriggerData->tg_relation->rd_att;

	auction_id = SPI_getbinval(
			rettuple,
			tupdesc,
			SPI_fnumber(tupdesc, "auction_id"),
			&isnull);

	sprintf(query, "SELECT date_part('epoch', stopdate), a.quantity, 
			a.person_id,
			buyer(a.id), next_incr(a.id), next_price(a.id),
			p.mail as seller_mail, p.locale
			FROM auction* a, person p WHERE a.id = %d AND p.id = a.person_id", 
			auction_id);

	SPI_exec(query, 1);

	if (SPI_processed == 0) {
		elog(ERROR, "bid_control.c: no auction returned for id %d", auction_id);
	}

	seller_locale = SPI_getvalue(
			SPI_tuptable->vals[0],
			SPI_tuptable->tupdesc,
			SPI_fnumber(SPI_tuptable->tupdesc, "locale"));

	/* set user language
	 */
	bindtextdomain("apartia_com", "/usr/local/auction/locale");
	textdomain("apartia_com");
	setlocale(LC_ALL, seller_locale);

/*	elog(NOTICE, "bid_control: stopdate returned %f",
			*DatumGetFloat32(SPI_getbinval(
			SPI_tuptable->vals[0],
			SPI_tuptable->tupdesc,
			SPI_fnumber(SPI_tuptable->tupdesc,"date_part"),
			&isnull)));*/

	stop_date = atoi(SPI_getvalue(
			SPI_tuptable->vals[0],
			SPI_tuptable->tupdesc,
			SPI_fnumber(SPI_tuptable->tupdesc,"date_part")));

	auc_quant = SPI_getbinval(
			SPI_tuptable->vals[0],
			SPI_tuptable->tupdesc,
			SPI_fnumber(SPI_tuptable->tupdesc,"quantity"),
			&isnull);

	bid_quant = SPI_getbinval(
			rettuple,
			tupdesc,
			SPI_fnumber(tupdesc,"quantity"),
			&isnull);

/*	seller_login = SPI_getvalue(
			SPI_tuptable->vals[0],
			SPI_tuptable->tupdesc,
			SPI_fnumber(SPI_tuptable->tupdesc,"login"));*/

	seller_id = SPI_getbinval(
			SPI_tuptable->vals[0],
			SPI_tuptable->tupdesc,
			SPI_fnumber(SPI_tuptable->tupdesc,"person_id"),
			&isnull);

/*	buyer_login = SPI_getvalue(
			rettuple,
			tupdesc,
			SPI_fnumber(tupdesc, "login"));*/

	buyer_id = SPI_getbinval(
			rettuple,
			tupdesc,
			SPI_fnumber(tupdesc, "person_id"),
			&isnull);

/*	last_buyer_login = SPI_getvalue(
			SPI_tuptable->vals[0],
			SPI_tuptable->tupdesc,
			SPI_fnumber(SPI_tuptable->tupdesc,"buyer"));*/

	last_buyer_id = SPI_getbinval(
			SPI_tuptable->vals[0],
			SPI_tuptable->tupdesc,
			SPI_fnumber(SPI_tuptable->tupdesc,"buyer"),
			&isnull);

/*	max_price = atof(SPI_getvalue(
			SPI_tuptable->vals[0],
			SPI_tuptable->tupdesc,
			SPI_fnumber(SPI_tuptable->tupdesc,"max_price")));*/

	next_price = atof(SPI_getvalue(
			SPI_tuptable->vals[0],
			SPI_tuptable->tupdesc,
			SPI_fnumber(SPI_tuptable->tupdesc,"next_price")));

	next_incr = atof(SPI_getvalue(
			SPI_tuptable->vals[0],
			SPI_tuptable->tupdesc,
			SPI_fnumber(SPI_tuptable->tupdesc,"next_incr")));

	bid_price = adjust(atof(SPI_getvalue(
			rettuple,
			tupdesc,
			SPI_fnumber(tupdesc,"price"))));

	seller_mail = SPI_getvalue(
			SPI_tuptable->vals[0],
			SPI_tuptable->tupdesc,
			SPI_fnumber(SPI_tuptable->tupdesc,"seller_mail"));

/*	new_price = max_price + next_incr;*/

	if (TRIGGER_FIRED_BEFORE(CurrentTriggerData->tg_event)) {

		if (stop_date < GetCurrentAbsoluteTime()) {
			elog(ERROR, "bid_control.c: Auction is expired since %d", stop_date);
		}

		if (auc_quant < bid_quant) {
			elog(ERROR, "bid_control.c: Bid quantity %d is higher than auction quantity %d",
					bid_quant, auc_quant);
		}

/*		if (strcmp(buyer_login, seller_login) == 0) {*/
		if (buyer_id == seller_id) {
/*			elog(ERROR, "bid_control.c: Buyer (%s) and seller (%s) are the same person", buyer_login, seller_login);*/
			elog(ERROR, "bid_control.c: Buyer (%d) and seller (%d) are the same person", buyer_id, seller_id);
		}

/*		if (last_buyer_login != NULL && strcmp(buyer_login, last_buyer_login) == 0) {*/
		if (last_buyer_id != 0 && buyer_id == last_buyer_id) {
/*			elog(ERROR, "bid_control.c: %s can't bid against himself", buyer_login);*/
			elog(ERROR, "bid_control.c: %d can't bid against himself", buyer_id);
		}

/*		if (bid_price < new_price) { */
		if (bid_price < next_price) { 
			elog(ERROR, "bid_control.c: bid price %.2f is below minimum bid 
					of %.2f", bid_price, next_price);
		}

		if (bid_price == next_price) {
/*			elog(NOTICE, "bid_control.c: %s bid the exact required amount of %.2f", buyer_login, bid_price);*/
			elog(NOTICE, "bid_control.c: %d bid the exact required amount of %.2f", buyer_id, bid_price);
			SPI_finish();
/*			sprintf(query, gettext("This is to inform you that user %s bid %.2f on your auction No %d"), buyer_login, bid_price, */
			sprintf(query, gettext("This is to inform you that user %d bid %.2f on your auction No %d"), buyer_id, bid_price, auction_id);
/*			sendmail(seller_mail, "bid notification", query);*/
/*			elog(NOTICE, "Sending mail to %s", seller_mail);*/
			return rettuple;
		}

		if (bid_price > next_price) {
			elog(NOTICE, "bid_control.c: %d is bidding %.2f $ more than the 
					required price %.2f", buyer_id, bid_price - next_price, 
					next_price);
			sprintf(query, "INSERT INTO autobid (login, id, price,
				quantity) VALUES(%d, %d, %f, %d)", buyer_id, auction_id,
				bid_price, bid_quant);
			SPI_exec(query, 1);
/*			price_datum = PointerGetDatum(bid_price);*/
/*			price_datum = SPI_getbinval(
					rettuple,
					tupdesc,
					SPI_fnumber(tupdesc,"price"),
					&isnull);*/
/*			*new_price_ptr = new_price;*/
			price_datum = Float64GetDatum(&next_price);
			i = SPI_fnumber(tupdesc, "price");
			rettuple = SPI_modifytuple(
					CurrentTriggerData->tg_relation,
					rettuple,
					1,
					&i,
					&price_datum,
					NULL);

			SPI_finish();
			sprintf(query, gettext("This is to inform you that user %d bid %.2f 
					on your aucion No %d"), buyer_id, bid_price, auction_id);
/*			sendmail(seller_mail, "bid notification", query);*/
			return rettuple;
		}

	} else { /* AFTER */

		sprintf(query, "SELECT * FROM autobid WHERE id = %d
				AND price > %f AND person_id <> %d", auction_id, next_price,
				buyer_id);
		SPI_exec(query, 1);

		if (SPI_processed != 0) {

/*			login0 = SPI_getvalue(
						SPI_tuptable->vals[0],
						SPI_tuptable->tupdesc,
						SPI_fnumber(SPI_tuptable->tupdesc,"login"));*/

			id0 = SPI_getbinval(
						SPI_tuptable->vals[0],
						SPI_tuptable->tupdesc,
						SPI_fnumber(SPI_tuptable->tupdesc,"person_id"),
						&isnull);

			price0 = atof(SPI_getvalue(
						SPI_tuptable->vals[0],
						SPI_tuptable->tupdesc,
						SPI_fnumber(SPI_tuptable->tupdesc,"price")));

			quant0 = atoi(SPI_getvalue(
						SPI_tuptable->vals[0],
						SPI_tuptable->tupdesc,
						SPI_fnumber(SPI_tuptable->tupdesc,"quantity")));

			sprintf(query, "SELECT * FROM autobid WHERE auction_id = %d
					AND price > %f AND person_id = %d", auction_id, next_price,
					buyer_id);

			SPI_exec(query, 1);

			if (SPI_processed != 0) {

/*				login1 = SPI_getvalue(
							SPI_tuptable->vals[0],
							SPI_tuptable->tupdesc,
							SPI_fnumber(SPI_tuptable->tupdesc,"login"));*/

				price1 = atof(SPI_getvalue(
							SPI_tuptable->vals[0],
							SPI_tuptable->tupdesc,
							SPI_fnumber(SPI_tuptable->tupdesc,"price")));

				quant1 = atoi(SPI_getvalue(
							SPI_tuptable->vals[0],
							SPI_tuptable->tupdesc,
							SPI_fnumber(SPI_tuptable->tupdesc,"quantity")));

				/* old limit is higher, so bid up to the new lower limit
				 */

				if (price0 > price1) {
					next_price = price1 + incr(price1);
/*					elog(NOTICE, "bid_control.c: %s is auto-bidding against %s up to %.2f", login0, buyer_login, next_price);*/
					elog(NOTICE, "bid_control.c: %d is auto-bidding against %d up to %.2f", id0, buyer_id, next_price);
					sprintf(query, "UPDATE bid SET price = %f
						modified = CURRENT_TIMESTAMP
						WHERE auction_id = %d AND person_id = '%d' 
						AND price = %f",
						price1, auction_id, buyer_id, bid_price);
					SPI_exec(query, 1);
					sprintf(query, "INSERT INTO bid (person_id, auction_id, 
					price, quantity) VALUES (%d, %d, %f, %d)", id0,
							auction_id, next_price, quant0);
					SPI_exec(query, 1);
				}

				/* limits are equal, so the oldest wins, the newest is reduced
				 * by on increment
				 */

				if (price0 == price1) {
					next_price = price1 - incr(price1 
							- incr(price1));
					elog(NOTICE, "bid_control.c: %d previously had the same limit as %d whose 
							max_price will be reduced by one increment from %.2f 
							to  %.2f", id0, buyer_id, price1, next_price);

					sprintf(query, "UPDATE bid SET price = %f
						WHERE auction_id = %d AND person_id = %d
						AND price = %f",
						next_price, auction_id, buyer_id, bid_price);
					SPI_exec(query, 1);

					sprintf(query, "INSERT INTO bid (person_id, auction_id, 
						price, quantity) VALUES ('%d', %d, %f, %d)", id0,
							auction_id, price0, quant0);
					SPI_exec(query, 1);
				}

				/* old price is lower, so bid up to the old price plus one
				 * increment and adjust old bid up to its limit
				 */

				if (price0 < price1) {
					next_price = price0 - incr(price0);
					elog(NOTICE, "bid_control.c: %d previously had the same limit as %d whose 
							max_price will be reduced by one increment from %.2f 
							to  %.2f", id0, buyer_id, price1, next_price);

					sprintf(query, "INSERT INTO bid (person_id, id, price, 
							quantity) VALUES (%d, %d, %f, %d)", id0,
							auction_id, price0, quant0);
					SPI_exec(query, 1);
				}

			} else {

			/* abid_new NOT found, the old maxprice holder will overbid
			 */
				elog(NOTICE, "bid_control.c: %d is overbidding %d by one increment of %.2f", id0, buyer_id, next_incr);
				sprintf(query, "INSERT INTO bid (person_id, id, price, 
					quantity) VALUES (%d, %d, %f, %d)", id0,
						auction_id, next_price, quant0);
				SPI_exec(query, 1);

			}

		} else {
/*			sprintf(query, "SELECT * FROM autobid WHERE id = %d
					AND price > %f AND login <> '%s'", auction_id, new_price,
				buyer_login);
			elog(NOTICE, "bid_control.c: query \"%s\" returned nothing", query);*/
		}

	}

	CurrentTriggerData = NULL;

/*	when = SPI_getvalue(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1);*/

/*	elog(NOTICE, "arg 1 was %s", CurrentTriggerData->tg_trigger->tgargs[0]);*/
/*	elog(NOTICE, "arg 1 was %s", SPI_gettype(tupdesc, 5));*/
/*	elog(NOTICE, "Title is %s", when);
	elog(NOTICE, "Current time is %d", Int32GetDatum(GetCurrentAbsoluteTime()));
	elog(NOTICE, "Auction time is %d", DatumGetInt32(id));*/

	SPI_finish();

	return NULL;

}

HeapTuple add_user_password() {
	TupleDesc	tupdesc;
	HeapTuple	rettuple;
	char * login, * mail, * passwd;
	char password[10], mess[500], salt[2];
	Datum pass;
	int i;

	if (!CurrentTriggerData) {
		elog(NOTICE, "add_user: triggers are not initialized");
	}
	if (TRIGGER_FIRED_BY_INSERT(CurrentTriggerData->tg_event)) {
		rettuple = CurrentTriggerData->tg_trigtuple;
		tupdesc = CurrentTriggerData->tg_relation->rd_att;
	} else {
		elog(ERROR, "add_user: trigger should only be called on INSERT");
	}

	if ((i = SPI_connect()) < 0)
		elog(NOTICE, "add_user: SPI_connect returned %d", i);

	if (TRIGGER_FIRED_BEFORE(CurrentTriggerData->tg_event)) {

		sprintf(password, "%06d", 1+(int)(999999.0*rand()/(RAND_MAX+1.0)));

		pass = PointerGetDatum(textin(password));

		i = SPI_fnumber(tupdesc, "password");
		rettuple = SPI_modifytuple(
				CurrentTriggerData->tg_relation,
				rettuple,
				1,
				&i,
				&pass,
				NULL);

		SPI_finish();

		return rettuple;

	} else if (TRIGGER_FIRED_AFTER(CurrentTriggerData->tg_event)) {
/*		elog(ERROR, "add_user: trigger should only be called BEFORE");*/
		login = SPI_getvalue(
				rettuple,
				tupdesc,
				SPI_fnumber(tupdesc, "login"));

		mail = SPI_getvalue(
				rettuple,
				tupdesc,
				SPI_fnumber(tupdesc, "mail"));

		passwd = SPI_getvalue(
				rettuple,
				tupdesc,
				SPI_fnumber(tupdesc, "password"));

		strncpy(salt, login, 2);
		salt[2] = '\0';
/*		password = crypt(password, salt);*/

		sprintf(mess, "UPDATE person SET password = '%s' WHERE login = '%s'",
				crypt(passwd, salt), login);
		SPI_exec(mess, 1);

		SPI_finish();

		sprintf(mess, gettext("Welcome to Apartia %s\n\nYour password is: %s"),
				login, passwd);

		sendmail(mail, gettext("Welcome to Apartia"), mess);
							 
		return rettuple;
	}

	CurrentTriggerData = NULL;

/*	SPI_finish();*/

	return NULL;
}

/*HeapTuple validate_category() {
	TupleDesc	tupdesc;
	HeapTuple	rettuple;
	char * login, * mail, * category;
	char password[10], mess[500];
	Datum pass;
	int r, i;

	if (!CurrentTriggerData) {
		elog(NOTICE, "add_user: triggers are not initialized");
	}
	if (TRIGGER_FIRED_BY_INSERT(CurrentTriggerData->tg_event)) {
		rettuple = CurrentTriggerData->tg_trigtuple;
		tupdesc = CurrentTriggerData->tg_relation->rd_att;
	} else {
		elog(ERROR, "add_user: trigger should only be called on INSERT");
	}

	if ((i = SPI_connect()) < 0)
		elog(NOTICE, "add_user: SPI_connect returned %d", i);

	if (TRIGGER_FIRED_BEFORE(CurrentTriggerData->tg_event)) {
		category = SPI_getvalue(
				rettuple,
				tupdesc,
				SPI_fnumber(tupdesc, "category"));
	} else {
		elog(ERROR, "add_user: trigger should only be called BEFORE");
	}

	SPI_finish();
	CurrentTriggerData = NULL;
	return NULL;
}*/

static int sendmail(char * mailto, char * subject, char * mess) {

	register FILE	*mail;
/*	register char	**env;*/
	auto char	mailcmd[MAX_COMMAND];
/*	auto char	hostname[MAXHOSTNAMELEN];*/

/*	(void) gethostname(hostname, MAXHOSTNAMELEN);*/
	(void) sprintf(mailcmd, MAILARGS, MAILCMD, mailto);
/*	if (!(mail = cron_popen(mailcmd, "w"))) {*/
	if (!(mail = popen(mailcmd, "w"))) {
		perror(MAILCMD);
		(void) _exit(ERROR_EXIT);
	}
	fprintf(mail, "From: root (Auction Daemon)\n");
	fprintf(mail, "To: %s\n", mailto);
	fprintf(mail, "Subject: %s\n", subject);
/*	for (env = e->envp;  *env;  env++)
		fprintf(mail, "X-Cron-Env: <%s>\n",
				*env);*/
	fprintf(mail, "\n");
	fprintf(mail, mess);
	fprintf(mail, "\n.\n");

	pclose(mail);
	return 1;
}

static double adjust(double f) {
	return f - fmod(f, incr(f));
}

static double incr(double f) {
	if (f < pow(10, (int)log10(f)+1)/2) {
		return pow(10, (int)log10(f)) * 0.05;
	} else {
		return (pow(10, (int)log10(f)+1)/2) * 0.05;
	}
}

/*static char *strAllocCat (char **a, const char *b) {
  size_t la = strlen (*a);
  *a = realloc (*a, la + strlen (b) + 1);
  strcpy (*a + la, b);
  return (*a);
}*/

