/* This file provides an implementation for class Temperature.

---------------------------------------------------------------------*/

#include "Temperature.h"

/*----------------------------------------------------------------
 This function performs the assignment operation.

   Receive: The Temperature object Temp being assigned
   Return:  The Temperature object containing this function,
              its members as copies of those of Temp
----------------------------------------------------------------*/

Temperature& Temperature::operator=(const Temperature& Temp)
{
   Degrees_ = Temp.Degrees_;
   Scale_ = Temp.Scale_;

   return *this;            // return this Temperature object
}

/*********** Class Constructor ***********************************

   Precondition:  A Temperature object has been declared.
   Receive:       The (optional) initialization values:
                    InitDegrees, for the Degrees (a double,
                      default 0.0), and
                    InitScale, for the Scale (a char, default 'C')
   Postcondition: The declared Temperature object has
                    its Degrees member initialized to InitDegrees
                    and its Scale member initialized to InitScale
                    (provided InitScale is 'C', 'c', 'F', or 'f').
-----------------------------------------------------------------*/

#include <stdlib.h>          // provides exit()

Temperature::Temperature(double InitDegrees, char InitScale)
{
   Degrees_ = InitDegrees;

   switch (InitScale)
   {
      case 'C': case 'c':    // InitScale must represent one of
      case 'F': case 'f':    //   Celsius or Fahrenheit, to be valid
            Scale_ = InitScale;
            break;
      default:
            cerr << "\n*** Illegal scale: " << InitScale
                 << " received by Temperature constructor!\n";
            exit (-1);
   }
}

/*-----------------------------------------------------------------
 This function returns the Fahrenheit equivalent of the
   Temperature stored within the object containing this function.

   Return: The Fahrenheit equivalent of the value of this object,
             if the value of this object is a Celsius Temperature,
             the value of this object, otherwise
------------------------------------------------------------------*/

Temperature Temperature::Fahrenheit(void) const
{
   switch (Scale_)
   {
      case 'f': case 'F':
         return *this;
      case 'c': case 'C':
         return Temperature(Degrees_ * 1.8 + 32.0, 'F');
      default:
         cerr << "\n*** Fahrenheit: Temperature has invalid scale of "
              << Scale_ << ".\n\n";
         exit(-1);
   }
}

/*-----------------------------------------------------------------
 This function returns the Celsius equivalent of the
   Temperature stored within the object containing this function.

   Return: The Celsius equivalent of the value of this object,
             if the value of this object is a Fahrenheit Temperature,
             the value of this object, otherwise
------------------------------------------------------------------*/

Temperature Temperature::Celsius(void) const
{
   switch (Scale_)
   {
      case 'c': case 'C':
         return *this;
      case 'f': case 'F':
         return Temperature( (Degrees_ - 32.0) * 5 / 9, 'C');
      default:
         cerr << "\n*** Celcius: Temperature has invalid scale of "
              << Scale_ << ".\n\n";
         exit(-1);
   }
}

/*----------------------------------------------------------------
 This function outputs a Temperature value.

    Receive: An ostream reference, Out
             A Temperature object, Temp
    Output:  The Degrees_ and Scale_ members of Temp, in readable
               format
    Return:  Out (for chaining)
------------------------------------------------------------------*/

ostream& operator<<(ostream& Out, const Temperature& Temp)
{
   Out << Temp.Degrees_;

   switch (Temp.Scale_)
   {
      case 'C': case 'c':
         Out << " C";
         break;
      case 'F': case 'f':
         Out << " F";
	 break;
      default:
         cerr << "\n*** << received Temperature with invalid scale "
              << Temp.Scale_ << ".\n\n";
	 exit (-1);
   }
   return Out;                   // to allow << chaining
}

/*----------------------------------------------------------------
 This function inputs a Temperature value.

    Receive: An istream reference, In
             A Temperature object, Temp
    Input:   A real value, to be stored in the Degrees_ member
               of Temp,
             A char value, to be stored in the Scale_ member
               of Temp
    Return:  Temp, containing the input values
             In (for chaining).
------------------------------------------------------------------*/

#include <limits.h>              // provides INT_MAX

istream& operator>>(istream& In, Temperature& Temp)
{
   Boolean
      Valid = False;             // to check for valid input

   In >> Temp.Degrees_;          // get the degrees

   do                            // loop
   {
      In >> Temp.Scale_;         //    get the scale

      switch (Temp.Scale_)       //    check that it's valid
      {
         case 'C': case 'c':
	 case 'F': case 'f':
            Valid = True;
            break;
	 default:
	    cerr << "\n*** >> received invalid scale " << Temp.Scale_
                 << " from keyboard.\n\n";
      }

      In.clear();                // clear state flags
      In.ignore(INT_MAX, '\n');  // ignore remainder of line
   }
   while (!Valid);

   return In;
}

/*----------------------------------------------------------------
 This function defines the Temperature equality operation.

   Receive: Two Temperature objects Temp1 and Temp2
   Return:  True if and only if corresponding data members of
              Temp1 and Temp2 are the same
------------------------------------------------------------------*/

Boolean operator== (const Temperature& Temp1,
                    const Temperature& Temp2)
{
   return ((Temp1.Degrees_ == Temp2.Degrees_) &&
           (Temp1.Scale_ == Temp2.Scale_));
}

/*----------------------------------------------------------------
 This function defines the inequality Temperature operation.

   Receive: Two Temperature objects named Temp1 and Temp2
   Return:  True if and only if at least one data members of
              Temp1 is different from the corresponding data
              member of Temp2
------------------------------------------------------------------*/

Boolean operator!= (const Temperature& Temp1,
                    const Temperature& Temp2)
{
   return ((Temp1.Degrees_ != Temp2.Degrees_) ||
           (Temp1.Scale_ != Temp2.Scale_));
}

/*----------------------------------------------------------------
 This function defines the less than operation on a Temperature
   object.

   Receive: Two Temperature objects named Temp1 and Temp2
   Return:  True, if and only if the Degrees_ member of Temp1
              is less than the Degrees_ member of Temp2
              (and their Scale_ members are equal)
------------------------------------------------------------------*/

Boolean operator< (const Temperature& Temp1,
                   const Temperature& Temp2)
{
   return (Temp1.Degrees_ < Temp2.Degrees_) &&
          (Temp1.Scale_ == Temp2.Scale_);
}

/*----------------------------------------------------------------
 This function defines the greater than operation on a Temperature
   object.

   Receive: Two Temperature objects named Temp1 and Temp2
   Return:  True, if and only if the Degrees_ member of Temp1
              is greater than the Degrees_ member of Temp2
              (and their Scale_ members are equal)
------------------------------------------------------------------*/

Boolean operator> (const Temperature& Temp1,
                   const Temperature& Temp2)
{
   return (Temp1.Degrees_ > Temp2.Degrees_) &&
          (Temp1.Scale_ == Temp2.Scale_);
}

/*----------------------------------------------------------------
 This function defines the less than or equal to operation on a
   Temperature object.

   Receive: Two Temperature objects named Temp1 and Temp2
   Return:  True, if and only if the Degrees_ member of Temp1
              is less than or equal to the Degrees_ member of
              Temp2 (and their Scale_ members are equal)
------------------------------------------------------------------*/

Boolean operator<=(const Temperature& Temp1,
                   const Temperature& Temp2)
{
   return (Temp1.Degrees_ <= Temp2.Degrees_) &&
          (Temp1.Scale_ == Temp2.Scale_);
}

/*----------------------------------------------------------------
 This function defines the greater than or equal to operation on a
   Temperature object.

   Receive: Two Temperature objects named Temp1 and Temp2
   Return:  True, if and only if the Degrees_ member of Temp1
              is greater than or equal to the Degrees_ member of
              Temp2 (and their Scale_ members are equal)
------------------------------------------------------------------*/

Boolean operator>=(const Temperature& Temp1,
                   const Temperature& Temp2)
{
   return (Temp1.Degrees_ >= Temp2.Degrees_) &&
          (Temp1.Scale_ == Temp2.Scale_);
}

