/*  addfood.c */

/*
    NUT nutrition software 
    Copyright (C) 1996-2008 by Jim Jozwiak.

    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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "addfood.h"
#include "meal.h"
#include "options.h"
#include "util.h"
#include "db.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

float GRquantamatrix[DV_COUNT][2 * DV_COUNT];
float GRnutslots[DV_COUNT];
int GRfoodslots[2 * DV_COUNT];
int GRsaveslots[2 * DV_COUNT];
int GRfoodlevel[2 * DV_COUNT];
int GRlowestlevelchange[2 * DV_COUNT];
int GRgroupsremaining[2 * DV_COUNT][DV_COUNT];
int GRtotalquanta;
int GRfoodcount;
int GRnutcount;
float GRbestvariance;

void add_foods(int label)
{
struct food *food_ptr = &food_root;
char key[121];
char buff[128];
char *token;
int c, modfood, junk, likeit = 'n', dupname = 0, resname = 0;
float ratio, one = 1, servings = 0, savegrams, savewater;
key[0] = '\0';
key_clean();
while (likeit == 'N' || likeit == 'n')
 {
 dupname = 0; resname = 0;
 if (! label) header("NUT:  Add a Recipe");
 if (label) header("NUT:  Add a Labeled Food");
 spacer(-1);
 if (! label) printf("Type name of new recipe (or <enter> to quit):  ");
 if (  label) printf("Type labeled food name (or <enter> to quit):  ");
 get_string(food_work.name,60);
 if (strcmp(food_work.name,"") == 0) return;
 junk = strlen(food_work.name);
 for (c = 0 ; c < junk ; c++) food_work.name[c] = toupper(food_work.name[c]);
 for (c = junk ; c >= 0 ; c--)
  {
  if (food_work.name[c] == ' ') food_work.name[c] = '\0';
  else break;
  }
 food_ptr = &food_root;
 while (food_ptr->next != NULL)
  {
  food_ptr = food_ptr->next;
  if (food_work.name[0] == food_ptr->name[0]) if (strcmp(food_work.name,food_ptr->name) == 0) dupname = 1;
  }
 if (strncmp(food_work.name,"THEUSUAL",8) == 0) resname = 1;
 if ( ! dupname || resname)
  {
  if (! label) header("NUT:  Add a Recipe");
  if (  label) header("NUT:  Add a Labeled Food");
  spacer(0);
  printf("Recipe name is \"%-s.\"\n",food_work.name);
  printf("Is this correct?  (y/n):  ");
  likeit = get_char();
  }
 if (dupname)
  {
  if (! label) header("NUT:  Add a Recipe");
  if (  label) header("NUT:  Add a Labeled Food");
  spacer(0);
  printf("\"%-s\"\n",food_work.name);
  printf("is a duplicate name.  Press <enter> to continue...");
  junk = get_char();
  likeit = 'n';
  }
 if (resname)
  {
  if (! label) header("NUT:  Add a Recipe");
  if (  label) header("NUT:  Add a Labeled Food");
  spacer(0);
  printf("\"%-s\"\n",food_work.name);
  printf("is a reserved name.  Press <enter> to continue...");
  junk = get_char();
  likeit = 'n';
  }
 }
if (strcmp(food_work.name,"") == 0) return;
if (  label)
 {
 header("NUT:  Add a Labeled Food");
 printf("\n    Now select the ingredients in the order they appear on the ingredients");
 printf("\n    statement.  Don't worry if you can't find some of the ingredients, but");
 printf("\n    for all of them that you do find, keep the order the same.");
 printf("\n");
 printf("\n    Here are some additional commands that facilitate the creation of");
 printf("\n    the ordered ingredients statement.  (The symbol \"#\" stands for a number");
 printf("\n    representing an ingredient in the list):");
 printf("\n");
 printf("\n         #         Delete this ingredient");
 printf("\n         #m#       Move this ingredient to the new position");
 printf("\n         #(        Begin here a group that constitutes a single ingredient");
 printf("\n         #)        End here a group that constitutes a single ingredient");
 printf("\n         #!        Remove a group indicator from this ingredient");
 printf("\n");
 spacer(14);
 printf("\nPress <enter> to continue...");
 junk = get_int();
 }
for ( ; ; )
 {
 for ( ; ; )
  {
  if (recipe_root.next != NULL)
   {
   if (! label) header("NUT:  Add a Recipe");
   if (  label) header("NUT:  Add a Labeled Food");
   if (! recipe_show(0,label?1:0))
    {
    printf("\nPress <enter> to continue...");
    junk = get_int();
    }
   else
    {
    printf("\nEnter number to delete or food name (or <enter> to quit):  ");
    get_string(key,60);
    if (strcmp(key,"") == 0)
     {
     if (! label)
      {
      header("NUT:  Add a Recipe");
      recipe_show(0,0);
      printf("\nDo you want to save recipe in the food database?  (y/n):  ");
      junk = get_char();
      if (junk == 'n' || junk == 'N')
       {
       token = NULL;
       while (recipe_root.next != NULL) modify_recipe_food(1,token);
       return;
       }
      }
     junk = 'z';
     while (junk != 'Y' && junk != 'y' && junk != 'N' && junk != 'n')
      {
      if (! label) header("NUT:  Add a Recipe");
      if (  label) header("NUT:  Add a Labeled Food");
      recipe_show(0,label?1:0);
      if (! label) printf("\nDo you want to add more foods to recipe?  (y/n):  ");
      if (  label) printf("\nDo you want to add more ingredients?  (y/n):  ");
      junk = get_char();
      }
     if (junk == 'y' || junk == 'Y') continue;
     clear_work();
     if (label) add_labels();
     food_ptr = &recipe_root;
     while (food_ptr->next != NULL && ! label)
      {
      food_ptr = food_ptr->next;
      for (c = 0; c < NUTRIENT_COUNT; c++) food_work.nutrient[c] += (food_ptr->nutrient[c] * food_ptr->grams / 100); 
      food_work.grams += food_ptr->grams;
      }
     token = NULL;
     while (recipe_root.next != NULL && ! label) modify_recipe_food(1,token);
     if (label) servings = 1;
     for (c = 0; c < NUTRIENT_COUNT; c++) if (food_work.nutrient[c] < 0) food_work.nutrient[c] = NoData; 
     while (servings <= 0)
      {
      header("NUT:  Add a Recipe");
      new_recipe_show();
      printf("\nHow many servings does this recipe make?  ");
      get_string(key,60);
      servings = atof(key);
      if (servings <= 0) continue;
      header("NUT:  Add a Recipe");
      new_recipe_show();
      printf("\nIs \"%-s\" the correct number of servings for this recipe?  (y/n)  ",key);
      junk = get_char();
      if (junk == 'N' || junk == 'n') servings = 0; 
      }
     for (c = 0; c < NUTRIENT_COUNT; c++) food_work.nutrient[c] /= servings; 
     food_work.grams /= servings;
     junk = 'n';
     while (junk == 'n')
      {
      new_recipe_show();
      printf("\nPlease enter serving unit (cup, tbsp, piece...):  ");
      get_string(food_work.unit,50); 
      new_recipe_show();
      printf("\nIs \"%-s\" correct?  (y/n)  ",food_work.unit);
      junk = get_char();
      if (junk == 'N') junk = 'n';
      }
     junk = 'n';
     while (junk == 'n')
      {
      new_recipe_show();
      printf("\nPlease enter number of serving units:  ");
      food_work.qty = get_float(); 
      if (food_work.qty <= 0) food_work.qty = 1;
      new_recipe_show();
      printf("\nIs \"%-1.2f\" the correct number of serving units?  (y/n)  ",food_work.qty);
      junk = get_char();
      if (junk == 'N') junk = 'n';
      }
     if (! label)
      {
      new_recipe_show();
      printf("\nWould you like to change weight of serving?  (y/n):  ");
      junk = get_char();
      if (junk == 'y' || junk == 'Y')
       {
       savegrams = food_work.grams;
       savewater = food_work.nutrient[WATER];
       junk = 'n';
       while (junk == 'n' || junk == 'N')
        {
        food_work.grams = savegrams;
        food_work.nutrient[WATER] = savewater;
        new_recipe_show();
        printf("\nType new weight of serving {#g for grams, #o for oz}:  ");
        get_recipe_qty(&food_work.grams);
        food_work.nutrient[WATER] -= savegrams - food_work.grams;
        new_recipe_show();
        printf("\nIs weight now correct?  (y/n):  ");
        junk = get_char();
        }
       }
      }
     if ((new_food = malloc(sizeof(struct food))) == NULL)
      {
      printf("We are out of memory.  Bummer.\n");
      abort();
      } 
     for (c = 0; c < NUTRIENT_COUNT; c++) food_work.nutrient[c] /= (food_work.grams / 100); 
     memcpy(new_food,&food_work,sizeof(struct food));
     for (c = 0; c < NUTRIENT_COUNT; c++) food_work.nutrient[c] *= (food_work.grams / 100); 
     new_food->ndb_no = options.next_recipe++;
     write_OPTIONS();
     reindex_meals(modify_food_index(order_new_food(),new_food));
     write_meal_db();
     write_theusual_db();
     write_food_db();
     write_recipe(new_food);
     new_recipe_show();
     printf("\nRecipe saved in food database.  Press <enter> to continue...  ");
     junk = get_int();
     food_work.name[0] = '\0';
     food_work.unit[0] = '\0';
     food_work.qty = 1;
     return;
     }
    }
   }
  modfood = atoi(key);
  strncpy(buff,key,60);
  token = strtok(buff, ", ");
  token = strtok(NULL,", ");
  if (strcmp(key,"") == 0 || modfood == 0) break;
  if (! label) modify_recipe_food(modfood,token);
  if (  label) modify_label_food(modfood,key);
  strcpy(key,"");
  }
 ratio = -383838;
 while (ratio == -383838)
  {
  key_put(key);
  if (! label) food_ptr = food_choice("NUT:  Add a Recipe", 0);
  if (  label) food_ptr = food_choice("NUT:  Add a Labeled Food", 0);
  if (food_ptr == (struct food *) -1) return;
  if (food_ptr == (struct food *)  0) key_clean();
  if (food_ptr == (struct food *)  0) break;
  if (! label) header("NUT:  Add a Recipe");
  if (  label) header("NUT:  Add a Labeled Food");
  food_show(food_ptr, &one);
  if (! label) get_qty(&ratio, &(food_ptr->grams),&(food_ptr->nutrient[ENERC_KCAL]));
  if (  label) get_select(&ratio);
  if (ratio == -383838)
   {
   key_take();
   strcpy(key,key_take());
   }
  }
 if (ratio == 0)
  {
  key_clean();
  strcpy(key,"");
  }
 if (ratio != -383838)
  {
  key_clean();
  strcpy(key,"");
  if ((new_recipe = malloc(sizeof(struct food))) == NULL)
   {
   printf("We are out of memory.  Bummer.\n");
   abort();
   } 
  memcpy(new_recipe,food_ptr,sizeof(struct food));
  new_recipe->grams *= ratio;
  if (label) new_recipe->refuse = 0;
  order_new_recipe();
  }
 }
}

void new_recipe_show()
{
float one = 1;
char buffer[100];
header("NUT:  Add a Recipe");
printf("%-60s   Grams:  %7.2f\n",food_work.name,food_work.grams);
printf("Serving:  %-50s   Ounces: %7.2f\n",format_serving(buffer,&one,&food_work),food_work.grams / GRAMS_IN_OUNCE);
printf("          %-50s   Water:   %6.2f%%\n"," ",100 * food_work.nutrient[WATER] / food_work.grams);
printf("Percentages of \"Daily Values\" in this serving:                 Refuse:  %3d%%\n\n",food_work.refuse);
food_display(stdout);
spacer(21);
}

void get_recipe_qty(float *result)
{
char buff[128];
fgets(buff,128,stdin);
if (strchr(buff,'g') != NULL) *result = (float) atof(buff);
else if (strchr(buff,'o') != NULL) *result = (float) atof(buff) * GRAMS_IN_OUNCE;
else *result = atof(buff);
}

int recipe_show(int trace, int level)
{
struct food *recipe_ptr = &recipe_root;
int i = 0, count = 0, cumlevel = 0, trimlength = 60;
char nametrim[61];
printf("Recipe Name:  %-s\n\n",food_work.name);
while (recipe_ptr->next != NULL)
 {
 recipe_ptr = recipe_ptr->next;
 count++;
 if (! level)
  {
  if (options.grams) printf("%2d. %-60s    %9.1f g\n",count,recipe_ptr->name,recipe_ptr->grams);
  if (!options.grams) printf("%2d. %-60s    %8.1f oz\n",count,recipe_ptr->name,recipe_ptr->grams/GRAMS_IN_OUNCE);
  }
 if (level)
  {
  printf("%2d. ",count);
  for (i = 0; i < cumlevel; i++) printf(" ");
  if (recipe_ptr->refuse > 0) for (i = 0; i < recipe_ptr->refuse; i++) printf("(");
  if (recipe_ptr->refuse > 0) cumlevel += recipe_ptr->refuse;
  for (i = 0; i < 60; i++) nametrim[i] = ' ';
  trimlength = 60 - cumlevel;
  for (i = 0; i < trimlength; i++) 
   {
   if (recipe_ptr->name[i] == '\0') break;
   nametrim[i] = recipe_ptr->name[i];
   }
  if (recipe_ptr->refuse < 0 && cumlevel + recipe_ptr->refuse < 0) recipe_ptr->refuse = 0 - cumlevel; 
  if (recipe_ptr->refuse < 0) 
   {
   for (i = 0; i < trimlength; i++) 
    {
    if (recipe_ptr->name[i] == '\0') break;
    nametrim[i] = recipe_ptr->name[i];
    }
   for (i = recipe_ptr->refuse; i < 0; i++) nametrim[trimlength+i] = ')';
   cumlevel += recipe_ptr->refuse;
   }
/*
  if (recipe_ptr->next == NULL && cumlevel > 0) for (i = cumlevel - recipe_ptr->refuse; i > 0; i--) nametrim[trimlength-i] = ')';
*/
  nametrim[trimlength] = '\0';
  printf("%-s",nametrim);
  if (options.grams && ! trace) printf("    %9.1f g\n",recipe_ptr->grams);
  if (options.grams && trace && recipe_ptr->grams >  0) printf("    %9.2f g\n",recipe_ptr->grams);
  if (options.grams && trace && recipe_ptr->grams == 0) printf("      %9s\n","trace");
  if (!options.grams) printf("    %8.1f oz\n",recipe_ptr->grams/GRAMS_IN_OUNCE);
  }
 }
if (count == 0) 
 {
 printf("\n\n\nNo foods have yet been recorded for this recipe.\n");
 spacer(6);
 return 0;
 } 
spacer(count + 2);
return 1;
}

void add_labels()
{
int i;
for (i = 0; i < NUTRIENT_COUNT; i++) food_work.nutrient[i] = -1;
header("NUT:  Add a Labeled Food");
printf("\n\n    Now enter the information from the nutrition statement on the package.");
printf("\n    Use grams rather than percentages whenever possible and ignore zero");
printf("\n    amounts, as only greater-than-zero Daily Value nutrients are used.");
printf("\n    There will be another opportunity later to review and correct the");
printf("\n    nutrient values after the recipe is determined, so wait until then");
printf("\n    to add vitamins and minerals if the food is heavily fortified and");
printf("\n    the individual vitamins are not part of the food ingredients.");
printf("\n\n    If you enter nutrients as a percentage of the Daily Value, this is");
printf("\n    the standard USA Daily Value for 2000-calorie diets, and not any");
printf("\n    customized values you may have set.");
printf("\n\n    Each time you quit from a nutrient screen, you will be able to select");
printf("\n    another group of nutrients to add.  Leave blank all the nutrients you");
printf("\n    don't find on the nutrition statement.");
spacer(16);
printf("\nPress <enter> to continue...");
i = get_int();
screen_subvert_label(1);
for ( ; ; )
 {
 header("NUT:  Add a Labeled Food");
 printf("\n\n    Now enter the serving size in grams.  If the food is liquid and measured");
 printf("\n    by volume, use the approximations 1 mL = 1 gram (1 liter = 1000 grams)");
 printf("\n    and 1 fluid ounce = 30 grams (1 quart = 960 grams).");
 spacer(4);
 printf("\nEnter weight of serving in grams:  ");
 get_nut_value(&food_work.grams);
 if (food_work.grams > 0) break;
 }
guess_recipe();
for (i = 0; i < NUTRIENT_COUNT; i++) if (test_for_negative_zero(&food_work.nutrient[i])) food_work.nutrient[i] = -1;
printf("\nDo you want to again view or edit the nutrient values (y/n)?  ");
i = get_char();
if (i != 'N' && i != 'n') screen_subvert_label(0);
for (i = 0; i < NUTRIENT_COUNT; i++) if (food_work.nutrient[i] == -1) food_work.nutrient[i] = NoData;
compute_derived_fields(&food_work);
}

void screen_subvert_label(int dv)
{
int screen_choice, savescreen, i;
char yn;
savescreen = options.screen;
options.screen = 1;
set_nut_table();
for ( ; ; )
 {
 yn = '\0';
 header("NUT:  Add a Labeled Food");
 printf("\n      Change group of nutrients to view?\n\n\n");
for (i = 0; i < (dv ? dv + 1 : MaxScreen); i++) printf("                    %d  --  %s\n\n",i+1,ScreenTitle[i]);
spacer((dv ? dv  + 1: MaxScreen) * 2 + 4);
printf("\nEnter your choice (just <enter> to quit):  ");
screen_choice = get_int();
if (screen_choice < 1 || screen_choice > (dv ? dv + 1 : MaxScreen))
{
while (yn != 'Y' && yn != 'y' && yn != 'N' && yn != 'n')
{
header("NUT:  Add a Labeled Food");
spacer(-1);
printf("Do you want to further edit the nutrient values (y/n)?  ");
yn = get_char();
if (yn == 'N' || yn == 'n') 
{
options.screen = savescreen;
return;
}
if (yn == 'Y' || yn == 'y') break;
}
}
if (yn == 'Y' || yn == 'y') continue;
options.screen = screen_choice - 1;
set_nut_table();
}
}

void set_nut_table()
{
float tempfloat;
int nutnum;
for ( ; ; )
{
header("NUT:  Add a Labeled Food");
label_nut_list(-1);
printf("\nType number of nutrient to set (or <enter> to quit):  ");
nutnum = get_int();
if (nutnum < 1 || nutnum > *ScreenMap[options.screen]) return;
nutnum = ScreenMap[options.screen][nutnum];

header("NUT:  Add a Labeled Food");
label_nut_list(nutnum);
printf("\nEnter value for %s or <enter> to blank value:  ",Nutrient[nutnum]);
get_nut_value(&tempfloat);
if (options.screen == 0 && (nutnum != FAMS && nutnum != FAPU && nutnum != OMEGA6 && nutnum != LA && nutnum != AA && nutnum != OMEGA3 && nutnum != ALA && nutnum != EPA && nutnum != DHA && nutnum != CHO_NONFIB)) tempfloat = tempfloat / 100 * DVBase[nutnum];
food_work.nutrient[nutnum] = tempfloat;
if (nutnum == VITE) food_work.nutrient[TOCPHA] = tempfloat * 2/3;
if (nutnum == LA  ) food_work.nutrient[F18D2] = tempfloat;
if (nutnum == AA  ) food_work.nutrient[F20D4] = tempfloat;
if (nutnum == ALA ) food_work.nutrient[F18D3] = tempfloat;
if (nutnum == EPA ) food_work.nutrient[F20D5] = tempfloat;
if (nutnum == DHA ) food_work.nutrient[F22D6] = tempfloat;
if (nutnum == ENERC_KCAL ) food_work.nutrient[ENERC_KJ] = tempfloat * 4.1835;
if (nutnum == TOCPHA) food_work.nutrient[VITE] = tempfloat * 3/2;
if (nutnum == F18D2 ) food_work.nutrient[LA] = tempfloat;
if (nutnum == F20D4 ) food_work.nutrient[AA] = tempfloat;
if (nutnum == F18D3 ) food_work.nutrient[ALA] = tempfloat;
if (nutnum == F20D5 ) food_work.nutrient[EPA] = tempfloat;
if (nutnum == F22D6 ) food_work.nutrient[DHA] = tempfloat;
if (nutnum == ENERC_KJ ) food_work.nutrient[ENERC_KCAL] = tempfloat / 4.1835;
}
}

void label_nut_list(int selection)
{
int count;
int intdiv = *ScreenMap[options.screen] / 2 + (*ScreenMap[options.screen] % 2 == 0 ? 0 : 1);
for ( count = 0 ; count <= *ScreenMap[options.screen] / 2 - (*ScreenMap[options.screen] % 2 == 0 ? 1 : 0); count++)
{
if (options.screen == 0)
{
if (food_work.nutrient[ScreenMap[options.screen][count+1]] >= 0 && ScreenMap[options.screen][count+1] != FAMS && ScreenMap[options.screen][count+1] != FAPU && ScreenMap[options.screen][count+1] != OMEGA6 && ScreenMap[options.screen][count+1] != LA && ScreenMap[options.screen][count+1] != AA && ScreenMap[options.screen][count+1] != OMEGA3 && ScreenMap[options.screen][count+1] != ALA && ScreenMap[options.screen][count+1] != EPA && ScreenMap[options.screen][count+1] != DHA && ScreenMap[options.screen][count+1] != CHO_NONFIB) printf("     %2d%s %-14s%7.0f%% DV   ",count+1,ScreenMap[options.screen][count+1]==selection ? "*" : ".",Nutrient[ScreenMap[options.screen][count+1]],food_work.nutrient[ScreenMap[options.screen][count+1]]/DVBase[ScreenMap[options.screen][count+1]]*100);
if (food_work.nutrient[ScreenMap[options.screen][count+1]] <  0 && ScreenMap[options.screen][count+1] != FAMS && ScreenMap[options.screen][count+1] != FAPU && ScreenMap[options.screen][count+1] != OMEGA6 && ScreenMap[options.screen][count+1] != LA && ScreenMap[options.screen][count+1] != AA && ScreenMap[options.screen][count+1] != OMEGA3 && ScreenMap[options.screen][count+1] != ALA && ScreenMap[options.screen][count+1] != EPA && ScreenMap[options.screen][count+1] != DHA && ScreenMap[options.screen][count+1] != CHO_NONFIB) printf("     %2d%s %-14s%7s%% DV   ",count+1,ScreenMap[options.screen][count+1]==selection ? "*" : ".",Nutrient[ScreenMap[options.screen][count+1]],"       ");
if (food_work.nutrient[ScreenMap[options.screen][count+1]] >= 0 && (ScreenMap[options.screen][count+1] == FAMS || ScreenMap[options.screen][count+1] == FAPU || ScreenMap[options.screen][count+1] == OMEGA6 || ScreenMap[options.screen][count+1] == LA || ScreenMap[options.screen][count+1] == AA || ScreenMap[options.screen][count+1] == OMEGA3 || ScreenMap[options.screen][count+1] == ALA || ScreenMap[options.screen][count+1] == EPA || ScreenMap[options.screen][count+1] == DHA || ScreenMap[options.screen][count+1] == CHO_NONFIB)) printf("     %2d%s %-14s %7.2f %-3s  ",count+1,ScreenMap[options.screen][count+1]==selection ? "*" : ".",Nutrient[ScreenMap[options.screen][count+1]],food_work.nutrient[ScreenMap[options.screen][count+1]],Unit[ScreenMap[options.screen][count+1]]);
if (food_work.nutrient[ScreenMap[options.screen][count+1]] <  0 && (ScreenMap[options.screen][count+1] == FAMS || ScreenMap[options.screen][count+1] == FAPU || ScreenMap[options.screen][count+1] == OMEGA6 || ScreenMap[options.screen][count+1] == LA || ScreenMap[options.screen][count+1] == AA || ScreenMap[options.screen][count+1] == OMEGA3 || ScreenMap[options.screen][count+1] == ALA || ScreenMap[options.screen][count+1] == EPA || ScreenMap[options.screen][count+1] == DHA || ScreenMap[options.screen][count+1] == CHO_NONFIB)) printf("     %2d%s %-14s %7s %-3s  ",count+1,ScreenMap[options.screen][count+1]==selection ? "*" : ".",Nutrient[ScreenMap[options.screen][count+1]],"       ",Unit[ScreenMap[options.screen][count+1]]);
if (count+intdiv < *ScreenMap[options.screen] && food_work.nutrient[ScreenMap[options.screen][count+1+intdiv]] >= 0) printf("    %2d%s %-14s%7.0f%% DV\n",count+1+intdiv,ScreenMap[options.screen][count+1+intdiv]==selection ? "*" : ".",Nutrient[ScreenMap[options.screen][count+1+intdiv]],food_work.nutrient[ScreenMap[options.screen][count+1+intdiv]]/DVBase[ScreenMap[options.screen][count+1+intdiv]]*100);
if (count+intdiv < *ScreenMap[options.screen] && food_work.nutrient[ScreenMap[options.screen][count+1+intdiv]] <  0) printf("    %2d%s %-14s%7s%% DV\n",count+1+intdiv,ScreenMap[options.screen][count+1+intdiv]==selection ? "*" : ".",Nutrient[ScreenMap[options.screen][count+1+intdiv]],"       ");
}
else
{
if (food_work.nutrient[ScreenMap[options.screen][count+1]] >= 0) printf("     %2d%s %-14s %7.2f %-3s  ",count+1,ScreenMap[options.screen][count+1]==selection ? "*" : ".",Nutrient[ScreenMap[options.screen][count+1]],food_work.nutrient[ScreenMap[options.screen][count+1]],Unit[ScreenMap[options.screen][count+1]]);
if (food_work.nutrient[ScreenMap[options.screen][count+1]] <  0) printf("     %2d%s %-14s %7s %-3s  ",count+1,ScreenMap[options.screen][count+1]==selection ? "*" : ".",Nutrient[ScreenMap[options.screen][count+1]],"       ",Unit[ScreenMap[options.screen][count+1]]);
if (count+intdiv < *ScreenMap[options.screen] && food_work.nutrient[ScreenMap[options.screen][count+1+intdiv]] >= 0) printf("    %2d%s %-14s %7.2f %-3s    \n",count+1+intdiv,ScreenMap[options.screen][count+1+intdiv]==selection ? "*" : ".",Nutrient[ScreenMap[options.screen][count+1+intdiv]],food_work.nutrient[ScreenMap[options.screen][count+1+intdiv]],Unit[ScreenMap[options.screen][count+1+intdiv]]);
if (count+intdiv < *ScreenMap[options.screen] && food_work.nutrient[ScreenMap[options.screen][count+1+intdiv]] <  0) printf("    %2d%s %-14s %7s %-3s    \n",count+1+intdiv,ScreenMap[options.screen][count+1+intdiv]==selection ? "*" : ".",Nutrient[ScreenMap[options.screen][count+1+intdiv]],"       ",Unit[ScreenMap[options.screen][count+1+intdiv]]);
}
if (count+intdiv >= *ScreenMap[options.screen]) printf("\n");
}
spacer(count);
}

void get_nut_value(float *dest)
{
char buff[128];
fgets(buff,128,stdin);
if (strcmp(buff,"\n") == 0) *dest = -1;
else *dest = (atof(buff));
}

void guess_recipe()
{
int i, j, savegramoption, cumlevel = 0, ok = 0;
int gt0dvnuts[DV_COUNT];
int remaininggroupsassigner[DV_COUNT];
int levelcumbuf[DV_COUNT] = {0,0,0,0,0,0,0,0,0,0,0,0};
int levelminbuf[DV_COUNT] = {0,0,0,0,0,0,0,0,0,0,0,0};
int levelmaxbuf[DV_COUNT] = {0,0,0,0,0,0,0,0,0,0,0,0};
int parentheses[2 * DV_COUNT];
float gt0dvnutfactors[DV_COUNT];
char *token;

struct food *recipe_ptr;
struct food *foodlist[2 * DV_COUNT];

GRtotalquanta = 100;
GRfoodcount = 0;
GRnutcount = 0;
for (i = 0; i < DV_COUNT; i++) remaininggroupsassigner[i] = 0;

for (j = 0; j < DV_COUNT; j++) if (food_work.nutrient[DVMap[j]] > 0) gt0dvnuts[GRnutcount++] = DVMap[j];

recipe_ptr = &recipe_root;
while (recipe_ptr->next != NULL && GRfoodcount < 2 * DV_COUNT)
{
parentheses[GRfoodcount] = recipe_ptr->next->refuse;
foodlist[GRfoodcount++] = recipe_ptr->next;
recipe_ptr = recipe_ptr->next;
}

for (j = 0; j < GRnutcount; j++) gt0dvnutfactors[j] = food_work.grams / (100 * food_work.nutrient[gt0dvnuts[j]]);
for (j = 0; j < GRnutcount; j++) for (i = 0; i < GRfoodcount; i++) GRquantamatrix[j][i] = foodlist[i]->nutrient[gt0dvnuts[j]] * gt0dvnutfactors[j];

for (i = GRfoodcount - 1; i >= 0; i--) cumlevel += parentheses[i];
if (cumlevel != 0) parentheses[GRfoodcount - 1] -= cumlevel;
cumlevel = 1;

while (1)
 {
 for (i = GRfoodcount - 1; i >= 0; i--) if (parentheses[i] > -2 && parentheses[i] < 2) ok = 1;
 if (ok) break;
 for (i = GRfoodcount - 1; i >= 0; i--)
  {
  if (parentheses[i] < -1) parentheses[i]++;
  if (parentheses[i] > -1) parentheses[i]--;
  }
 }

for (i = 0; i < GRfoodcount; i++)
 {
 if (parentheses[i] > 1)
  {
  for (j = i + 1; j < GRfoodcount; j++) if (parentheses[j] < 0) break;
  while (parentheses[i] > 1 && parentheses[j] < -1)
   {
   parentheses[i]--;
   parentheses[j]++;
   }
  }
 }

for (i = GRfoodcount - 1; i >= 0; i--)
 {
 if (parentheses[i] < 0)
  {
  cumlevel -= parentheses[i];
  GRfoodlevel[i] = cumlevel;
  }
 else if (parentheses[i] == 0)
  {
  GRfoodlevel[i] = cumlevel;
  }
 else if (parentheses[i] > 0)
  {
  GRfoodlevel[i] = cumlevel;
  cumlevel -= parentheses[i];
  }
 }

for (i = GRfoodcount - 1; i >= 0; i--)
 {
 if (parentheses[i] < 0)
  {
  for (j = GRfoodlevel[i] + parentheses[i]; j < GRfoodlevel[i]; j++) remaininggroupsassigner[j]++;
  for (j = GRfoodlevel[i]; j < DV_COUNT; j++) remaininggroupsassigner[j] = 1;
  }
 else remaininggroupsassigner[GRfoodlevel[i]]++;
 for (j = GRfoodlevel[i]; j >= 0; j--) GRgroupsremaining[i][j] = remaininggroupsassigner[j];
 }

GRlowestlevelchange[0] = 0;
for (i = GRfoodcount - 1; i > 0; i--)
 {
 for (j = 0; j <= GRfoodlevel[i]; j++)
  {
  if (GRgroupsremaining[i-1][j] == GRgroupsremaining[i][j]) continue;
  GRlowestlevelchange[i] = j;
  break;
  }
 }

GRbestvariance = 1e38;

for (i = 0; i < GRfoodcount; i++) GRfoodslots[i] = 0;
if (GRfoodcount > 10) GRtotalquanta = 40;
levelcumbuf[0] = levelminbuf[0] = GRtotalquanta;

solve_it(0,levelcumbuf,levelminbuf,levelmaxbuf);

for (i = 0; i < GRfoodcount; i++) foodlist[i]->grams = GRsaveslots[i] * food_work.grams / GRtotalquanta;

header("NUT:  Add a Labeled Food");
savegramoption = options.grams;
options.grams = 1;
recipe_show(1,1);
options.grams = savegramoption;
options.screen = 0;
for (i = 0; i < NUTRIENT_COUNT; i++) recipe_root.nutrient[i] = NoData;
recipe_ptr = &recipe_root;
while (recipe_ptr->next != NULL)
 {
 recipe_ptr = recipe_ptr->next;
 for (i = 0; i < NUTRIENT_COUNT; i++) recipe_root.nutrient[i] += (recipe_ptr->nutrient[i] * recipe_ptr->grams / 100);
 }
for (i = 0; i < NUTRIENT_COUNT; i++)
 {
 if (food_work.nutrient[i] > 0) recipe_root.nutrient[i] = food_work.nutrient[i];
 food_work.nutrient[i] = recipe_root.nutrient[i];
 recipe_root.nutrient[i] = 0;
 }
compute_derived_fields(&food_work);
token = NULL;
while (recipe_root.next != NULL) modify_recipe_food(1,token);
}

void solve_it(int foodno, int *oldlevelcumbuf, int *oldlevelminbuf, int *oldlevelmaxbuf)
{
float newvariance;
int max, min, x, i, j, prevquanta, availquanta, localavailquanta;
int levelcumbuf[DV_COUNT], levelminbuf[DV_COUNT], levelmaxbuf[DV_COUNT];
int progressfactor = 0;

for (i = 0; i <= GRfoodlevel[foodno]; i++) levelcumbuf[i] = oldlevelcumbuf[i];
for (i = 0; i <= GRfoodlevel[foodno]; i++) levelminbuf[i] = oldlevelminbuf[i];
for (i = 0; i <= GRfoodlevel[foodno]; i++) levelmaxbuf[i] = oldlevelmaxbuf[i];
if (GRlowestlevelchange[foodno] < GRfoodlevel[foodno])
 {
 levelmaxbuf[GRlowestlevelchange[foodno]] = oldlevelcumbuf[GRlowestlevelchange[foodno]];
 levelmaxbuf[0] = GRlowestlevelchange[foodno];
 }
if (foodno == 0)
 {
 for (i = 0;  i <= GRfoodlevel[foodno]; i++) levelmaxbuf[i] = oldlevelcumbuf[0];
 levelmaxbuf[0] = 1;
 }

prevquanta = levelcumbuf[GRlowestlevelchange[foodno]];
for (i = GRlowestlevelchange[foodno]; i <= GRfoodlevel[foodno]; i++) levelcumbuf[i] = 0;
availquanta = levelminbuf[0] - levelcumbuf[0];

localavailquanta = max = availquanta < prevquanta ? availquanta : prevquanta;
if (GRlowestlevelchange[foodno] == GRfoodlevel[foodno])
 {
 for (i = 1; i <= levelmaxbuf[0]; i++) if (localavailquanta > levelmaxbuf[i] - levelcumbuf[i]) localavailquanta = levelmaxbuf[i] - levelcumbuf[i];
 max = localavailquanta;
 }

if (GRlowestlevelchange[foodno] <= 1)
 {
 levelminbuf[1] = (availquanta / GRgroupsremaining[foodno][1]) + ((availquanta % GRgroupsremaining[foodno][1]) > 0 ? 1 : 0);
 for (i = 2; i <= GRfoodlevel[foodno]; i++) levelminbuf[i] = (levelminbuf[i-1] / GRgroupsremaining[foodno][i]) + ((levelminbuf[i-1] % GRgroupsremaining[foodno][i]) > 0 ? 1 : 0);
 }

else
 {
 levelminbuf[GRlowestlevelchange[foodno]] = ((levelminbuf[GRlowestlevelchange[foodno]-1] - levelcumbuf[GRlowestlevelchange[foodno]-1]) / GRgroupsremaining[foodno][GRlowestlevelchange[foodno]]) + (((levelminbuf[GRlowestlevelchange[foodno]-1] - levelcumbuf[GRlowestlevelchange[foodno]-1]) % GRgroupsremaining[foodno][GRlowestlevelchange[foodno]]) > 0 ? 1 : 0);
 for (i = GRlowestlevelchange[foodno] + 1; i <= GRfoodlevel[foodno]; i++) levelminbuf[i] = (levelminbuf[i-1] / GRgroupsremaining[foodno][i]) + ((levelminbuf[i-1] % GRgroupsremaining[foodno][i]) > 0 ? 1 : 0);
 }

min = levelminbuf[GRfoodlevel[foodno]];
if (min < 0) min = 0;

if (!foodno)
 {
 progressfactor = min + 79;
 printf("\nStarting calculation...\n");
 }

for (i = 0; i <= GRfoodlevel[foodno]; i++) levelcumbuf[i] += max + 1;

for (x = max; x >= min; x--)
 {
 newvariance = 0;
 for (i = 0; i <= GRfoodlevel[foodno]; i++) levelcumbuf[i]--;
 if (!foodno)
  {
  for (i = 0; i < progressfactor - x; i++) printf("=");
  if (i > 0) printf(">\n");
  }
 GRfoodslots[foodno] = x;
 for (i = foodno + 1; i < GRfoodcount; i++) GRfoodslots[i] = 0;
 if (availquanta - x > 0) solve_it(foodno + 1, levelcumbuf, levelminbuf, levelmaxbuf);
 else
  {
  for (j = 0; j < GRnutcount; j++)
   {
   GRnutslots[j] = 0 - GRtotalquanta;
   for (i = 0; i <= foodno; i++) GRnutslots[j] += GRfoodslots[i] * GRquantamatrix[j][i];
   newvariance += GRnutslots[j] * GRnutslots[j];
   }
  if (newvariance < GRbestvariance)
   {
   GRbestvariance = newvariance;
   for (i = 0; i < GRfoodcount; i++) GRsaveslots[i] = GRfoodslots[i];
   }
  }
 }
}

void modify_servings()
{
char buf[1000];
char *result[61];
char servdisp[120];
char junk;
struct food *food_ptr = NULL;
float ratio = -1, one = 1;
int resultcount, i, choice;
int *resultparm = &resultcount;
key_clean();
for ( ; ; )
 {
 if (ratio <= 0)
  {
  food_ptr = food_choice("NUT:  Modify Serving Sizes", 0);
  if (food_ptr == (struct food *) -1) return;
  if (food_ptr == (struct food *) 0) key_clean();
  if (food_ptr == (struct food *) 0) continue;
  }
 header("NUT:  Modify Serving Sizes");
 if (ratio > 0) food_show(food_ptr,&ratio);
 else food_show(food_ptr,&one);
 get_select(&ratio);
 if (ratio == -383838) key_take();
 else
  {
  resultcount = 0;
  read_weightlib(food_ptr->ndb_no,buf,sizeof(buf),result,resultparm);
  header("NUT:  Modify Serving Sizes");
  printf("%-60s   Grams  %8.2f\n",food_ptr->name,food_ptr->grams);
  printf("Serving:  %-50s   Ounces: %7.2f\n\n",format_serving(servdisp,&one,food_ptr),food_ptr->grams / GRAMS_IN_OUNCE);
  for (i = 1; i <= resultcount / 3; i++)
   {
   strcpy(servdisp,result[3*i-3]);
   strcat(servdisp," ");
   strcat(servdisp,result[3*i-2]);
   servdisp[60] = '\0';
   printf("%2d.  %-60s %6.1f grams\n",i,servdisp,atof(result[3*i-1]));
   }
  printf("%2d.  I will enter my own serving size in grams or ounces.\n",i);
  spacer(i+2);
  printf("Select your choice, or just press <enter> to retain current serving:  ");
  choice = get_int();
  if (choice < 1 || choice > i)
   {
   ratio = 0;
   key_clean();
   continue;
   }
  if (choice != i)
   {
   food_ptr->qty = atof(result[3*choice-3]);
   strncpy(food_ptr->unit,result[3*choice-2],50);
   food_ptr->grams = atof(result[3*choice-1]);
   }
  if (choice == i)
   {
   junk = 'n';
   header("NUT:  Modify Serving Sizes");
   printf("%-60s   Grams  %8s\n",food_ptr->name,"?");
   printf("Serving:  %-50s   Ounces  %7s\n\n"," ","?");
   spacer(3);
   while (junk == 'n' || junk == 'N')
    {
    printf("\nType new weight of serving {#g for grams, #o for oz}:  ");
    get_recipe_qty(&food_work.grams);
    header("NUT:  Modify Serving Sizes");
    printf("%-60s   Grams  %8.2f\n",food_ptr->name,food_work.grams);
    printf("Serving:  %-50s   Ounces  %7.2f\n\n"," ",food_work.grams / GRAMS_IN_OUNCE);
    spacer(3);
    printf("\nIs weight now correct?  (y/n):  ");
    junk = get_char();
    if (food_work.grams == 0) junk = 'n';
    if (junk == 'n' && junk == 'y' && junk == 'N' && junk == 'Y') junk = 'n';
    if (junk == 'n')
     {
     header("NUT:  Modify Serving Sizes");
     printf("%-60s   Grams  %8s\n",food_ptr->name,"?");
     printf("Serving:  %-50s   Ounces  %7s\n\n"," ","?");
     spacer(3);
     }
    }
   header("NUT:  Modify Serving Sizes");
   printf("%-60s   Grams  %8.2f\n",food_ptr->name,food_work.grams);
   printf("Serving:  %-50s   Ounces  %7.2f\n\n","?",food_work.grams / GRAMS_IN_OUNCE);
   spacer(3);
   junk = 'n';
   while (junk == 'n')
    {
    printf("\nPlease enter serving unit (cup, tbsp, piece...):  ");
    get_string(food_work.unit,50); 
    header("NUT:  Modify Serving Sizes");
    printf("%-60s   Grams  %8.2f\n",food_ptr->name,food_work.grams);
    printf("Serving:  %-50s   Ounces  %7.2f\n\n","?",food_work.grams / GRAMS_IN_OUNCE);
    spacer(3);
    printf("\nIs \"%-s\" correct?  (y/n)  ",food_work.unit);
    junk = get_char();
    if (junk == 'N') junk = 'n';
    }
   junk = 'n';
   while (junk == 'n')
    {
    header("NUT:  Modify Serving Sizes");
    printf("%-60s   Grams  %8.2f\n",food_ptr->name,food_work.grams);
    printf("Serving:  %-50s   Ounces  %7.2f\n\n",format_serving(servdisp,&one,&food_work),food_work.grams / GRAMS_IN_OUNCE);
    spacer(3);
    printf("\nPlease enter number of serving units:  ");
    food_work.qty = get_float(); 
    if (food_work.qty <= 0) food_work.qty = 1;
    header("NUT:  Modify Serving Sizes");
    printf("%-60s   Grams  %8.2f\n",food_ptr->name,food_work.grams);
    printf("Serving:  %-50s   Ounces  %7.2f\n\n",format_serving(servdisp,&one,&food_work),food_work.grams / GRAMS_IN_OUNCE);
    spacer(3);
    printf("\nIs \"%-1.2f\" the correct number of serving units?  (y/n)  ",food_work.qty);
    junk = get_char();
    if (junk == 'N') junk = 'n';
    }
   food_ptr->grams = food_work.grams;
   food_ptr->qty   = food_work.qty  ;
   strncpy(food_ptr->unit,food_work.unit,50);
   }
  ratio = 1;
  write_food_db();
  write_serving(food_ptr);
  header("NUT:  Modify Serving Sizes");
  food_show(food_ptr,&ratio);
  printf("\nServing size saved in food database.  Press <enter> to continue...  ");
  junk = get_int();
  food_work.name[0] = '\0';
  food_work.unit[0] = '\0';
  ratio = 0;
  key_clean();
  continue;
  }
 }
}
