/*  gnutrition - a nutrition and diet analysis program.
 *  Copyright( C) 2000, 2001 Edgar Denny( e.denny@ic.ac.uk)
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 * ( at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <gnome.h>
#include <glade/glade.h>
#include <ctype.h>

#include "text_util.h"
#include "food.h"
#include "plan_compute_dlg.h"
#include "plan_compute.h"
#include "wrap_mysql.h"

static GList *get_ingredient_list( char *);
static GList *get_food_nutr_list( char *);
static float get_gm_per_measure( char *, char *);
static void add_food_nutr_comp( GList **, char *, float, float);
static void compute_plan_recipe_total( GList **, char *, char *);
static void add_plan_food_total( GList **, char *, char *, char *);
static void divide_total_by_days( GList **, char *, char *);
static GList *init_nutr_list( void);

/* get the list of ingredients for a specified recipe number. */
static GList *
get_ingredient_list( char *recipe_no)
{
	GList *list;
	char *query;

	g_return_val_if_fail( recipe_no, NULL);

	query = g_strconcat( "SELECT amount, msre_no, fd_no FROM ingredient ",
		"WHERE recipe_no = '", recipe_no, "'", NULL);
	list = rows_glist_ret_val_query_db( query);
	g_assert( list);
	g_free( query);

	return list;
}

/* get the list of nutrient values for the specific food. */
static GList *
get_food_nutr_list( char *fd_no)
{
	GList *list;
	char *query;

	g_return_val_if_fail( fd_no, NULL);

	query = g_strconcat( "SELECT nutr_no, nutr_val FROM nut_data ",
		"WHERE fd_no = '", fd_no, "'", NULL);
	list = rows_glist_ret_val_query_db( query);
	g_assert( list);
	g_free( query);

	return list;
}

/* get the number of grams in a measure for a specific food. */
static float
get_gm_per_measure( char *fd_no, char *msre_no)
{
	char *query;
	char *text;
	float gm_per_msre;

	g_return_val_if_fail( fd_no, 0.0);
	g_return_val_if_fail( msre_no, 0.0);

	if ( strcmp( msre_no, "99999") == 0) {
		gm_per_msre = 1.0;
	} else {
		query = g_strconcat( "SELECT wgt_val FROM weight WHERE ",
			"fd_no = '", fd_no, "' AND msre_no = '", msre_no, "'",
			NULL);
		text = single_ret_val_query_db( query);
		g_assert( text);
		gm_per_msre = atof( text);
		g_free( text);
		g_free( query);
	}

	return gm_per_msre;
}

/* add the nutrient contribution of the ingredient to the total
 * nutrient composition. */
static void
add_food_nutr_comp( GList **list_total,
                    char *fd_no,
                    float amount,
                    float gm_per_msre)
{
	GList *list_fd_nutr;
	GList *ptr1, *ptr2;
	char **elm1, **elm2;
	float total;

	g_return_if_fail( list_total);
	g_return_if_fail( fd_no);

	list_fd_nutr = get_food_nutr_list( fd_no);

	/* iterate over the nutrient running totals. */
	for ( ptr1 = *list_total; ptr1; ptr1 = ptr1->next) {
		elm1 = (char **)ptr1->data;
		/* elm1[0] = nutr_no, elm1[1] = total_nutr_val. */

		/* iterate over the nutrients for the specific ingredient. */
		for ( ptr2 = list_fd_nutr; ptr2; ptr2 = ptr2->next) {
			elm2 = (char **)ptr2->data;
			/* elm2[0] = nutr_no, elm2[1] = ingr_nutr_val. */

			/* sum the nutrient values for the ingredient to the
			 * running total. */
			if ( strcmp( elm1[0], elm2[0]) == 0) {
				total = atof( elm1[1]);
				total += amount * gm_per_msre * 
					atof( elm2[1]) / 100.0;
				g_free( elm1[1]);
				elm1[1] = g_strdup( ftoa( total));
				break;
			}
		}
	}
	gnutr_free_row_list( list_fd_nutr, 2);
}


static void
compute_plan_recipe_total( GList **list_nutr_tot, 
                           char *recipe_no, 
                           char *no_portions)
{
	GList *list_ingr;
	GList *ptr;
	char **elm;
	float gm_per_msre;
	float amount;
	char *no_serv, *query;

	g_return_if_fail( list_nutr_tot);
	g_return_if_fail( recipe_no);
	g_return_if_fail( no_portions);

	list_ingr = get_ingredient_list( recipe_no);

	/* get the number of servings for the recipe. */
	query = g_strconcat( "SELECT no_serv FROM recipe "
		"WHERE recipe_no = '", recipe_no, "'", NULL);
	no_serv = single_ret_val_query_db( query);
	g_assert( no_serv);
	g_free( query);

	/* interate over the list of ingredients. */
	for ( ptr = list_ingr; ptr; ptr = ptr->next) {
		elm = (char **)ptr->data;
		/* elm[0] = amount, elm[1] = msre_no, elm[2] = fd_no. */

		amount = atof( elm[0]) * atof( no_portions) / atof( no_serv);
		gm_per_msre = get_gm_per_measure( elm[2], elm[1]);

		add_food_nutr_comp( list_nutr_tot, elm[2], amount, 
				gm_per_msre);
	}
	g_free( no_serv);

	gnutr_free_row_list( list_ingr, 3);
}

/* compute the nutrient composition for the food in the plan
 * and add it to total. */
static void
add_plan_food_total( GList **list_nutr_tot,
                     char *amount,
                     char *msre_no,
                     char *fd_no)
{
	float quantity;
	float gm_per_msre;

	g_return_if_fail( list_nutr_tot);
	g_return_if_fail( amount);
	g_return_if_fail( msre_no);
	g_return_if_fail( fd_no);

	quantity = atof( amount);
	gm_per_msre = get_gm_per_measure( fd_no, msre_no);

	add_food_nutr_comp( list_nutr_tot, fd_no, quantity, 
			gm_per_msre);
}

/* create a list that will contain the result and initialize the
 * nutrient value elements to zero. */
static GList *
init_nutr_list()
{
	char *query;
	GList *list_ret = NULL, *list_nutr_no, *ptr;
	char **elm;
	char *nutr_no;

	/* get the full list of nutrient numbers. */
	query = g_strdup( "SELECT nutr_no FROM nutr_def");
	list_nutr_no = get_glist_fields_query_db( query);
	g_assert( list_nutr_no);
	g_free( query);

	/* create the list that contains the result and initialize
	 * it to zero. */
	for (ptr = list_nutr_no; ptr; ptr = ptr->next) {
		nutr_no = (char *)ptr->data;
		elm = (char **)g_malloc( 2 * sizeof( char *));
		/* elm[0] = nutr_no, elm[1] = nutr_val */
		elm[0] = g_strdup( nutr_no);
		elm[1] = g_strdup( "0.000");
		list_ret = g_list_append( list_ret, (gpointer)elm);
	}
	gnutr_free_field_list( list_nutr_no);

	return list_ret;
}

/* compute the average nutrient total for the number of days
 * specified by the start and end dates. */
static void
divide_total_by_days( GList **list_nutr_tot, 
                      char *date_start, 
                      char *date_end)
{
	char *query;
	char *no_days1 = NULL, *no_days2 = NULL;
	int days_diff;
	GList *ptr;
	char **elm;
	float total, avg;

	g_return_if_fail( list_nutr_tot);
	g_return_if_fail( date_start);
	g_return_if_fail( date_end);

	query = g_strconcat( "SELECT TO_DAYS('", date_start, "')", NULL);
	no_days1 = single_ret_val_query_db( query);
	g_assert( no_days1);
	g_free( query);

	query = g_strconcat( "SELECT TO_DAYS('", date_end, "')", NULL);
	no_days2 = single_ret_val_query_db( query);
	g_assert( no_days2);
	g_free( query);

	days_diff = atoi( no_days2) - atoi( no_days1) + 1;
	g_assert( days_diff >= 1);
	g_free( no_days1);
	g_free( no_days2);

	for ( ptr= *list_nutr_tot; ptr; ptr = ptr->next) {
		elm = (char **)ptr->data;
		/* elm[0] = nutr_no, elm[1] = nutr_tot_val. */

		total = atof( elm[1]);
		g_free( elm[1]);
		avg = total / (float)days_diff;
		elm[1] = g_strdup( ftoa( avg));
	}
}

GList *
gnutr_compute_plan_nutr_comp( char     *date_start, 
                              char     *date_end, 
                              gboolean  calc_avg)
{
	char *query;
	char **elm;
	GList *list = NULL, *ptr;
	GList *list_nutr_tot = NULL;

	g_return_val_if_fail( date_start, NULL);
	g_return_val_if_fail( date_end, NULL);
	g_return_val_if_fail( calc_avg == TRUE || calc_avg == FALSE, NULL);

	/* create the list that contains to result and initialize
	 * its nutrient value elements to zero. */
	list_nutr_tot = init_nutr_list();

	/* retrive the recipes in the plan that fall within the
	 * specified dates. */
	query = g_strconcat( "SELECT recipe_no, no_portions FROM ",
		"recipe_plan_temp WHERE date >='", date_start, "' AND ",
		"date <='", date_end, "'", NULL);
	list = rows_glist_ret_val_query_db( query);
	g_free( query);

	/* compute the recipe nutrient totals. */
	for ( ptr = list; ptr; ptr = ptr->next) {
		elm = (char **)ptr->data;
		/* elm[0] = recipe_no, elm[1] = no_portions. */

		compute_plan_recipe_total( &list_nutr_tot, elm[0], elm[1]);
	}
	if ( list) gnutr_free_row_list( list, 2);
	list = NULL;

	/* retrive the foods in the plan that fall within the
	 * specified dates. */
	query = g_strconcat( "SELECT amount, msre_no, fd_no FROM ",
		"food_plan_temp WHERE date >='", date_start, "' AND ",
		"date <='", date_end, "'", NULL);
	list = rows_glist_ret_val_query_db( query);
	g_free( query);

	/* compute the food nutrient totals. */
	for ( ptr = list; ptr; ptr = ptr->next) {
		elm = (char **)ptr->data;
		/* elm[0] = amount, elm[1] = msre_no, elm[2] = fd_no. */

		add_plan_food_total( &list_nutr_tot, elm[0], elm[1], elm[2]);
	}
	if (list) gnutr_free_row_list( list, 3);

	if ( calc_avg) {
		divide_total_by_days( &list_nutr_tot, date_start, date_end);
	}

	return list_nutr_tot;
}
