// FILE: organism.cpp
//
// CARNIVORE ADDED BY: (your name and email)
//
// CLASSES IMPLEMENTED:
//   Organism, Plant, Animal, Herbivore (see organism.h for documentation)
//
// INVARIANT for the Organism class:
//   1. The member variable size contains the organism's size, in ounces.
//   2. The member variable rate contains the organism's growth rate, in
//      ounces per week.
//
// INVARIANT for the Animal class:
//   1. The member variable need_each_week contains the organism's food
//      requirement, in ounces per week.
//   2. The member variable eaten_this_week contains the number of ounces of
//      food eaten by the organism in the current week.
//
// The Plant and Herbivore classes have no extra member variables, so there
// is no need for an invariant.

#include <cassert>
#include <cstdlib>
#include "organism.h"

using namespace std;

Organism::Organism(double init_size, double init_rate) {
  if (init_size == 0)
    assert(init_rate == 0);
  else
    assert(init_size >= 0);
  size = init_size;
  rate = init_rate;
}

void Organism::alter_size(double amount) {
  size += amount;
  if (size <= 0)
    death( );
}

void Organism::death( ) {
  size = rate = 0;
}

Plant::Plant(double init_size, double init_rate)
    : Organism(init_size, init_rate) {}
void Plant::nibbled_on(double amount) {
  assert(amount >= 0);
  assert(amount <= get_size( ));
  alter_size( -amount);
}

Animal::Animal(double init_size, double init_rate, double init_need)
    : Organism(init_size, init_rate) {
  assert(0 <= init_need);
  need_each_week = init_need;
  eaten_this_week = 0;
}

void Animal::assign_need(double new_need) {
  assert(new_need >= 0);
  need_each_week = new_need;
}

void Animal::eat(double amount) {
  assert(amount >= 0);
  eaten_this_week += amount;
}

void Animal::simulate_week( ) {
  Organism::simulate_week( );
  if (eaten_this_week < need_each_week)
    death( );
  eaten_this_week = 0;
}

double Animal::still_need( ) const {
  if (eaten_this_week >= need_each_week)
    return 0;
  else
    return need_each_week - eaten_this_week;
}

Herbivore::Herbivore(double init_size, double init_rate, double init_need)
  : Animal(init_size, init_rate, init_need) {}
void Herbivore::nibble(Plant& meal) {
  const double PORTION = 0.5;
  const double MAX_FRACTION = 0.1;
  double amount;
  amount = PORTION * meal.get_size( );
  if (amount > MAX_FRACTION * total_need( ))
    amount = MAX_FRACTION * total_need( );
  if (amount > still_need( ))
    amount = still_need( );
  eat(amount);
  meal.nibbled_on(amount);
}