
#ifdef GNU
#define std
#include <iostream.h>
#else
#include <iostream>
#endif

#include "Scheduler.h"
#include <algorithm>


Scheduler::Scheduler( )
{
}
  
Scheduler::~Scheduler()
{
}

void 
Scheduler::AddCage( const Cage& cage )
{
  cages_.push_back( cage );
}


//  Schedule an animal, assuming it is not already in the schedule.
//
Scheduler::Status
Scheduler::Schedule( Animal& animal )
{
  bool in_weight_range=false;
  bool in_schedule=false;

  //  Take the first empty cage the animal fits in.  Keep track of
  //  whether it fit into any cage.
  //
  for ( int c=0; c<cages_.size() && !in_schedule; c++ ) {
    if ( cages_[c].InWeightRange( animal ) ) {
      in_weight_range=true;
      if ( cages_[c].Schedule( animal ) ) {
	in_schedule = true;
	animal.AssignCage( c );
      }
    }
  }

  // Save the animal on the scheduled list or the waiting list, as
  // appropriate.
  //
  if ( in_schedule ) {
     //MODIFIED FOR HashTable
     //scheduled_.push_back( animal );
     scheduled_.Insert(animal.Name(), animal);
    return SCHEDULED;
  }
  else if ( in_weight_range ) {
     //MODIFIED FOR HashTable
     //waiting_.push_back( animal );
    waiting_.Insert(animal.Name(), animal);
    return WAITING_LIST;
  }
  else {
    return NO_FIT;
  }
}


//  Cancel an animal from the schedule or from the waiting list.
//
void
Scheduler::Cancel( const std::string& name,
		   std::list<Animal>& added )
{
  // Use standard library find algorithm to test first the scheduled
  // list and second the waiting list.  If the animal is removed from
  // the schedule, try waiting animals.
  //
  
   //MODIFY THIS CODE TO USE HashTable Interface

//   std::list<Animal>::iterator p =
//           std::find( scheduled_.begin(), scheduled_.end(), name );
//   if ( p != scheduled_.end() ) {
//     int was_in_cage = (*p).GetCage();
//     cages_[was_in_cage].Cancel( *p );
//     scheduled_.erase(p);
//     TryFromWaitList( was_in_cage, added );
//   }
//   else {
//     waiting_.remove( name );
//   }
}


// Determine where an animal is in the schedule, if at all.  Set the
// status and fill in the details.
//
bool
Scheduler::InSchedule( const std::string& name,
		       Scheduler::Status& status,
		       Animal& animal )
{
   //std::list<Animal>::iterator p = 
   //	  std::find( scheduled_.begin(), scheduled_.end(), name );
   //  if ( p != scheduled_.end() ) {
   HashTable<Animal>::entry_ptr ep = scheduled_.Find(name);
   if (ep){   
    animal = ep->second;
    status = SCHEDULED;
    return true;
  }
  else{
     //if ( (p = std::find(waiting_.begin(), waiting_.end(), name))
     //    != waiting_.end() ) {
     ep = waiting_.Find(name);
     if (ep){
        animal = ep->second;
        status = WAITING_LIST;
        return true;
     }
     else
        return false;
  }
}


//  Change an animal's schedule if possible.  If it can be changed,
//  try animals from the waiting list.  If it can't, don't remove it
//  from the current position in the schedule.
//
bool
Scheduler::Change( const std::string& name,
		   const Date& new_start,
		   int new_days,
		   std::list<Animal>& added)
{
  //  Find the animal in the schedule, temporarily remove it, and try
  //  to reschedule.
  //
   
   //MODIFY TO USE HashTable Interface
//   std::list<Animal>::iterator p = 
//           std::find( scheduled_.begin(), scheduled_.end(), name );
//   if ( p != scheduled_.end() ) {
//     Animal old_animal( *p );
//     int old_cage = old_animal.GetCage(); 
//     cages_[old_cage].Cancel( old_animal );  //  remove from cage
//     Animal new_animal( name, old_animal.Weight(), new_start, new_days );
//     scheduled_.erase( p );              // remove from scheduled animals
//     bool in_schedule=false;
//     for ( int c=0; c<cages_.size() && !in_schedule; c++ ) {
//       if ( cages_[c].InWeightRange( new_animal ) &&
//            cages_[c].Schedule( new_animal ) ) {
//         new_animal.AssignCage( c );
//         scheduled_.push_back( new_animal );
//         in_schedule = true;
//       }
//     }
    
//     //  If it could be changed, try waiting animals (if any of the
//     //  days it was previously scheduled are now free).  Otherwise,
//     //  put it back in the schedule.
//     //
//     if ( in_schedule ) {
//       if (new_start > old_animal.StartDate() ||
//           new_start + new_days <
//           old_animal.StartDate() + old_animal.NumDays() ) {
//         TryFromWaitList( old_cage, added );
//       }
//       return true;
//     }
//     else {
//       cages_[old_cage].Schedule( old_animal );
//       scheduled_.push_back( old_animal );
//       return false;
//     }
//   }
//   return false;

}


//  Print the schedule for the given day.
//
void
Scheduler::OutputSchedule( std::ostream& out_str,
			 const Date& day )
{
  std::cout << "\nSchedule for " << day << std::endl;
  for ( int c=0; c<cages_.size(); c++ ) {
    Animal animal;
    if ( cages_[c].OnDay(day, animal) ) 
      std::cout << "  " << c << ": " << animal.Name() << std::endl;
    else
      std::cout << "  " << c << ": _____ " << std::endl;
  }
}


//  Test animals from the waiting list for the given cage.  Do this in
//  order.  It might be that more than one can be accommodated.
//  Return the list of animals moved from the waiting list to the schedule.
//
void
Scheduler::TryFromWaitList( int cage_num,
			    std::list<Animal> & added )
{
  added.erase(added.begin(), added.end());

  std::vector<HashTable<Animal>::exported_type> animals;

  //NOTICE HOW WE ARE USING THE HashTable Routines
  //std::list<Animal>::iterator p = waiting_.begin();
  //while ( p != waiting_.end() ) {

  waiting_.ToVector(animals);
  std::vector<HashTable<Animal>::exported_type>::iterator ip = animals.begin();

  while ( ip != animals.end() ) {
     Animal p = ip->second;
    if ( cages_[cage_num].InWeightRange( p ) &&
	 cages_[cage_num].Schedule( p ) ) {
      p.AssignCage( cage_num );
      added.push_back( p );
      scheduled_.Insert( p.Name(), p );
      waiting_.Remove( p.Name() );
    }
    ++ip;
  }
}

