/* ranking.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 "ranking.h"
#include "food.h"
#include "util.h"
#include "options.h"
#include <stdio.h>
#include <string.h>

int Minut = -1;
int Fdgrp = -1;

void view_nuts(int opt)
{
int num = 0;
char head[80];
for ( ; ; )
 {
 if (opt == 0 && options.screen == 0) header("NUT:  View \"Daily Values\" and Rank Foods by Nutrients per 100 Calories");
 if (opt == 1 && options.screen == 0) header("NUT:  View \"Daily Values\" and Rank Foods by Nutrients per 100 Grams");
 if (opt == 3 && options.screen == 0) header("NUT:  View \"Daily Values\" and Rank Foods by Nutrients per 100 Grams Dry Weight");
 if (opt == 4 && options.screen == 0) header("NUT:  View \"Daily Values\" and Rank Foods by Nutrients per Serving");
 if (opt == 2 && options.screen == 0) header("NUT:  View \"Daily Values\" and Rank Foods by Nutrients per Daily Recorded Meals");
 if (opt == 0 && options.screen > 0) header("NUT:  View Nutrients and Rank Foods by Nutrients per 100 Calories");
 if (opt == 1 && options.screen > 0) header("NUT:  View Nutrients and Rank Foods by Nutrients per 100 Grams");
 if (opt == 3 && options.screen > 0) header("NUT:  View Nutrients and Rank Foods by Nutrients per 100 Grams Dry Weight");
 if (opt == 4 && options.screen > 0) header("NUT:  View Nutrients and Rank Foods by Nutrients per Serving");
 if (opt == 2 && options.screen > 0) header("NUT:  View Nutrients and Rank Foods by Nutrients per Daily Recorded Meals");
 if (opt == 5)
  {
  strcpy(head,"NUT:  Rank Foods by Nutrients per Serving, Minimize ");
  strcat(head,Minut == -1 ? "?" : Nutrient[Minut]);
  header(head);
  nut_list();
  if (Minut == -1)
   {
   printf("\nType number of Nutrient to Minimize (or <enter> to quit):  ");
   num = get_int();
   if (num < 1 || num > *ScreenMap[options.screen]) return;
   Minut = ScreenMap[options.screen][num];
   }
  strcpy(head,"NUT:  Rank Foods by Nutrients per Serving, Minimize ");
  strcat(head,Minut == -1 ? "?" : Nutrient[Minut]);
  header(head);
  }
 if (opt == 6)
  {
  strcpy(head,"NUT:  Rank Foods by Nutrients within ");
  strcat(head,Fdgrp == -1 ? "?" : FdGrp[Fdgrp]);
  header(head);
  fdgrp_list();
  if (Fdgrp == -1)
   {
   printf("\nType number of Food Group (or <enter> to quit):  ");
   num = get_int();
   if (num < 1 || num > MaxFdGrp) return;
   Fdgrp = FdGrpMap[num-1];
   }
  strcpy(head,"NUT:  Rank Foods by Nutrients within ");
  strcat(head,Fdgrp == -1 ? "?" : FdGrp[Fdgrp]);
  header(head);
  }
 nut_list();
 printf("\nType number of Nutrient to see food rankings (or <enter> to quit):  ");
 num = get_int();
 if (num < 1 || num > *ScreenMap[options.screen]) return;
 rank_foods(ScreenMap[options.screen][num],opt);
 }
}

void rank_foods(int num, int opt)
{
int count, max = 0, junk, displayed = 0, meal = 0, meals = 0, maxmeal = options.defanal;
int mincount = 0;
float minavg = 0;
float abacus[MAX_FOOD];
struct food *food_ptr = &food_root;
struct meal *meal_ptr = &meal_root;
char head[80];
char thisname[60], meal_date[9];
float theseounces, days;
if (options.screen == 0) strcpy(head,"NUT:  Foods Ranked by % of ");
else strcpy(head,"NUT:  Foods Ranked by ");
if ( meal_count(options.temp_meal_root) < options.defanal || options.defanal == 0)
 {
 days = (float) meal_count(options.temp_meal_root) / (float) options.mealsperday;
 maxmeal = meal_count(options.temp_meal_root);
 }
else
 {
 days = (float) options.defanal / (float) options.mealsperday;
 maxmeal = options.defanal;
 }
strcat(head,Nutrient[num]);
if (opt == 0 && options.screen == 0) strcat(head," DV per 100 Calories");
if (opt == 1 && options.screen == 0) strcat(head," DV per 100 Grams (3.5 oz)");
if (opt == 4 && options.screen == 0) strcat(head," DV per Serving");
if (opt == 2 && options.screen == 0) strcat(head," DV per Daily Recorded Meals");
if (opt == 3 && options.screen == 0) strcat(head," DV per 100 Grams Dry Weight");
if (opt == 0 && options.screen > 0) strcat(head," per 100 Calories");
if (opt == 1 && options.screen > 0) strcat(head," per 100 Grams (3.5 oz)");
if (opt == 3 && options.screen > 0) strcat(head," per 100 Grams Dry Weight");
if (opt == 4 && options.screen > 0) strcat(head," per Serving");
if (opt == 2 && options.screen > 0) strcat(head," per Daily Recorded Meals");
if (opt == 5) sprintf(head,"%-49s%14s %14s","NUT:  Foods Ranked by Nutrients per Serving:",Nutrient[num],Nutrient[Minut]);
if (opt == 6 && options.screen > 0) sprintf(head,"%-21s %1s %2s %-1s","NUT:  Foods Ranked by",Nutrient[num],"in 100g",FdGrp[Fdgrp]);
if (opt == 6 && options.screen == 0) sprintf(head,"%-21s %1s %2s %-1s","NUT:  Foods Ranked by %DV",Nutrient[num],"in 100g",FdGrp[Fdgrp]);
if (opt == 2 && meal_count(options.temp_meal_root) == 0)
 {
 header(head); 
 printf("These rankings show which foods actually eaten are contributing to the Daily\n");
 printf("Values, but there are no meals in the meal database.\n");
 spacer(2);
 printf("\nPress <enter> to continue...  ");
 junk = get_int();
 return;
 } 
if (num == ENERC_KCAL && opt == 0 && options.screen == 0)
 {
 header(head); 
 printf("These rankings are percentages of the \"Daily Value\" per 100 calories, to\n");
 printf("identify the foods which provide a high daily percentage of a nutrient with\n");
 printf("a low daily percentage of calories.  Thus, calories are not ranked.\n");
 spacer(3);
 printf("\nPress <enter> to continue...  ");
 junk = get_int();
 return;
 } 
if ((num == ENERC_KCAL || num == ENERC_KJ) && opt == 0 && options.screen > 0)
 {
 header(head); 
 printf("These rankings are greatest nutrient values per 100 calories, to identify\n");
 printf("foods which provide a high amount of a nutrient with low energy values.\n");
 printf("Thus energy in kilocalories or kilojoules is not ranked.\n");
 spacer(3);
 printf("\nPress <enter> to continue...  ");
 junk = get_int();
 return;
 } 
if (opt == 3 && num == WATER)
 {
 header(head); 
 printf("These rankings subtract water from the food, so water itself is not ranked.\n");
 spacer(1);
 printf("\nPress <enter> to continue...  ");
 junk = get_int();
 return;
 } 
if (opt == 1) for (count = 0 ; count < FoodCount ; count++)
 {
 food_ptr = food_ptr->next;
 if (options.screen == 0) abacus[count] = 100 * food_ptr->nutrient[num] / DV[num];
 else abacus[count] = food_ptr->nutrient[num];
 }
if (opt == 6) for (count = 0 ; count < FoodCount ; count++)
 {
 food_ptr = food_ptr->next;
 if (options.screen == 0) abacus[count] = 100 * food_ptr->nutrient[num] / DV[num];
 else abacus[count] = food_ptr->nutrient[num];
 if (food_ptr->fdgrp != Fdgrp) abacus[count] = -1;
 }
if (opt == 5)
 {
 for (count = 0 ; count < FoodCount ; count++) 
  {
  food_ptr = food_ptr->next;
  if (! test_for_negative_zero(&food_ptr->nutrient[Minut]))
   {
   minavg += food_ptr->nutrient[Minut];
   mincount++;
   }
  }
 if (mincount > 0) minavg /= mincount;
 food_ptr = &food_root;
 for (count = 0 ; count < FoodCount ; count++) 
  {
  food_ptr = food_ptr->next;
  if (mincount == 0 ||
      test_for_negative_zero(&food_ptr->nutrient[num]) ||
/*
      test_for_negative_zero(&food_ptr->nutrient[Minut]) ||
*/
      Minut == num ||
      (num == VITE && Minut == TOCPHA) || (num == TOCPHA && Minut == VITE) ||
      (num == LA && Minut == F18D2) || (num == F18D2 && Minut == LA) ||
      (num == LA && Minut == F18D2CN6) || (num == F18D2CN6 && Minut == LA) ||
      (num == AA && Minut == F20D4) || (num == F20D4 && Minut == AA) ||
      (num == ALA && Minut == F18D3) || (num == F18D3 && Minut == ALA) ||
      (num == ALA && Minut == F18D3CN3) || (num == F18D3CN3 && Minut == ALA) ||
      (num == EPA && Minut == F20D5) || (num == F20D5 && Minut == EPA) ||
      (num == DHA && Minut == F22D6) || (num == F22D6 && Minut == DHA))
      abacus[count] = 0;
  else abacus[count] = (food_ptr->grams / -100 * food_ptr->nutrient[num]) * ((food_ptr->grams / 100 * food_ptr->nutrient[Minut] / minavg) - 1);
  }
 }
if (opt == 0) for (count = 0 ; count < FoodCount ; count++) 
 {
 food_ptr = food_ptr->next;
 if (food_ptr->nutrient[ENERC_KCAL] > 0)
  {
  if (options.screen == 0) abacus[count] = (10000 * food_ptr->nutrient[num]) / (food_ptr->nutrient[ENERC_KCAL] * DV[num]);
  else abacus[count] = 100 * food_ptr->nutrient[num] / food_ptr->nutrient[ENERC_KCAL];
  }
 else abacus[count] = 0;
 }
if (opt == 4) for (count = 0 ; count < FoodCount ; count++) 
 {
 food_ptr = food_ptr->next;
 if (options.screen == 0) abacus[count] = (food_ptr->grams * food_ptr->nutrient[num]) / DV[num];
 else abacus[count] = food_ptr->grams / 100 * food_ptr->nutrient[num];
 }
if (opt == 3) for (count = 0 ; count < FoodCount ; count++)
 {
 food_ptr = food_ptr->next;
 if (options.screen == 0) abacus[count] = (10000 * food_ptr->nutrient[num]) / ((100 - food_ptr->nutrient[WATER]) * DV[num]);
 if (options.screen > 0)  abacus[count] = (100 * food_ptr->nutrient[num]) / (100 - food_ptr->nutrient[WATER]);
 }
if (opt == 2) 
 {
 for (count = 0 ; count < FoodCount ; count++) abacus[count] = 0;
 meal_ptr = options.temp_meal_root;
 meals = 0;
 while ( meal_ptr->next != NULL && meals <= maxmeal )
  {
  meal_ptr = meal_ptr->next;
  if (strcmp(meal_date,meal_ptr->meal_date) != 0 || meal != meal_ptr->meal)
   {
   strcpy(meal_date,meal_ptr->meal_date);
   meal = meal_ptr->meal;
   meals++;
   }
  if ( meals <= maxmeal ) abacus[meal_ptr->food_no] += meal_ptr->grams;
  }
 for (count = 0 ; count < FoodCount ; count++)
  { 
  abacus[count] /= days;
  if (options.screen == 0) abacus[count] *= FoodIndex[count]->nutrient[num] / DV[num];
  if (options.screen  > 0) abacus[count] *= FoodIndex[count]->nutrient[num];
  }
 }
junk = 'm';
max = max_array(abacus);
while (displayed <= (FoodCount - 20) && (junk == 'm' || junk == 'M'))
 {
 header(head); 
 for (count = 0; count < 20; count++)
  {
  if ( abacus[max] == 0 )
   {
   max = FoodCount + 1;
   displayed = FoodCount + 1;
   spacer(count);
   count = 20;
   printf("\nPress <enter> to quit...  ");
   junk = get_char();
   break;
   }
  food_ptr = food_number(max);
  strncpy(thisname,food_ptr->name,54); thisname[54] = '\0';
  if (opt == 5) strncpy(thisname,food_ptr->name,52); thisname[52] = '\0';
  if (opt == 2 && options.screen == 0)
   {
   theseounces = abacus[max] * DV[num] / ( food_ptr->nutrient[num] * GRAMS_IN_OUNCE);
   if (options.grams) printf("%-54s        %7.1f g %5.0f%%\n",thisname,theseounces*GRAMS_IN_OUNCE,abacus[max]);
   if (!options.grams) printf("%-54s        %6.1f oz %5.0f%%\n",thisname,theseounces,abacus[max]);
   }
  if (opt == 2 && options.screen > 0 && options.screen < 4)
   {
   theseounces = abacus[max] / (food_ptr->nutrient[num] * GRAMS_IN_OUNCE);
   if (options.grams) printf("%-54s %7.1f g %9.1f %-3s\n",thisname,theseounces*GRAMS_IN_OUNCE,abacus[max]/100,Unit[num]);
   if (!options.grams) printf("%-54s %6.1f oz %9.1f %-3s\n",thisname,theseounces,abacus[max]/100,Unit[num]);
   }
  if (opt == 2 && options.screen > 3)
   {
   theseounces = abacus[max] / (food_ptr->nutrient[num] * GRAMS_IN_OUNCE);
   if (options.grams) printf("%-54s %7.1f g %9.2f %-3s\n",thisname,theseounces*GRAMS_IN_OUNCE,abacus[max]/100,Unit[num]);
   if (!options.grams) printf("%-54s %6.1f oz %9.2f %-3s\n",thisname,theseounces,abacus[max]/100,Unit[num]);
   }
  if (opt == 1 && options.screen == 0) printf("%-54s                  %5.0f%%\n",thisname,abacus[max]);
  if (opt == 1 && options.screen > 0 && options.screen < 4) printf("%-54s          %10.1f %-3s\n",thisname,abacus[max],Unit[num]);
  if (opt == 1 && options.screen > 0 && options.screen > 3) printf("%-54s          %10.2f %-3s\n",thisname,abacus[max],Unit[num]);
  if (opt == 6 && options.screen == 0) printf("%-54s                  %5.0f%%\n",thisname,abacus[max]);
  if (opt == 6 && options.screen > 0 && options.screen < 4) printf("%-54s          %10.1f %-3s\n",thisname,abacus[max],Unit[num]);
  if (opt == 6 && options.screen > 0 && options.screen > 3) printf("%-54s          %10.2f %-3s\n",thisname,abacus[max],Unit[num]);
  if (opt == 3 && options.screen == 0 && options.grams) printf("%-54s        %7.1f g %5.0f%%\n",thisname,10000 / (100 - food_ptr->nutrient[WATER]),abacus[max]);
  if (opt == 3 && options.screen == 0 && !options.grams) printf("%-54s        %6.1f oz %5.0f%%\n",thisname,10000 / (100 - food_ptr->nutrient[WATER]) / GRAMS_IN_OUNCE,abacus[max]);
  if (opt == 3 && options.screen > 0 && options.screen < 4 && options.grams) printf("%-54s %7.1f g %9.1f %-3s\n",thisname,10000 / (100 - food_ptr->nutrient[WATER]),abacus[max],Unit[num]);
  if (opt == 3 && options.screen > 0 && options.screen < 4 && !options.grams) printf("%-54s %6.1f oz %9.1f %-3s\n",thisname,10000 / (100 - food_ptr->nutrient[WATER]) / GRAMS_IN_OUNCE,abacus[max],Unit[num]);
  if (opt == 3 && options.screen > 0 && options.screen > 3 && options.grams) printf("%-54s %7.1f g %9.2f %-3s\n",thisname,10000 / (100 - food_ptr->nutrient[WATER]),abacus[max],Unit[num]);
  if (opt == 3 && options.screen > 0 && options.screen > 3 && !options.grams) printf("%-54s %6.1f oz %9.2f %-3s\n",thisname,10000 / (100 - food_ptr->nutrient[WATER]) / GRAMS_IN_OUNCE,abacus[max],Unit[num]);
  if (opt == 0 && options.screen == 0 && options.grams) printf("%-54s        %7.1f g %5.0f%%\n",thisname,10000 / food_ptr->nutrient[ENERC_KCAL],abacus[max]);
  if (opt == 0 && options.screen == 0 && !options.grams) printf("%-54s        %6.1f oz %5.0f%%\n",thisname,10000 / food_ptr->nutrient[ENERC_KCAL] / GRAMS_IN_OUNCE,abacus[max]);
  if (opt == 0 && options.screen > 0 && options.screen < 4 && options.grams) printf("%-54s %7.1f g %9.1f %-3s\n",thisname,10000 / food_ptr->nutrient[ENERC_KCAL],abacus[max],Unit[num]);
  if (opt == 0 && options.screen > 0 && options.screen < 4 && !options.grams) printf("%-54s %6.1f oz %9.1f %-3s\n",thisname,10000 / food_ptr->nutrient[ENERC_KCAL] / GRAMS_IN_OUNCE,abacus[max],Unit[num]);
  if (opt == 0 && options.screen > 0 && options.screen > 3 && options.grams) printf("%-54s %7.1f g %9.2f %-3s\n",thisname,10000 / food_ptr->nutrient[ENERC_KCAL],abacus[max],Unit[num]);
  if (opt == 0 && options.screen > 0 && options.screen > 3 && !options.grams) printf("%-54s %6.1f oz %9.2f %-3s\n",thisname,10000 / food_ptr->nutrient[ENERC_KCAL] / GRAMS_IN_OUNCE,abacus[max],Unit[num]);
  if (opt == 5 && !test_for_negative_zero(&food_ptr->nutrient[Minut])) printf("%-52s %7.1f %-3s    %7.1f %-3s\n",thisname,food_ptr->grams / 100 * food_ptr->nutrient[num],Unit[num],food_ptr->grams / 100 * food_ptr->nutrient[Minut],Unit[Minut]);
  if (opt == 5 && test_for_negative_zero(&food_ptr->nutrient[Minut])) printf("%-52s %7.1f %-3s        (nd)\n",thisname,food_ptr->grams / 100 * food_ptr->nutrient[num],Unit[num]);
  if (opt == 4 && options.screen == 0 && options.grams) printf("%-54s        %7.1f g %5.0f%%\n",thisname,food_ptr->grams,abacus[max]);
  if (opt == 4 && options.screen == 0 && !options.grams) printf("%-54s        %6.1f oz %5.0f%%\n",thisname,food_ptr->grams / GRAMS_IN_OUNCE,abacus[max]);
  if (opt == 4 && options.screen > 0 && options.screen < 4 && options.grams) printf("%-54s %7.1f g %9.1f %-3s\n",thisname,food_ptr->grams,abacus[max],Unit[num]);
  if (opt == 4 && options.screen > 0 && options.screen < 4 && !options.grams) printf("%-54s %6.1f oz %9.1f %-3s\n",thisname,food_ptr->grams / GRAMS_IN_OUNCE,abacus[max],Unit[num]);
  if (opt == 4 && options.screen > 0 && options.screen > 3 && options.grams) printf("%-54s %7.1f g %9.2f %-3s\n",thisname,food_ptr->grams,abacus[max],Unit[num]);
  if (opt == 4 && options.screen > 0 && options.screen > 3 && !options.grams) printf("%-54s %6.1f oz %9.2f %-3s\n",thisname,food_ptr->grams / GRAMS_IN_OUNCE,abacus[max],Unit[num]);
  abacus[max] = 0;
  max = max_array(abacus);
  }
 displayed += 20;
 if ( max < FoodCount && abacus[max] != 0 ) 
  {
  printf("\nPress \"m\" for more (or just <enter> to quit)...  ");
  junk = get_char();
  }
 if ( abacus[max] == 0 && max < FoodCount )
  {
  max = FoodCount + 1;
  spacer(count);
  printf("\nPress <enter> to quit...  ");
  junk = get_char();
  break;
  }
 }
}

int max_array(float abacus[])
{
int count, i = 0;
for ( count = 1 ; count < FoodCount ; count++ ) if (abacus[count] > abacus[i]) i = count;
return i;
}

void nut_list()
{
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)
  {
  printf("     %2d. %-14s %7.1f %-3s  ",count+1,Nutrient[ScreenMap[options.screen][count+1]],DV[ScreenMap[options.screen][count+1]],Unit[ScreenMap[options.screen][count+1]]);
  if (count+intdiv < *ScreenMap[options.screen]) printf("    %2d. %-14s %7.1f %-3s    \n",count+1+intdiv,Nutrient[ScreenMap[options.screen][count+1+intdiv]],DV[ScreenMap[options.screen][count+1+intdiv]],Unit[ScreenMap[options.screen][count+1+intdiv]]);
  }
 else
  {
  printf("             %2d. %-14s %7s ",count+1,Nutrient[ScreenMap[options.screen][count+1]]," ");
  if (count+intdiv < *ScreenMap[options.screen]) printf("          %2d. %-14s\n",count+1+intdiv,Nutrient[ScreenMap[options.screen][count+1+intdiv]]);
  }
 if (count+intdiv >= *ScreenMap[options.screen]) printf("\n");
 }
spacer(count);
}

void fdgrp_list()
{
int count;
int intdiv = MaxFdGrp / 2 + (MaxFdGrp % 2 == 0 ? 0 : 1);
printf("\n\n");
for ( count = 0 ; count <= MaxFdGrp / 2 - (MaxFdGrp % 2 == 0 ? 1 : 0); count++)
 {
 printf("%2d. %-33s %1s ",count+1,FdGrp[FdGrpMap[count]]," ");
 if (count+intdiv < MaxFdGrp) printf("%2d. %-33s\n",count+1+intdiv,FdGrp[FdGrpMap[count+intdiv]]);
 if (count+intdiv >= MaxFdGrp) printf("\n");
 }
spacer(count+2);
}
