
// Author:  Chuck Stewart
//
// Purpse:  This is the main program for the Meow and Mutts project.
// Its job is to read the cage file and interactively prompt the user
// for input.  The main work of scheduling is in the Scheduler class.  
// Several notes are of interest:
//
// . There are separate functions to read the input and check for
// reasonable correctness.  These aren't fool-proof, but they are close
// (perhaps more than was needed for the project).  In general, making
// code tolerant of all types of input errors is lengthy and
// time-consuming.  It is not something we should focus on here.
//
// . If the program was more "event-driven", meaning that it was
// driven by menus and mouse operations, the main program would have
// to change substantially (obviously!), but little would need to
// change in the class structure and member functions.  Keeping
// functionality and interface separated in this manner is crucial to
// good programming.
//
// . In the code, Animal objects are created, passed around by
// reference, and stored in lists, vectors, and arrays.   Actually
// stored are copies of each animal, so that the same object is
// duplicated in several places.  This means that changes to an animal
// object in one place will not be automatically reflected elsewhere.
// Fortunately, this is not a problem for my program as written.  If
// it were a problem, or if efficiency were more of a concern, I would
// change the program to create objects through the "new" operator and
// store pointers to these objects in the various data structures.
// 

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

#include "Scheduler.h"

//  Prompt for and get the animal's name.  Clear remainder of input line.
//
std::string
GetName( )
{
  std::string str, name;
  do {
    std::cout << "What is the animal's name? ";
    if ( ! (std::cin >> name) ) {
      std::getline( std::cin, str );
      std::cout << "Error in input.  Please try again.\n";
    }
    else {
      std::getline( std::cin, str );
      break;
    }
  } while (true);
  return name;
}


//  Prompt for and get the animal's weight.  Clear remainder of input line.
//
int
GetWeight( const std::string& name )
{
  int wgt;
  std::string str;
  do {
    std::cout << "What is " << name << "'s weight? ";
    if ( ! (std::cin >> wgt) ) {
      std::cin.clear();
      std::getline( std::cin, str );
      std::cout << "Error in input.  Please try again.\n";
    }
    else {
      std::getline( std::cin, str );
      break;
    }
  } while (true);
  return wgt;
}


//  Prompt for and get the animal's arrival date.  Clear remainder
//  of input line.
//
Date
GetDate( const std::string& name )
{
  std::string str;
  int month, day;
  do {
    std::cout << "When will " << name << " arrive (month and day)? ";
    if ( ! (std::cin >> month >> day) ) {
      std::cin.clear();
      std::getline( std::cin, str );
      std::cout << "Error in input.  Please try again.\n";
    }
    else {
      std::getline( std::cin, str );
      break;
    }
  } while (true);
  return Date(month,day);
}


//  Prompt for and get a date.  Clear remainder of input line.
//
Date
GetDate(  )
{
  std::string str;
  int month, day;
  do {
    std::cout << "For what date (month and day)? ";
    if ( ! (std::cin >> month >> day) ) {
      std::cin.clear();
      std::getline( std::cin, str );
      std::cout << "Error in input.  Please try again.\n";
    }
    else {
      std::getline( std::cin, str );
      break;
    }
  } while (true);
  return Date(month,day);
}


//  Prompt for and get the length of an animals' stay.  Clear
//  remainder of input line.
//
int
GetDuration( const std::string& name )
{
  std::string str;
  int ndays;
  do {
    std::cout << "How long will " << name << " stay? ";
    std::cin >> ndays;
    if ( std::cin.fail() ) {
      std::cin.clear();
      std::getline( std::cin, str );
      std::cout << "Error in input.  Please try again.\n";
    }
    else {
      std::getline( std::cin, str );
      break;
    }
  } while (true);
  return ndays;
}



//  The main program checks the arguments, reads the cage information,
//  and then prompts for and reads scheduling events.  The main
//  scheduling operations are handled by the Scheduler.

void
main(int argc, char** argv )
{
  Scheduler m_and_m;
  
  if ( argc != 2 ) {
    std::cerr << "Usage: " << argv[0] << " cages.txt\n";
    exit(0);
  }

  std::ifstream in_str( argv[1] );
  if ( !in_str ) {
    std::cerr << "Can't open " << argv[1] << " to read\n";
    exit(0);
  }

  int min_w, max_w;
  while ( in_str >> min_w >> max_w ) {
    m_and_m.AddCage( Cage(min_w, max_w) );
  }

  bool done = false;
  do {
    std::cout << "\nEnter: S, C, R, A, P, Q for schedule, cancel, revise,\n"
	      << "animal status, print day or quit ==> ";
    std::string req, str;
    std::cin >> req;
    std::getline( std::cin, str );
    std::string name;
    int weight;
    Date date;
    int num_days;
    Scheduler::Status status;
    Animal animal;
    std::list<Animal> added;

    switch ( req[0] ) {
    case 'S': case's':
      std::cout << "\nScheduling:\n";
      name = GetName( );
      if ( m_and_m.InSchedule( name, status, animal ) ) {
	std::cout << name << " is already "
		  << (status == Scheduler::WAITING_LIST ?
		      "on the waiting list." : "in the schedule.")
		  << std::endl;
      }
      else {
	weight = GetWeight( name );
	date = GetDate( name );
	num_days = GetDuration( name );
	std::cout << "\n";
	animal = Animal(name, weight, date, num_days);
	status = m_and_m.Schedule( animal );
	if ( status == Scheduler::NO_FIT ) 
	  std::cout << "Sorry, but " << name << " doesn't fit in any cage.\n";
	else if ( status == Scheduler::WAITING_LIST )
	  std::cout << "The schedule is full during the requested time.\n"
		    << name << " is on the waiting list for the dates "
		    << date << " to " << date+(num_days-1) << std::endl;
	else 
	  std::cout << name << " is scheduled to board from "
		    << date << " to " << date+(num_days-1) << std::endl;
      }
      break;


    case 'C': case 'c':
      std::cout << "\nCanceling\n";
      name = GetName( );
      std::cout << "\n";
      if ( ! m_and_m.InSchedule( name, status, animal ) ) 
	std::cout << name << " is neither currently scheduled nor "
		  << "on the waiting list.\n";
      else {
	m_and_m.Cancel( name, added );
	std::cout << name << " has been removed from the " 
		  << (status == Scheduler::SCHEDULED
		      ? "schedule." : "waiting list." )
		  << std::endl;
	if ( added.size() > 0 ) {
	  std::cout << "As a result, " << added.size()
		    << " animal" << (added.size()==1 ? " was " : "s were ")
		    << "scheduled from the waiting list:\n";
	  for ( std::list<Animal>::iterator p = added.begin();
		p != added.end(); ++ p ) {
	    std::cout << *p << "\n";
	  }
	}
      }
      break;

    case 'R': case 'r':
      std::cout << "Revising:\n";
      name = GetName( );
      if ( ! m_and_m.InSchedule( name, status, animal ) ) 
	std::cout << name << " is neither currently scheduled nor "
		  << "on the waiting list.\n";
      else {
	date = GetDate( name );
	num_days = GetDuration( name );
	std::cout << "\n";
	if ( m_and_m.Change( name, date, num_days, added ) ) {
	  std::cout << name << " is now scheduled to board from "
		    << date << " to " << date+(num_days-1) << std::endl;
	  if ( added.size() > 0 ) {
	    std::cout << "As a result of this change, " << added.size()
		      << " animals were scheduled from the waiting list:\n";
	    for ( std::list<Animal>::iterator p = added.begin();
		  p != added.end(); ++ p ) {
	      std::cout << *p << "\n";
	    }
	  }
	}
      }
      break;

    case 'A': case 'a':
      std::cout << "Querying animal's status:\n";
      name = GetName( );
      std::cout << "\n";
      if ( m_and_m.InSchedule( name, status, animal ) ) {
	if ( status == Scheduler::SCHEDULED )
	  std::cout << "Scheduled: " << animal << std::endl;
	else
	  std::cout << "Waiting: " << animal << std::endl;
      }
      else 
	std::cout << name << " is neither scheduled nor on the waiting list"
		  << std::endl;
      break;

    case 'P': case 'p':
      std::cout << "Print schedule:\n";
      date = GetDate( );
      m_and_m.OutputSchedule( std::cout, date );
      break;

    case 'Q': case 'q':
      done = true;
      break;

    default: // otherwise:
      std::cout << "Invalid option.  Try again.\n";
    }
  } while  ( !done );

  std::cout << "That is the end of scheduling.  Good-bye\n";
}
