/* rdi.c - computes Recommeded Daily Intakes for various nutrients */

/* Copyright (C) 1999, 2000 Ian Haywood */
/* 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, in the Copying */
/* section of the manual	*/

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

#include "text_util.h"
#include "food.h"
#include "recipe.h"
#include "rdi.h"

#define CHO 2
#define FAT 1
#define ENERGY 9
#define SUGAR 10
#define M_UNSAT_FAT 76
#define P_UNSAT_FAT 77
#define SAT_FAT 52
#define LINO 63 /* (18:2) */
#define A_LINO 64 /* (18:3) */
#define MET 38
#define CYS 39
#define PHE 40
#define TYR 41
#define EN_KCAL 4

#define KJ2KCAL 0.24
#define EN_CHO 4.27 /* calorific value of carbohydrates */
#define EN_FAT 9.0  /* calorific value of fat */

/* RDI for a given age and above */
struct age_data {
	float age; /* -1=end of dataset */
	float man, woman; /* value for each sex */
};

static float lookup( struct age_data *, float age, gboolean gender); 
static float normal_rdi( int i, float age, float weight, 
		gboolean female, gboolean preg, gboolean lac);
static float energy( float, float, gboolean);
static GList *append_to_goal_list( GList *, char *, float);

#define MAX_AGE_DATA 30 /* maximum age_data for a nutrient */

struct rdi {
	int idx; /* the nutr_no in the nutr_def database table. */
	gboolean by_weight; 
	/* FALSE= units per day, TRUE = units per day per kg body weight */
	float pregnancy; /* increments for each of these states */
	float lactation;
	struct age_data data[MAX_AGE_DATA]; /* RDIs by age */
};

/* General RDI tables */
/* Reference: Bender, D.A. and Bender, A.E., `Nutrition: a reference handbook'
 * 1st ed., Oxford University Press, 1997, ISBN 0-19-262368-0. All page numbers
 * are for this book */
/* Unless otherwise stated, American values are chosen where Bender gives
 * a choice, and the lower value is generally chosen where a range is
 * given.
 * BUGS: more data on cholesterol, fatty acids and fibre */

#define NUM_NORMAL_RDIS 32

struct rdi rdi_table[NUM_NORMAL_RDIS]=
{
	{
		203, /* nutr_no. Protein pg 182 */
		TRUE, /* by weight. */
		6, 17.5, /* pregnacy, lactation (EU values). */
		{{0.3, 1.85, 1.85}, /* {age, man, woman} (end of set: age = -1). */
		{0.58, 1.65, 1.65}, {0.83, 1.48, 1.48},
		{1.0, 1.26, 1.26}, {1.5, 1.17, 1.17}, {2, 1.13, 1.13}, 
		{3, 1.09, 1.09}, {4, 1.06, 1.06}, {5, 1.02, 1.02}, 
		{6, 1.01, 1.01}, {9, 0.99, 0.99}, {10, 0.99, 1}, 
		{11, 0.98, 0.98}, {12, 1, 0.96}, {13, 0.97, 0.94},
		{14, 0.96, 0.90}, {15, 0.92, 0.87}, {16, 0.90, 0.83},
		{17, 0.86, 0.80}, {18, 0.75, 0.75}, {-1, 0, 0}}
	},
	/* NOTE: Fats defined by special algorithm */
	{
		291, /* Fibre	pg 117 */
		FALSE,
		0, 0,
		{{18, 30, 30}, {-1, 0, 0}} /* Paediatric value not available */
	},
	/* Metals */
	{
		301, /* Ca pg 63 */
		FALSE,
		400, 400,
		{{0, 400, 400}, {0.5, 600, 600}, {1, 800, 800}, {11, 1200, 1200},
		 {25, 800, 800}, {-1, 0, 0}}
	},
	{ 
		303, /* Fe, pg 63 */
		FALSE,
		15, 0,
		{{0, 6, 6}, {0.5, 10, 10}, {11, 12, 15}, {19, 10, 15}, 
		 {51, 10, 10}, {-1, 0, 0}}
	},
	{
		304, /* Mg, pg 63*/
		FALSE,
		40, 75,
		{{0, 40, 40}, {0.5, 60, 60}, {1, 80, 80}, {4, 120, 120}, 
		 {7, 170, 170}, {11, 270, 280}, {15, 400, 300}, {19, 350, 280},
		 {-1, 0, 0}}
	},
	{ 
		305, /* P, pg 63 */
		FALSE,
		400, 400,
		{{0, 300, 300}, {0.5, 500, 500}, {1, 800, 800}, {11, 1200, 1200},
		 {25, 800, 800}, {-1, 0, 0}} 
	},
	{
		306, /* K , pg 430*/
		FALSE,
		0, 0,
		{{0, 500, 500}, {0.5, 700, 700}, {1, 1000, 1000}, {2, 1400, 1400},
		 {6, 1600, 1600}, {10, 2000, 2000}, {-1, 0, 0}}
	},
	{
		307, /* Na pg 430 */
		FALSE,
		0, 0,
		{{0, 120, 120}, {0.5, 200, 200}, {1, 225, 225}, {2, 300, 300},
		 {6, 400, 400}, {10, 500, 500}, {-1, 0, 0}}
	},
	{
		309, /* Zn, pg 435 */
		FALSE,
		3, 7,
		{{0, 5, 5}, {1, 10, 10}, {11, 15, 12}, {-1, 0, 0}}
	},
	{
		312, /* Cu, pg 431 */
		FALSE,
		0, 0.3, /* UK value */
		/* back to US */
		{{0, 0.4, 0.4}, {0.5, 0.6}, {1, 0.7, 0.7}, {4, 1.0, 1.0}, 
		 {11, 1.5, 1.5}, {-1, 0, 0}}
	},
	{
		315, /* Mn pg 435*/
		FALSE,
		0,0,
		/* UK values */
		{{0, 0.3, 0.3}, {0.5, 0.6, 0.6}, {1, 0.7, 0.7}, {4, 1.0, 1.0},
		 {11, 1.5, 1.5}, {-1, 0, 0}}
	},
	{ 
		317, /* Se pg 63*/
		FALSE,
		10, 20,
		{{0, 10, 10}, {0.5, 15, 15}, {1, 20, 20}, {7, 30, 30}, 
		 {11, 40, 45}, {15, 50, 50}, {19, 70, 55}, {-1, 0, 0}}
	},

	/* Vitamins */

	{
		392, /* Vitamin A, pg 63 */
		FALSE,
		0, 500,
		{{0, 375, 365}, {1, 400, 400}, {4, 500, 500}, {7, 700, 700},
		 {11, 1000, 800}, {-1, 0, 0}}
	},
	{
		394, /* Vitamin E, pg 63 */
		FALSE,
		2, 2,
		{{0, 3, 3}, {0.5, 4, 4}, {1, 6, 6}, {4, 7, 7}, {11, 10, 8},
		 {-1, 0, 0}}
	},
	{
		401, /* Vitamin C, pg 63 */
		FALSE,
		10, 35,
		{{0, 30, 30}, {0.5, 35, 35}, {1, 40, 40}, {4, 45, 45}, 
		 {11, 50, 50}, {15, 60, 60}, {-1, 0, 0}}
	},
	{
		404, /* Thiamine, pg 63 */
		FALSE,
		0.4, 0.5,
		{{0, 0.3, 0.3}, {0.5, 0.4, 0.4}, {1, 0.7, 0.7}, {4, 0.9, 0.9},
		 {7, 1.0, 1.0}, {11, 1.3, 1.1}, {15, 1.5, 1.1}, {51, 1.2, 1.1},
		 {-1, 0, 0}}
	},
	{
		405, /* riboflavin, pg 63 */
		FALSE,
		0.3, 0.5,
		{{0, 0.4, 0.4}, {0.5, 0.5, 0.5}, {1, 0.8, 0.8}, {4, 1.1, 1.1},
		 {7, 1.2, 1.2}, {11, 1.5, 1.3}, {15, 1.8, 1.3}, {19, 1.7, 1.3},
		 {51, 1.4, 1.3}, {-1, 0, 0}}
	},
	{
		406, /* niacin, pg 63 */
		FALSE,
		2, 5,
		{{0, 5, 5}, {0.5, 6, 6}, {1, 9, 9}, {4, 12, 12}, {7, 13, 13},
		 {11, 17, 15}, {15, 20, 15}, {19, 19, 15}, {51, 15, 13},
		 {-1, 0, 0}}
	},
	{
		410, /* pantotheic acid, pg 392 */
		FALSE,
		0, 0,
		{{0, 2, 2}, {0.5, 3, 3}, {7, 4, 4}, {-1, 0, 0}}
	},
	{
		415, /* vitamin B-6, pg 63 */
		FALSE,
		0.6, 0.5,
		{{0, 0.3, 0.3}, {0.5, 0.5, 0.5}, {1, 0.7, 0.7}, {4, 1.1, 1.1},
		 {7, 1.4, 1.4}, {11, 1.7, 1.4}, {15, 2.0, 1.5}, {19, 2.0, 1.6},
		 {-1, 0, 0}}
	},
	{
		417, /* folate, pg 63 */
		FALSE,
		220, 100,
		{{0, 25, 25}, {0.5, 35, 35}, {1, 50, 50}, {4, 75, 75}, 
		 {7, 100, 100}, {11, 150, 150}, {15, 200, 180}, {-1, 0, 0}}
	},
	{
		418, /* vitamin B-12, pg 63 */
		FALSE,
		0.2, 0.6,
		{{0, 0.3, 0.3}, {0.5, 0.5, 0.5}, {1, 0.7, 0.7}, {4, 1, 1}, 
		 {7, 1.4, 1.4}, {11, 2.0, 2.0}, {-1, 0, 0}}
	},
							 
	/* The essential amino acids. Paediatric values are provided for
	 * the ages 3-4 months, 2 years and 10-12 years. I have 
	 * extended these to age-ranges: 3 mths up, 2yrs up, 10 yrs up
	 * and adult. All pg 183 (adults), 184 (children) */
	{
		501, /* Trp */
		TRUE,
		0, 0,
		{{0.25, 17, 17}, {2, 12.5, 12.5}, {10, 3.5, 3.5}, {-1, 0, 0}}
	},
	{
		502, /* Thr */
		TRUE,
		0, 0,
		{{0.25, 87, 87}, {2, 37, 37}, {10, 28, 28}, {18, 7, 7}, 
		 {-1, 0, 0}}
	},
	{
		503, /* Ile */
		TRUE,
		0, 0,
		{{0.25, 70, 70}, {2, 31, 31}, {10, 28, 28}, {18, 10, 10}, 
		 {-1, 0, 0}}
	},
	{
		504, /* Leu */
		TRUE,
		0, 0,
		{{0.25, 161, 161}, {2, 73, 73}, {10, 44, 44}, {18, 14, 14},
		 {-1, 0, 0}}
	},
	{
		505, /* Lys */
		TRUE,
		0, 0,
		{{0.25, 103, 103}, {2, 64, 64}, {10, 44, 44}, {18, 12, 12},
		 {-1, 0, 0}}
	},
	{
		506, /* Cys + Met */
		TRUE,
		0, 0,
		{{0.25, 58, 58}, {2, 27, 27}, {10, 22, 22}, {18, 13, 13},
		 {-1, 0, 0}}
	},
	{
		508, /* Phe + Tyr */
		TRUE,
		0, 0,
		{{0.25, 125, 125}, {2, 69, 69}, {10, 22, 22}, {18, 14, 14},
		 {-1, 0, 0}}
	},
	{
		510, /* Val */
		TRUE,
		0, 0,
		{{0.25, 93, 93}, {2, 38, 38}, {10, 25, 25}, {18, 10, 10},
		 {-1, 0, 0}}
	},
	{
		512, /* His */
		TRUE,
		0, 0, 
		{{0, 8, 8}, {-1, 0, 0}}
		/* WARNING: Histidine is essential for children, in quantities
		 * greater than adults, but I can't get appropriate values */
	},
	{
		601, /* Cholesterol (pg140) */
		FALSE,
		0, 0,
		{{18, 300, 300}, {-1, 0, 0}}
	}
};


/* returns value for the given age on a table (array of age_data). 
 * 0 = no valid entry */
static float 
lookup( struct age_data *data, 
        float age, 
        gboolean gender)

{
	float entry = 0;
	float *dum;
	
	dum = &entry;
	while (data->age != -1) {
		if (age >= data->age) {
			if (gender)
				*dum = data->woman;
			else
				*dum = data->man;
		}
		data++;
	}
	data--;
	return *dum;
}

/* returns RDI for which a struct rdi is defined */
/* RDIs with special algorithms (no struct rdi defined) */
static float 
normal_rdi( int i, 
            float age, 
            float weight, 
            gboolean female,
            gboolean preg, 
            gboolean lac)
{
	float r;

	r = lookup (rdi_table[i].data, age, female);
	if (rdi_table[i].by_weight) {
		r *= weight;
		/* for proteins convert from milligrams to grams. */
		if ( i >= 22 && i <= 30) r /= 1000.0;
	}
	if (preg) {
		r += rdi_table[i].pregnancy;
	}
	if (lac) {
		r += rdi_table[i].lactation;
	}

	return r;
}


/* returns recommended energy intake for the user */		
static float 
energy( float age, 
        float weight, 
        gboolean female)
{

	/* energy consumption variees according to activity. Values
		 presented here represent a "sedentary lifestyle" ;-) */

	struct age_data child[10] =
		/* age : boys : girls */
	{ 
		{0, 410, 410}, /* NOTE kJ per kg body weight */ 
		{1, 414, 431}, /* defined up to age 10 */
		{2, 414, 406}, /* pg 89 */
		{3, 393, 377}, 
		{4, 377, 364},
		{5, 364, 352},
		{6, 352, 331},
		{7, 331, 301},
		{8, 306, 276},
		{9, 285, 247}
	};
	
/* for adults, much more interesting. First BMR (basal metabolic rate)
	 is computed using the equation A (weight) + B, where A and B are
	 dependent upon the age and sex. (pg 82) The BMR is converted into energy
	 requirements by a fourth variable C, also age- and sex-dependent (pg 89)*/
	
	struct age_data A[5]=
		
		/* age: men : women */
	{
		{10, 0.0732, 0.0510},
		{18, 0.0640, 0.0615},
		{30, 0.0485, 0.0364},
		{60, 0.0565, 0.0439},
		{-1, 0, 0}
	};
	
	struct age_data B[5]=
		
		/* age : men :women */
		
	{
		{10, 2.72, 3.12},
		{18, 2.84, 2.08},
		{30, 3.67, 3.47},
		{60, 2.04, 2.49},
		{-1, 0, 0}
	};
	
	struct age_data C[11]=
		
		/* age:men:women */
		
	{
		{10, 1.74, 1.59},
		{11, 1.67, 1.55},
		{12, 1.61, 1.51},
		{13, 1.56, 1.47},
		{14, 1.49, 1.46},
		{15, 1.44, 1.47},
		{16, 1.40, 1.48},
		{17, 1.40, 1.50},
		{18, 1.41, 1.42},
		{60, 1.40, 1.40},
		{-1, 0, 0}
	};
	
	static float BMR;
	static float e;
	
	if (age < 10)
		e = lookup (child, age, female);
	else {
			BMR = ( lookup( A, age, female) * weight)
						+ lookup( B, age, female);
			e = BMR * lookup( C, age, female);
	}
	e *= 1000; /* tables are in MJ, we want kJ */
	return e;
}

static GList *
append_to_goal_list( GList *list, char *msre_no, float goal_val)
{
	char **elm;

	elm = (char **)g_malloc( 2 * sizeof(char *));
	elm[0] = g_strdup( msre_no);
	elm[1] = g_strdup( ftoa( goal_val));

	list = g_list_append( list, (gpointer)elm);

	return list;
}

 
GList *
rdi_calc_rdi( int age, 
              int weight, 
              gboolean female, 
              gboolean preg, 
              gboolean lac)
{
	float enrg;
	GList *list = NULL;
	int i;

	enrg = energy( age, weight, female);

	/* compute the enery dependent nutrient goal values. */
	/* energy in kj. */
	list = append_to_goal_list( list, "268", enrg);
	/* energy in kcal. */
	enrg *= KJ2KCAL;
	list = append_to_goal_list( list, "208", enrg);
	/* sugar. */
	list = append_to_goal_list( list, "269", 0.1 * enrg/ EN_CHO);
	/* total fat. */
	list = append_to_goal_list( list, "204", 0.3 * enrg/ EN_FAT);
	/* saturated fat. */
	list = append_to_goal_list( list, "606", 0.1 * enrg/ EN_FAT);
	/* monounsaturated fat. */
	list = append_to_goal_list( list, "645", 0.1 * enrg/ EN_FAT);
	/* polyunsaturated fat. */
	list = append_to_goal_list( list, "646", 0.1 * enrg/ EN_FAT);
	/* linoleic acid 1% (18:2) (UK value, pg133)*/
	list = append_to_goal_list( list, "618", 0.01 * enrg/ EN_FAT);
	/* alpha-linolenic acid 0.2% (18:3) (UK value, pg133) */
	list = append_to_goal_list( list, "619", 0.002 * enrg/ EN_FAT);
	/* carbohydrate, 53% of total energy intake (pg 122), as grams. */
	list = append_to_goal_list( list, "205", 0.53 * enrg/ EN_CHO);
	
	/* compute the nutrients that are age, weight, or sex dependent. */
	for (i = 0; i<NUM_NORMAL_RDIS; i++) {
		list = append_to_goal_list( list, itoa( rdi_table[i].idx),
			normal_rdi( i, age, weight, female, preg, lac));
	}

	/* Cys and Met, can be interconverted by hepatic
	 * enzymes, so RDIs are issued for the total.*/
	/* meth and cyst. */
	list = append_to_goal_list( list, "506", 
			0.5 * normal_rdi( 27, age, weight, female, preg, lac));
	list = append_to_goal_list( list, "507", 
			0.5 * normal_rdi( 27, age, weight, female, preg, lac));
	
	/* phen and tyro. */
	list = append_to_goal_list( list, "508", 
			0.5 * normal_rdi( 28, age, weight, female, preg, lac));
	list = append_to_goal_list( list, "509", 
			0.5 * normal_rdi( 28, age, weight, female, preg, lac));


	return list;
}
