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

struct meal theusual_root, *new_theusual, meal_root, *new_meal;

void order_new_meal()
{
struct meal *meal_ptr = &meal_root;
while (meal_ptr->next != NULL &&
       strcmp(new_meal->meal_date, meal_ptr->next->meal_date) < 0)
            meal_ptr = meal_ptr->next;
while (meal_ptr->next != NULL &&
       strcmp(new_meal->meal_date, meal_ptr->next->meal_date) <= 0 &&
       new_meal->meal < meal_ptr->next->meal)
            meal_ptr = meal_ptr->next;
while (meal_ptr->next != NULL &&
       strcmp(new_meal->meal_date, meal_ptr->next->meal_date) <= 0 &&
       new_meal->meal <= meal_ptr->next->meal &&
       strcmp(FoodIndex[new_meal->food_no]->name,FoodIndex[meal_ptr->next->food_no]->name) > 0) 
            meal_ptr = meal_ptr->next;
new_meal->next = meal_ptr->next;
meal_ptr->next = new_meal;
}

void order_new_theusual()
{
struct meal *theusual_ptr = &theusual_root;
while (theusual_ptr->next != NULL &&
       strcmp(new_theusual->meal_date, theusual_ptr->next->meal_date) > 0)
            theusual_ptr = theusual_ptr->next;
while (theusual_ptr->next != NULL &&
       strcmp(new_theusual->meal_date, theusual_ptr->next->meal_date) >= 0 &&
       strcmp(FoodIndex[new_theusual->food_no]->name,FoodIndex[theusual_ptr->next->food_no]->name) > 0) 
            theusual_ptr = theusual_ptr->next;
new_theusual->next = theusual_ptr->next;
theusual_ptr->next = new_theusual;
}

struct meal *theusual_choice(char *screentitle, char *key)
{
struct meal *theusual_ptr;
char meal_date[9];
char new_meal_date[9];
int c;
for (c = 8; c < 17; c++) meal_date[c-8] = key[c];
for ( ; ; )
 {
 theusual_ptr = theusual_find(meal_date);
 if (theusual_ptr != NULL) return theusual_ptr;
 header(screentitle);
 spacer(theusual_list()-1);
 printf("\nType \"The Usual\" Identifier (max. 8 characters):  ");
 get_string(new_meal_date,8);
 if (strlen(new_meal_date) == 0) return (struct meal *) 0;
 for (c = 0 ; c < 9 ; c++) meal_date[c] = toupper(new_meal_date[c]);
 theusual_ptr = theusual_find(meal_date);
 if (theusual_ptr != NULL) return theusual_ptr;
 }
}

int meal_show(char *meal_date, int meal)
{
struct meal *meal_ptr = &meal_root;
char namebuf[60];
int count = 0;
printf("Meal Date:  %s                                            Meal Number:  %d\n\n",meal_date,meal);
for ( ; ; )
 {
 if (strcmp(meal_date,meal_ptr->meal_date) == 0 && meal == meal_ptr->meal) 
  {
  count++;
  strncpy(namebuf,FoodIndex[meal_ptr->food_no]->name,59); namebuf[59] = '\0';
  if (options.grams) printf("%2d. %-59s     %9.1f g\n",count,namebuf,meal_ptr->grams);
  if (!options.grams) printf("%2d. %-59s     %8.1f oz\n",count,namebuf,meal_ptr->grams/GRAMS_IN_OUNCE);
  }
 if (meal_ptr->next == NULL) break;
 meal_ptr = meal_ptr->next;
 }
if (count == 0) 
 {
 printf("\n\n\nNo foods have yet been recorded for this meal.\n");
 spacer(6);
 return 0;
 } 
spacer(count + 2);
return 1;
}

int theusual_show(char *meal_date)
{
struct meal *theusual_ptr = &theusual_root;
char namebuf[60];
int count = 0;
printf("\"The Usual\" Identifier:  %s\n\n",meal_date);
for ( ; ; )
 {
 if (strcmp(meal_date,theusual_ptr->meal_date) == 0) 
  {
  count++;
  strncpy(namebuf,FoodIndex[theusual_ptr->food_no]->name,59); namebuf[59] = '\0';
  if (options.grams) printf("%2d. %-59s     %9.1f g\n",count,namebuf,theusual_ptr->grams);
  if (!options.grams) printf("%2d. %-59s     %8.1f oz\n",count,namebuf,theusual_ptr->grams/GRAMS_IN_OUNCE);
  }
 if (theusual_ptr->next == NULL) break;
 theusual_ptr = theusual_ptr->next;
 }
if (count == 0) 
 {
 printf("\n\n\nNo foods have yet been recorded for this customary meal.\n");
 spacer(6);
 return 0;
 } 
spacer(count + 2);
return 1;
}

struct meal *meal_find(char *meal_date,int meal)
{
struct meal *meal_ptr = &meal_root;
while (meal_ptr->next != NULL)
 {
 meal_ptr = meal_ptr->next;
 if (strcmp(meal_ptr->meal_date,meal_date) == 0 && meal_ptr->meal == meal) return meal_ptr;
 }
return NULL;
}

struct meal *theusual_find(char *meal_date)
{
struct meal *theusual_ptr = &theusual_root;
while (theusual_ptr->next != NULL)
 {
 theusual_ptr = theusual_ptr->next;
 if (strcmp(theusual_ptr->meal_date,meal_date) == 0) return theusual_ptr;
 }
return NULL;
}

struct meal *prev_meal(struct meal *meal_after)
{
struct meal *meal_ptr = &meal_root;
while (meal_ptr->next != meal_after) meal_ptr = meal_ptr->next;
return meal_ptr;
}

void modify_meal(char *meal_date, int meal, int num, char *qty)
{
struct meal *m, *meal_ptr = &meal_root;
int count = 0;
float newqty;
while (meal_ptr->next != NULL)
 {
 if (strcmp(meal_date,meal_ptr->next->meal_date) == 0 && meal == meal_ptr->next->meal) 
  {
  count++;
  if (count == num)
   {
   newqty = evaluate_qty(food_number(meal_ptr->next->food_no),qty);
   if (newqty == 0)
    {
    m = meal_ptr->next;
    meal_ptr->next = meal_ptr->next->next;
    free(m);
    }
   else meal_ptr->next->grams = newqty;
   return;
   }
  }
 meal_ptr = meal_ptr->next;
 }
}

void delete_meal_with_ptr(struct meal *target)
{
struct meal *meal_ptr = &meal_root;
while (meal_ptr->next != NULL)
 {
 if (meal_ptr->next == target)
  {
  meal_ptr->next = target->next;
  free(target);
  return;
  }
 meal_ptr = meal_ptr->next;
 }
}

void delete_theusual_with_ptr(struct meal *target)
{
struct meal *meal_ptr = &theusual_root;
while (meal_ptr->next != NULL)
 {
 if (meal_ptr->next == target)
  {
  meal_ptr->next = target->next;
  free(target);
  return;
  }
 meal_ptr = meal_ptr->next;
 }
}

void modify_theusual(char *meal_date, int num, char *qty)
{
struct meal *m, *theusual_ptr = &theusual_root;
int count = 0;
float newqty;
while (theusual_ptr->next != NULL)
 {
 if (strcmp(meal_date,theusual_ptr->next->meal_date) == 0) 
  {
  count++;
  if (count == num)
   {
   newqty = evaluate_qty(food_number(theusual_ptr->next->food_no),qty);
   if (newqty == 0)
    {
    m = theusual_ptr->next;
    theusual_ptr->next = theusual_ptr->next->next;
    free(m);
    }
   else theusual_ptr->next->grams = newqty;
   return;
   }
  }
 theusual_ptr = theusual_ptr->next;
 }
}

int meal_count(struct meal *meal_ptr)
{
int count = 0;
char lastdate[9];
int lastmeal;
if (meal_ptr->next == NULL) return 0;
meal_ptr = meal_ptr->next;
strcpy(lastdate,meal_ptr->meal_date);
lastmeal = meal_ptr->meal;
count = 1;
while ((meal_ptr = meal_ptr->next))
 {
 if (strcmp(lastdate,meal_ptr->meal_date) != 0 || lastmeal != meal_ptr->meal)
  {
  count++;
  strcpy(lastdate,meal_ptr->meal_date);
  lastmeal = meal_ptr->meal;
  }
 } 
return count;
}

void delete_meals(int keep)
{
struct meal *meal_ptr = &meal_root, *last_meal_ptr = NULL;
int count = 0;
char meal_date[9], meal = 0;
if (keep < 0) return;
strcpy(meal_date,"");
while (count <= keep && meal_ptr->next != NULL)
 {
 last_meal_ptr = meal_ptr;
 meal_ptr = meal_ptr->next;
 if (strcmp(meal_date,meal_ptr->meal_date) != 0  || meal != meal_ptr->meal) 
  {
  count++;
  strcpy(meal_date,meal_ptr->meal_date);
  meal = meal_ptr->meal;
  }
 }  
if (count <= keep) return;
last_meal_ptr->next = NULL;
while (meal_ptr != NULL)
 {
 last_meal_ptr = meal_ptr;
 meal_ptr = meal_ptr->next;
 free(last_meal_ptr);
 }
}

int theusual_list()
{ 
struct meal *theusual_ptr = &theusual_root;
char last_meal_date[9];
int c = 0;
last_meal_date[0] = '\0';
printf("Customary Meals so far:\n\n");
while (theusual_ptr->next != NULL)
 {
 theusual_ptr = theusual_ptr->next;
 if (strcmp(last_meal_date,theusual_ptr->meal_date) == 0) continue;
 strcpy(last_meal_date,theusual_ptr->meal_date);
 printf(" %-8s",theusual_ptr->meal_date);
 c++;
 if (c%7 == 0) printf("\n");
 else printf("  ");
 }
return c/7+3;
}

void meal_list(char *bufptr)
{ 
struct meal *meal_ptr = &meal_root;
int c;
int missing[20];
for (c=0; c<20; c++) missing[c] = 0;
while (meal_ptr->next != NULL && strcmp(bufptr,meal_ptr->meal_date) != 0) meal_ptr = meal_ptr->next;
if (meal_ptr->next == NULL && strcmp(bufptr,meal_ptr->meal_date) != 0)
 {
 for (c = options.mealsperday; c >= 1; c--)
  {
  missing[0]++;
  missing[c] = 1;
  }
 }
else for (c = options.mealsperday; c >= 1; c--)
 {
 if (c != meal_ptr->meal)
  {
  missing[0]++;
  missing[c] = 1;
  }
 while (c == meal_ptr->meal && meal_ptr->next != NULL && strcmp(bufptr,meal_ptr->next->meal_date) == 0) meal_ptr = meal_ptr->next;
 }
if (missing[0] == 0) printf("\n\n\n");
else
 {
 printf("Missing Meals:\n\n");
 for (c=1; c<20; c++) if (missing[c] == 1) printf("%-4d",c);
 printf("\n");
 }
}

void reindex_meals(int foodnum)
{
struct meal *meal_ptr = &meal_root;
while (meal_ptr->next != NULL)
 {
 meal_ptr = meal_ptr->next;
 if (meal_ptr->food_no >= foodnum) meal_ptr->food_no++;
 }
meal_ptr = &theusual_root;
while (meal_ptr->next != NULL)
 {
 meal_ptr = meal_ptr->next;
 if (meal_ptr->food_no >= foodnum) meal_ptr->food_no++;
 }
}

void full_meal_reindexing()
{
struct meal *meal_ptr = &meal_root;
printf("\nStarting to re-index meals...\n");
while (meal_ptr->next != NULL)
 {
 if (meal_ptr->next->ndb_no == 0 && version(0) < 9) meal_ptr->next->ndb_no = 42231;
 meal_ptr->next->food_no = find_ndbno(meal_ptr->next->ndb_no);
 while (meal_ptr->next->food_no == -1)
  {
  delete_meal_with_ptr(meal_ptr->next);
  if (meal_ptr->next == NULL) break;
  if (meal_ptr->next->ndb_no == 0 && version(0) < 9) meal_ptr->next->ndb_no = 42231;
  meal_ptr->next->food_no = find_ndbno(meal_ptr->next->ndb_no);
  }
 if (meal_ptr->next != NULL) meal_ptr = meal_ptr->next;
 }
if (meal_ptr->ndb_no == 0 && version(0) < 9) meal_ptr->ndb_no = 42231;
meal_ptr->food_no = find_ndbno(meal_ptr->ndb_no);
if (meal_ptr->food_no == -1) delete_meal_with_ptr(meal_ptr);

meal_ptr = &theusual_root;
while (meal_ptr->next != NULL)
 {
 if (meal_ptr->next->ndb_no == 0 && version(0) < 9) meal_ptr->next->ndb_no = 42231;
 meal_ptr->next->food_no = find_ndbno(meal_ptr->next->ndb_no);
 while (meal_ptr->next->food_no == -1)
  {
  delete_theusual_with_ptr(meal_ptr->next);
  if (meal_ptr->next == NULL) return;
  if (meal_ptr->next->ndb_no == 0 && version(0) < 9) meal_ptr->next->ndb_no = 42231;
  meal_ptr->next->food_no = find_ndbno(meal_ptr->next->ndb_no);
  }
 if (meal_ptr->next != NULL) meal_ptr = meal_ptr->next;
 }
if (meal_ptr->ndb_no == 0 && version(0) < 9) meal_ptr->ndb_no = 42231;
meal_ptr->food_no = find_ndbno(meal_ptr->ndb_no);
if (meal_ptr->food_no == -1) delete_theusual_with_ptr(meal_ptr);
}

float average_cals(void)
{
struct meal *meal_ptr = &meal_root;
float calsum = 0;
int count;
while (meal_ptr->next != NULL)
 {
 meal_ptr = meal_ptr->next;
 calsum += meal_ptr->grams / 100 * FoodIndex[meal_ptr->food_no]->nutrient[ENERC_KCAL];
 }
count = meal_count(&meal_root);
if (count > 0) return calsum / count * options.mealsperday;
else return 2000;
}
