// Nick Cash
// Assignment #3
// 810:062-2

#include <cstdlib>
#include <iostream>

// contains all custom definitions
#include "main.h"

using namespace std;

int main( void )
{
  cAirline  airline;
  cFlight  *f = NULL;
  
  // for random num gen
  srand(time(NULL));
  
  // create some flights
  airline.CreateFlight("FTA-100");
  airline.CreateFlight("FTA-1000");
  airline.CreateFlight("FTA-200");
  airline.CreateFlight("FTA-100"); // this one should not happen as it already exists

  // set some data
  f = airline.GetFlight("FTA-100");
  
  if ( f )
  {
    f->AddCrew( "Captain", "Jim" );
    f->AddCrew( "Head Flight Attendant", "Sally" );
    f->AddCrew( "Co-Captain", "Bobby");
    
    // attempt to add another captain
    f->AddCrew( "Captain", "Evil Jim");
    
    // add the citiies
    f->AddCity( "Origin", "Cedar Rapids" );
    f->AddCity( "Destination", "Chicago" );
    
    f->AssignSeat( "aisle", 50, new cPerson("Trinity"));
    f->AssignSeat( "aisle", 20, new cPerson("Neo"));
    f->AssignSeat( "aisle", 1, new cPerson("Hammurabi"));
    f->AssignSeat( "aisle", 9, new cPerson("Tokugawa-san"));
    
    f->AssignSeat( "window", 30, new cPerson("Nick"));
    f->AssignSeat( "window", 6, new cPerson("David"));
    f->AssignSeat( "window", 21, new cPerson("Ryan"));
    f->AssignSeat( "window", 12, new cPerson("James"));
    f->AssignSeat( "window", 50, new cPerson("Ben"));
    
    // this one shoudlnt go through, invalid data
    f->AssignSeat( "windoez", 3110, new cPerson("Mr. Anderson"));
    
    // menu
    f->AddMenu( "chicken", "chicken, chips, drink, potato" );
    f->AddMenu( "beef", "steak or hamburger, fries or chips, drink" );
    f->AddMenu( "vegitarian", "cheese soup, salad, drink" );
    
  }
  else
   cout << "\n\nFTA-100 not found!\n\n";

  // set some more data
  f = airline.GetFlight("FTA-200");

  if ( f )
  {
    f->AddCrew( "Captain", "Anti-Jim" );
    f->AddCrew( "Head Flight Attendant", "Anti-Sally" );
    f->AddCrew( "Co-Captain", "Anti-Bobby");

    // add the citiies
    f->AddCity( "Origin", "Hell" );
    f->AddCity( "Destination", "Earth" );
    
    f->AssignSeat( "aisle", 50, new cPerson("Satan"));
    f->AssignSeat( "aisle", 20, new cPerson("God"));
    f->AssignSeat( "aisle", 1, new cPerson("Jesus"));
    f->AssignSeat( "aisle", 9, new cPerson("Dante"));
    f->AssignSeat( "aisle", 18, new cPerson("Matthias"));

    f->AssignSeat( "window", 30, new cPerson("Generic Name"));
    f->AssignSeat( "window", 6, new cPerson("Bob"));
    f->AssignSeat( "window", 21, new cPerson("Even"));
    f->AssignSeat( "window", 12, new cPerson("Willy"));
    
    // menu
    f->AddMenu( "chicken", "chicken, chips, drink, potato" );
    f->AddMenu( "beef", "steak or hamburger, fries or chips, drink" );
    f->AddMenu( "vegitarian", "cheese soup, salad, drink" );
  }
  else
   cout << "\n\nFTA-200 not found!\n\n";
   
  // and set even more data
  airline.SetCrew( "FTA-1000", "Captain", "Super Jim" );
  airline.SetCrew( "FTA-1000", "Co-Captain", "Super Bobby" );
  airline.SetCrew( "FTA-1000", "Head Flight Attendant", "Super Sally" );
  
  airline.SetCity( "FTA-1000", "Origin", "Earth" );
  airline.SetCity( "FTA-1000", "Destination", "Heaven" );

  // set seats
  airline.SetSeat( "FTA-1000", "aisle", 33, new cPerson("Super Man"));
  airline.SetSeat( "FTA-1000", "aisle", 1, new cPerson("Batman"));
  airline.SetSeat( "FTA-1000", "aisle", 2, new cPerson("Catwoman"));

  airline.SetSeat( "FTA-1000", "window", 50, new cPerson("Aquaman"));
  airline.SetSeat( "FTA-1000", "window", 9, new cPerson("Green Lantern"));
  airline.SetSeat( "FTA-1000", "window", 14, new cPerson("Bruce Wayne"));
  airline.SetSeat( "FTA-1000", "window", 19, new cPerson("Takeda Shengin"));

  // set menu
  airline.SetMenu( "FTA-1000", "chicken", "chicken, chips, drink, potato" );
  airline.SetMenu( "FTA-1000", "beef", "steak or hamburger, fries or chips, drink" );
  airline.SetMenu( "FTA-1000", "vegitarian", "cheese soup, salad, drink" );

  //// end of data setting ////

  // the menu
  bool done = false;

  while ( done == false )
  {
     string name, type;
     char c;
     int i = 0, x = 0;

     cout << "What would you like to do?\n\n";
     cout << "1. List Flights\n";
     cout << "2. List Flights & Crews\n";
     cout << "3. List Flights by Origin\n";
     cout << "4. List Flights by Destination\n";
     cout << "5. List Passengers by Seat\n";
     cout << "6. List Passengers by Seat & Meal Choice\n";
     cout << "7. Free a Seat\n";
     cout << "8. Delete a Flight\n";
     cout << "9. Exit\n\n";

     cin >> i;
     
     // handle choices
     switch ( i )
     {
       default:
       case 1:
            cout << "Full print? (y/n)\n";
            cin >> c;
            
            if ( c == 'y' || c == 'Y' )
              airline.PrintFlights(true);
            else
              airline.PrintFlights();

            break;
            
       case 2:
            airline.PrintFlights(true,true);
            break;
            
       case 3:
            airline.PrintFlightsByCity("Origin");
            break;
            
       case 4:
            airline.PrintFlightsByCity("Destination");
            break;
            
       case 5:
            cout << "Flight name?\n";
            cin >> name;
            
            airline.PrintPassengers(name);
            break;

       case 6:
            cout << "Flight name?\n";
            cin >> name;
            
            airline.PrintPassengers(name,true);
            break;

       case 7:
            cout << "Flight name?\n";
            cin >> name;
            
            cout << "Seat number ( 1 - 50 )?\n";
            cin >> x;
            
            if ( x < 1 || x > 50 )
            {
             cout << "\n\nNo! Between 1 and 50!\n\n";
             break;
            }
            
            cout << "aisle or window (no caps)?\n";
            cin >> type;
            
            if ( airline.FreeSeat( name, type, x ) == false )
              cout << "\nUnable to free seat. Its either free or you supplied bad data!\n\n";

            cout << endl;

            break;
            
       case 8:
            cout << "Flight name?\n";
            cin >> name;
            
            
            if ( airline.DeleteFlight(name) == false )
             cout << "\n\nNo flight named '" << name << "' was found.\n\n";
            
            break;
            
       case 9:
            done = true;
            break;
     }
  }
}

///////////////////////
// class definitions //
///////////////////////

cPerson::cPerson()
{
  int num = rand() % 4;
  
  switch ( num )
  {
    default:
     Set("meal","beef");
     break;
     
    case 0:
     Set("meal","vegitarian");
     break;
     
    case 1:
     Set("meal","beef");
     break;
     
    case 2:
     Set("meal","chicken");
     break;
  }
}



cPerson::cPerson(string n)
{
  Set("name",n);
  
  // meal pref
  int num = rand() % 4;

  switch ( num )
  {
    default:
     Set("meal","beef");
     break;

    case 0:
     Set("meal","vegitarian");
     break;

    case 1:
     Set("meal","beef");
     break;

    case 2:
     Set("meal","chicken");
     break;
  }
}

cPerson::~cPerson()
{
}

// get the data stored in s (for example, name, connecting flights, meal, etc)
string cPerson::Get(string s)
{
  if ( !s.empty() )
   return data[s];

  return "";
}

// set x as y. For example, Set("name", "joe bob) sets data["name"] = "joe bob"
bool cPerson::Set(string x, string y)
{
  if ( !x.empty() && !y.empty() )
  {
   data[x] = y;
   return true;
  }
  
  return false;
}

///

cFlight::cFlight()
{
  name = "";
  
  // create seats, all empty
  for ( int i = 1; i <= 50; i++ )
  {
    aisle_seats[i] = NULL;
    window_seats[i] = NULL;
  }
}

cFlight::cFlight(string n)
{
  if ( !n.empty() )
    name = n;

  // create seats, all empty
  for ( int i = 1; i <= 50; i++ )
  {
    aisle_seats[i] = NULL;
    window_seats[i] = NULL;
  }
}

// unallocs all people associated with the flight
cFlight::~cFlight()
{
  cPerson *p = NULL;
  
  for (SeatMap::iterator j = aisle_seats.begin (); j != aisle_seats.end (); )
  {
      p = j->second;
      
      // unalloc
      delete p;

      // remove from list
      aisle_seats.erase(j++); // iterator is advanced before the erase occurs
  }

  for (SeatMap::iterator j = window_seats.begin (); j != window_seats.end (); )
  {
      p = j->second;

      // unalloc
      delete p;

      // remove from list
      window_seats.erase(j++); // iterator is advanced before the erase occurs
  }

}

// assign a person to a seat
//
// type = aisle or window
// num >= 1 && num <= 50
// p must be a valid alloced cPerson
//
// may return false on bad data or occupied seat
bool cFlight::AssignSeat( string type, int num, cPerson *p)
{
   if ( type.empty() || (num < 1 || num > 50) || !p ) // verify valid data
    return false;
    
   if ( type == "aisle" )
   {
     SeatMap::const_iterator j = aisle_seats.find(num);
     
     if ( j->second )
      return false; // occupied

     aisle_seats[num] = p;
   }
   else if ( type == "window" )
   {
     SeatMap::const_iterator j = window_seats.find(num);

     if ( j->second )
      return false; // occupied

     window_seats[num] = p;
   }
   else
    return false;
}

// free up a seat, return false on free seat or bad data
bool cFlight::FreeSeat( string type, int num )
{
   SeatMap::iterator x;
   cPerson *s = NULL;

   if ( (num < 1 || num > 50) || type.empty() )
    return false;
    
   if ( type == "aisle" )
   {
     x = aisle_seats.find(num);
     
     // was the seat already free?
     if ( x->second == NULL )
      return false;
      
     s = x->second;

     aisle_seats.erase(x);

     delete s;
   }
   else if ( type == "window" )
   {
     x = window_seats.find(num);
     
     // was the seat already free?
     if ( x->second == NULL )
      return false;
      
     s = x->second;

     window_seats.erase(x);

     delete s;
   }
   else
     return false;
     
}

// prints everything about a flight
void cFlight::Print(bool bcrew, bool bcities, bool bmenu, bool bseats )
{
  StringMap::const_iterator cities_end = cities.end();
  StringMap::const_iterator crew_end   = crew.end();
  StringMap::const_iterator j;
  
  SeatMap::const_iterator aisle_end = aisle_seats.end();
  SeatMap::const_iterator window_end = window_seats.end();
  SeatMap::const_iterator i;

  cout << "\n\n\nPrinting " << name << "....\n--------------\n\n";

  if ( bmenu == true )
  {
     cout << "Menu:\n-----\n";
     for ( StringMap::const_iterator x = menu.begin(); x != menu.end(); x++ )
       cout << x->first << " -> " << x->second << endl;
    
     cout << endl;
  }


  if ( (j = crew.begin()) != crew_end && bcrew == true )
  {
    cout << "Crew:\n-----\n";

    for ( ; j != crew_end; j++ )
     cout << j->first << " == " << j->second << "\n";
  }

  if ( (j = cities.begin()) != cities_end && bcities == true )
  {
     cout << "\nCities:\n-------\n";
     
     for ( ; j != cities_end; j++ )
      cout << j->first << " == " << j->second << "\n";
  }
  
  if ( (i = aisle_seats.begin()) != aisle_end && bseats == true )
  {
     cout << "\nAisle Seats:\n-------\n";

     for ( ; i != aisle_end; i++ )
     {
       // if there is a person in the seat, print their name
       if ( i->second != NULL )
       {
        cout << i->first << " -> " << i->second->Get("name");

        if ( bmenu )
          cout << " (Meal: " << i->second->Get("meal") << " )";

        cout << endl;
       }
     }
  }
  
  if ( (i = window_seats.begin()) != window_end && bseats == true )
  {
     cout << "\nWindow Seats:\n-------\n";

     for ( ; i != window_end; i++ )
     {
       // if there is a person in the seat, print their name
       if ( i->second != NULL )
       {
        cout << i->first << " -> " << i->second->Get("name");
        
        if ( bmenu )
          cout << " (Meal: " << i->second->Get("meal") << " )";
             
        cout << endl;
       }
     }
  }

  cout << endl << endl;
}

// adds a crew member to the supplied position
// returns false is someone is at the position already
bool cFlight::AddCrew( string position, string name )
{
  if ( !position.empty() && !name.empty() )
  {
     StringMap::const_iterator i = crew.find(position);

     // check for an existing crew position
     if (i != crew.end())
      return false;


     // add to map
     crew[position] = name;

     return true;
  }
}

// adds a city name to the supplied key word, such as origin or destination
// returns false if the key exists already
bool cFlight::AddCity( string key, string name )
{
  if ( !key.empty() && !name.empty() )
  {
     StringMap::const_iterator i = cities.find(key);

     // check for an existing key
     if (i != cities.end())
      return false;


     // add to map
     cities[key] = name;

     return true;
  }
}

// adds a course to the menu, with item's listed. Returns false if it was supplied bad data
bool cFlight::AddMenu( string course, string items )
{
  if ( !course.empty() && !items.empty() )
  {
    menu[course] = items;
    return true;
  }
  
  return false;
}

///

cAirline::cAirline()
{
}

// deletes all flights, which, in turns, deletes all cPerson's allocated
cAirline::~cAirline()
{
  cFlight *p = NULL;

  for (FlightMap::iterator j = flights.begin (); j != flights.end (); )
  {
      p = j->second;

      // unalloc
      delete p;

      // remove from list
      flights.erase(j++); // iterator is advanced before the erase occurs
  }
}

// delete's a flight from the map and normally. Returns false if it flight not found/destroyed
bool cAirline::DeleteFlight( string fname )
{
  FlightMap::iterator j = flights.find(fname);
  
  // if it wasnt found...
  if ( j == flights.end() )
   return false; // then return false
  else
  {
    cFlight *f = j->second;
    
    // remove from list
    flights.erase(j);
    
    // unalloc flight, which destroys all passengers
    delete f;

    cout << "\n\nFlight '" << fname << "' deleted.\n\n";

    return true;
  }
}

// create's a flight, adds to map. Returns false if it exists already
bool cAirline::CreateFlight( string fname )
{
   if ( !fname.empty() )
   {
     FlightMap::const_iterator i = flights.find(fname);

     // check for an existing flight
     if (i != flights.end())
      return false;
     else
     {
       cFlight *f = new cFlight(fname);
       
       // check alloc
       if ( !f )
       {
         cout << "\n\n\ncAirline::CreateFlight - Out of Memory!\n\nAborting!\n\n";
         abort();
       }
       
       // add to map
       flights[fname] = f;
       
       return true;
     }
   }
}

// print all flights, with a possible (but not by default) call to flight print
void cAirline::PrintFlights(bool print_flight_data, bool crew_only)
{
  FlightMap::const_iterator end = flights.end();

  if ( !print_flight_data )
   cout << "\n\n\nPrinting flights....\n--------------\n\n";

  // iterate through, prints both key and getname, which should be equal
  for (FlightMap::iterator j = flights.begin(); j != end; j++ )
  {
    if ( !print_flight_data )
     cout << "Flight -> '" << j->second->GetName() << "'\n";
    else
    {
     if ( crew_only == false )
       j->second->Print();
     else
       j->second->Print( true, false, false, false );
    }
  }
   
  cout << endl << endl;
}

cFlight* cAirline::GetFlight( string fname )
{
   FlightMap::const_iterator i = flights.find(fname);
   
   if ( i == flights.end() )
    return NULL;
   else
    return i->second;
}

// somewhat of wrapper
bool cAirline::SetCrew( string flight, string pos, string name )
{
  cFlight *f = GetFlight( flight );
  
  if ( f )
   return f->AddCrew( pos, name );
  else
   return false;
}

bool cAirline::SetCity( string flight, string key, string name )
{
  cFlight *f = GetFlight( flight );

  if ( f )
   return f->AddCity( key, name );
  else
   return false;
}

bool cAirline::SetMenu( string flight, string course, string items )
{
  cFlight *f = GetFlight( flight );
  
  if ( f )
   return f->AddMenu( course, items );
  else
   return false;
}

bool cAirline::SetSeat( string flight, string type, int num, cPerson *p )
{
   cFlight *f = GetFlight( flight );

   if ( type.empty() || (num < 1 || num > 50) || !p ) // verify valid data
    return false;
    
   if ( f )
    return f->AssignSeat( type, num, p );


   return false;
}

// print all flights by specifier
void cAirline::PrintFlightsByCity( string city )
{
  StringMap map;

  cout << "\n\nFlights with by " << city << "\n\n";

  // load into a map for easy sorting alphanumerically
  for ( FlightMap::const_iterator x = flights.begin(); x != flights.end(); x++ )
     map[x->second->GetCity(city)] = x->first;

  for ( StringMap::const_iterator x = map.begin(); x != map.end(); x++ )
  {
     cout << "Flight " << x->second << "'s " << city << " is "
          << x->first << "\n";
  }
  
  cout << endl << endl;
}

// print all passengers from flights
void cAirline::PrintPassengers(string flight, bool with_meal)
{
   FlightMap::const_iterator x = flights.find(flight);

   if ( x->second )
    if ( with_meal == true )
      x->second->Print( false, false, true, true);
    else
      x->second->Print( false, false, false, true);
   else // no flight w/ name == flight
    cout << "\n\nNo flight with name == " << flight << " found.\n\n";
}

// calls cFlight::Freeseat
bool cAirline::FreeSeat( string flight, string type, int num )
{
  FlightMap::const_iterator x = flights.find(flight);
  
  if ( x != flights.end() )
   return x->second->FreeSeat( type, num );
  else
   return false;
}