/*  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 "recipe.h"
#include "recipe_win.h"
#include "support.h"
#include "nutr_comp_dlg.h"
#include "wrap_mysql.h"
#include "person.h"

static GList * compute_nutr_total( void);
static GList * compute_percent_nutrient_goal( GList *);
static void load_xml( void);
static void connect_signals( void);
static void update_nutrient_dialog( GList *, GList *);
static void add_food_to_nutrient_total( GList **, char *, char *, char *);

/* callback. */
static void on_ok_button_released( GtkButton *, gpointer);

static GladeXML *xml = NULL;

struct t_nutr_goal {
	char *widget; /* name of Glade widget */
	char *idx;    /* index to nutr_no in database. */
};

/* nutrient labeled "no_name" - it is vitamin A, 
 * but measured in IU rather than mcg RE. */
struct t_nutr_goal nc_data[NO_NUTR] = 
{
	/* macro-nutrients */
	{"protein", "203"}, {"fat", "204"}, {"sat_fat", "606"}, 
	{"mono_fat", "645"}, {"poly_fat", "646"}, {"carb", "205"}, 
	{"chol", "601"}, {"fiber", "291"}, {"kj", "268"}, {"kcal", "208"}, 
	/* micro-nutrients. */
	{"vita", "392"}, {"no_name", "318"}, {"vite", "394"}, {"vitc", "401"}, 
	{"thia", "404"}, {"ribo", "405"}, {"nia", "406"}, {"panto", "410"}, 
	{"vitb6", "415"}, {"fol", "417"}, {"vitb12", "418"}, {"ca", "301"}, 
	{"fe", "303"}, {"mg", "304"}, {"p", "305"}, {"k", "306"}, 
	{"na", "307"}, {"zn", "309"}, {"cu", "312"}, {"mn", "315"}, 
	{"se", "317"}, 
	/* proteins */
	{"tryp", "501"}, {"thre", "502"}, {"isol", "503"}, 
	{"leuc", "504"}, {"lysi", "505"}, {"meth", "506"}, {"cyst", "507"}, 
	{"phen", "508"}, {"tyro", "509"}, {"vali", "510"}, {"argi", "511"}, 
	{"hist", "512"}, {"alan", "513"}, {"aspa", "514"}, {"glut", "515"}, 
	{"glyc", "516"}, {"prol", "517"}, {"seri", "518"}, 
	/* fats */
	{"fat_4d0", "607"},  {"fat_6d0", "608"}, {"fat_8d0", "609"}, 
	{"fat_10d0", "610"}, {"fat_12d0", "611"}, {"fat_14d0", "612"}, 
	{"fat_16d0", "613"}, {"fat_18d0", "614"}, {"fat_20d0", "615"}, 
	{"fat_18d1", "617"}, {"fat_18d2", "618"}, {"fat_18d3", "619"},
	{"fat_20d4", "620"}, {"fat_22d6", "621"}, {"fat_22d0", "624"}, 
	{"fat_14d1", "625"}, {"fat_16d1", "626"}, {"fat_18d4", "627"}, 
	{"fat_20d1", "628"}, {"fat_20d5", "629"}, {"fat_22d1", "630"}, 
	{"fat_22d5", "631"}, {"fat_15d0", "652"}, {"fat_17d0", "653"}, 
	{"fat_24d0", "654"},  
	/* miscelaneous */
	{"ash", "207"}, {"alco", "221"}, {"water", " 255"}, {"phyto", "636"}, 
	{"theob", "263"}, {"caff", "262"}, {"sugar", "269" } 
};

/* load the glade xml if not already loaded. */
static void
load_xml()
{
	static gboolean loaded_xml = FALSE;

	/* load the glade interface. */
	if ( !loaded_xml) {
		xml = glade_xml_new( 
			GNUTRITION_GLADEDIR "/nutr_comp_dlg.glade", NULL);
		loaded_xml = TRUE;
		if ( xml) {
			connect_signals();
		} else {
			g_log( "Gnutrition", G_LOG_LEVEL_ERROR,
				"cannot load file: nutr_comp_dlg.glade\n");
			return;
		}
	}
}

static void
connect_signals()
{
	gtk_signal_connect( GTK_OBJECT( 
		glade_xml_get_widget( xml, "ok_button")),
		"released", GTK_SIGNAL_FUNC( on_ok_button_released), NULL);
}

GtkWidget *
gnutr_get_comp_notebook()
{
	if ( !xml) load_xml();
	return glade_xml_get_widget( xml, "notebook");
}

GtkWidget *
gnutr_get_comp_notebook_container()
{
	if ( !xml) load_xml();
	return glade_xml_get_widget( xml, "notebook_container");
}


/* update the nutrition composition dialog. */
static void
update_nutrient_dialog( GList *list_tot_nutr,
                        GList *list_pcnt_goal)
{
	GtkWidget *entry;
	char *widget_name;
	GList *ptr;
	char **elm;
	int i;

	g_return_if_fail( list_tot_nutr);
	g_return_if_fail( list_pcnt_goal);

	/* initialize all the entries to zero. */
	for ( i=0; i<NO_NUTR; i++) {
		widget_name = g_strconcat( nc_data[i].widget, "_entry", NULL);
		if ( strcmp( widget_name, "no_name_entry") == 0) {
			g_free( widget_name);
			continue;
		}
		entry = glade_xml_get_widget( xml, widget_name);
		gtk_entry_set_text( GTK_ENTRY( entry), "0.000");
		g_free( widget_name);

		widget_name = g_strconcat( nc_data[i].widget, "_dg_entry", 
			NULL);
		entry = glade_xml_get_widget( xml, widget_name);
		gtk_entry_set_text( GTK_ENTRY( entry), "0.000");
		g_free( widget_name);
	}

	/* the nutrient totals. */
	for ( ptr = list_tot_nutr; ptr; ptr = ptr->next) {
		elm = ( char **)ptr->data;
		/* elm[0] = nutr_no, elm[1] = nutr_val_tot. */

		for ( i=0; i<NO_NUTR; i++) {
			if ( strcmp( elm[0], nc_data[i].idx) == 0) {
				widget_name = g_strconcat( nc_data[i].widget, 
					"_entry", NULL);
				if ( strcmp( widget_name, "no_name_entry") == 0)
				{
					g_free( widget_name);
					continue;
				}
				entry = glade_xml_get_widget( xml, widget_name);
				gtk_entry_set_text( GTK_ENTRY( entry), elm[1]);
				g_free( widget_name);
			}
		}
	}
	
	/* the totals as a percentage of the nutrient goal. */
	for ( ptr = list_pcnt_goal; ptr; ptr = ptr->next) {
		elm = ( char **)ptr->data;
		/* elm[0] = nutr_no, elm[1] = nutr_pcnt_tot. */

		for ( i=0; i<NO_NUTR; i++) {
			if ( strcmp( elm[0], nc_data[i].idx) == 0) {
				widget_name = g_strconcat( nc_data[i].widget, 
					"_dg_entry", NULL);
				if ( strcmp( widget_name, 
						"no_name_dg_entry") == 0) 
				{
					g_free( widget_name);
					continue;
				}
				entry = glade_xml_get_widget( xml, widget_name);
				gtk_entry_set_text( GTK_ENTRY( entry), elm[1]);
				g_free( widget_name);
			}
		}
	}
}

static void
add_food_to_nutrient_total( GList **list_tot_nutr_val, 
                            char *amount, 
                            char *msre_no, 
                            char *fd_no)
{
	char *query;
	GList *list_food_nutr, *ptr1, *ptr2;
	char **fd_elm, **tot_elm;
	float total;
	char *gm_per_msre;

	g_return_if_fail( *list_tot_nutr_val);
	g_return_if_fail( amount);
	g_return_if_fail( msre_no);
	g_return_if_fail( fd_no);

	/* get the list of nutrient values/100g for each ingredient. */
	query = g_strconcat( "SELECT nutr_no, nutr_val FROM nut_data ",
		"WHERE fd_no ='", fd_no, "'", NULL);
	list_food_nutr = rows_glist_ret_val_query_db( query);
	g_free( query);

	/* get the number of grams in the measure. */
	if ( strcmp( msre_no, "99999") == 0) {
		gm_per_msre = g_strdup( "1.0");
	} else {
		query = g_strconcat( "SELECT wgt_val FROM weight ",
			"WHERE fd_no ='", fd_no, "' AND msre_no ='", 
			msre_no, "'", NULL);
		gm_per_msre = single_ret_val_query_db( query);
		g_assert( gm_per_msre);
		g_free( query);
	}

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

		/* iterate over nutrients for specific food. */
		for ( ptr2 = list_food_nutr; ptr2; ptr2 = ptr2->next) {
			fd_elm = ( char **)ptr2->data;
			/* fd_elm[0] = nutr_no, fd_elm[1] = nutr_val. */

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

/* compute the nutrient total for the recipe. */
static GList *
compute_nutr_total()
{
	GList *ptr;
	char *no_serv, *nutr_no;
	char **elm;
	GList *list_nutr_no = NULL;
	GList *list_tot_nutr_val = NULL;
	GList *list_ingr = NULL;
	float total;
	GHashTable *htbl1, *htbl2;
	char *text1, *text2;

	htbl1 = get_htbl_fd_no_fd_desc();
	htbl2 = get_htbl_msre_no_msre_desc();

	/* get the full list of nutrient numbers. */
	list_nutr_no = gnutr_get_nutr_no_list();

	/* 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_tot_nutr_val = g_list_append( list_tot_nutr_val, 
			(gpointer)elm);
	}

	no_serv = gnutr_get_recipe_no_serv();

	list_ingr = gnutr_get_recipe_ingr_list();

	/* iterate 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. */

		text1 = (char *)g_hash_table_lookup( htbl1, (gpointer)elm[2]);
		text2 = (char *)g_hash_table_lookup( htbl2, (gpointer)elm[1]);

		add_food_to_nutrient_total( &list_tot_nutr_val, elm[0], 
			elm[1], elm[2]);
	}
	gnutr_free_row_list( list_ingr, 3);

	/* divide the total by the number of servings. */
	for ( ptr = list_tot_nutr_val; ptr; ptr = ptr->next) {
		elm = ( char **)ptr->data;
		total = atof( elm[1]);
		total /= atof( no_serv);
		g_free( elm[1]);
		elm[1] = g_strdup( ftoa( total));
	}

	return list_tot_nutr_val;
}

/* compute the percentage of the nutrient goal achieved
 * by the nutrient values per serving. */
static GList *
compute_percent_nutrient_goal( GList *list_tot_nutr_val)
{
	char *person_no;
	GList *list_pcnt_goal = NULL;
	GList *list_goal;
	GList *ptr1, *ptr2;
	char **elm1, **elm2, **elm3;
	char *query;
	char *nutr_no1, *tot_val, *nutr_no2, *goal_val;
	float pcnt;

	g_return_val_if_fail( list_tot_nutr_val, NULL);

	person_no = gnutr_get_connected_person_no();

	query = g_strconcat( "SELECT nutr_no, goal_val FROM nutr_goal ",
		"WHERE person_no ='", person_no, "'", NULL);
	list_goal = rows_glist_ret_val_query_db( query);
	g_assert( list_goal);
	g_free( query);
	g_free( person_no);

	for ( ptr2 = list_tot_nutr_val; ptr2; ptr2 = ptr2->next) {
		elm2 = ( char **)ptr2->data;
		nutr_no2 = elm2[0];
		tot_val = elm2[1];

		/* create an element in the percentage goal list, and
		 * assign to zero. */
		elm3 = ( char **)g_malloc( 2 * sizeof( char *));
		elm3[0] = g_strdup( nutr_no2);
		elm3[1] = g_strdup( "0.000");
		list_pcnt_goal = g_list_append( list_pcnt_goal, (gpointer)elm3);

		/* compute the percentage of the goal. */
		for ( ptr1 = list_goal; ptr1; ptr1 = ptr1->next) {
	 		elm1 = ( char **)ptr1->data;
			nutr_no1 = elm1[0];
			goal_val = elm1[1];

			if ( strcmp( nutr_no1, nutr_no2) == 0) {
				if ( atof( goal_val) == 0.0) continue;
				pcnt = atof( tot_val) * 100.0 / atof( goal_val);
				g_free( elm3[1]);
				elm3[1] = g_strdup( ftoa( pcnt));
			}
		}
	}
	gnutr_free_row_list( list_goal, 2);

	return list_pcnt_goal;
}

/* reset all the nutrient entries to zero. */
void
gnutr_reset_nutr_comp()
{
	int i;
	char *widget_name;
	GtkWidget *entry;

	for ( i=0; i<NO_NUTR; i++) {
		widget_name = g_strconcat( nc_data[i].widget, "_entry", NULL);
		if ( strcmp( widget_name, "no_name_entry") == 0) {
			g_free( widget_name);
			continue;
		}
		entry = glade_xml_get_widget( xml, widget_name);
		gtk_entry_set_text( GTK_ENTRY( entry), "0.000");
		g_free( widget_name);

		widget_name = g_strconcat( nc_data[i].widget, "_dg_entry", 
			NULL);
		entry = glade_xml_get_widget( xml, widget_name);
		gtk_entry_set_text( GTK_ENTRY( entry), "0.000");
		g_free( widget_name);
	}
}

/* When the "Nutrients" button is released in the recipe window, show
 * the nutritional composition dialog. */
void
gnutr_show_nutr_comp_dlg( GList *list,
                          int   called_from)
{
	GList *list_tot_nutr_val;
	GList *list_pcnt_goal;
	GtkWidget *child, *parent;
	enum { CALLED_FROM_RECIPE, CALLED_FROM_PLAN };

	g_return_if_fail( called_from == CALLED_FROM_RECIPE ||
			called_from == CALLED_FROM_PLAN);

	if ( !xml) load_xml();

	if ( called_from == CALLED_FROM_RECIPE) {
		list_tot_nutr_val = compute_nutr_total();
	} else {
		list_tot_nutr_val = list;
	}

	/* there is a possibility that the notebook has been reparented
	 * to be used in the food view. Reparent it back to its
	 * original dialog so it can be re-used here. */
	child = glade_xml_get_widget( xml, "notebook");
	parent = glade_xml_get_widget( xml, "notebook_container");
	gtk_widget_reparent( child, parent);

	list_pcnt_goal = compute_percent_nutrient_goal( list_tot_nutr_val);

	update_nutrient_dialog( list_tot_nutr_val, list_pcnt_goal);

	gnutr_free_row_list( list_tot_nutr_val, 2);
	gnutr_free_row_list( list_pcnt_goal, 2);

	gtk_widget_show( glade_xml_get_widget( xml, "nutr_comp_dlg"));
}

/* When the "OK" button is pressed in the nutrient composition dialog,
 * hide the dialog. */
static void
on_ok_button_released( GtkButton *button,
                      gpointer   user_data)
{
	GtkWidget *widget = glade_xml_get_widget( xml, "nutr_comp_dlg");
	gtk_widget_hide( widget);
}

void
gnutr_calc_nutr_comp( char *amount,
                      char *msre_no,
                      char *fd_no)
{
	GList *list_nutr_val = NULL; 
	GList *list_pcnt_goal = NULL;
	GList *list_nutr_no = NULL, *ptr;
	char **elm;
	char *nutr_no;

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

	/* get the full list of nutrient numbers. */
	list_nutr_no = gnutr_get_nutr_no_list();

	/* 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_nutr_val = g_list_append( list_nutr_val, (gpointer)elm);
	}

	add_food_to_nutrient_total( &list_nutr_val, amount, msre_no, fd_no);
	list_pcnt_goal = compute_percent_nutrient_goal( list_nutr_val);
	update_nutrient_dialog( list_nutr_val, list_pcnt_goal);

	gnutr_free_row_list( list_nutr_val, 2);
	gnutr_free_row_list( list_pcnt_goal, 2);
}
