// File: temperature.cpp
// Implementation file for Temperature class operations.
// ----------------------------------------------------------------------
// Class: CS 210                     Instructor: Dr. Deborah Hwang
// Assignment: In-class exercise for 3/14/06
// Programmer(s): <fill in your name (s)>

#include <cctype>                            // islower(), toupper()
#include <cstdlib>                           // exit()
#include <iomanip>                           // setstate()
#include "temperature.h"                     // Temperature
using namespace std;

// Default constructor
Temperature::Temperature()
{
   myDegrees = 0.0;
   myScale = 'C';
}  // end default constructor


// Explicit-value constructor
// Constructs an object from initial degrees and initial scale
Temperature::Temperature(double initialDegrees,  // RECEIVED: initial values
                         char initialScale)
{
   if (islower(initialScale))               // if scale is lowercase
      initialScale = toupper(initialScale); //  convert it to uppercase

   switch (initialScale)
   {
      case 'F': case 'C': case 'K':         // if scale is valid
         myDegrees = initialDegrees;        //  proceed with
         myScale = initialScale;            //  initialization
         break;
      default:                              // otherwise, error msg
         cerr << "\n*** Temperature constructor received invalid scale "
              << initialScale << endl;
         exit(1);
   }  // end switch
}  // end explicit-value constructor


// Function: Degrees
// Returns the number of degrees
double Temperature::Degrees() const
{
   return myDegrees;
}  // end Degrees


// Function: Scale
// Returns the scale of temperature
char Temperature::Scale() const
{
   return myScale;
}  // end Scale


// Function:  Fahrenheit
// Computes and returns the equivalent Fahrenheit temperature
Temperature Temperature::Fahrenheit() const
{
   switch (myScale)
   {
      case 'F': 
         return Temperature(myDegrees, 'F');
      case 'C':
         return Temperature(myDegrees * 1.8 + 32.0, 'F');
      case 'K':
         return Temperature((myDegrees - 273.15) * 1.8 + 32.0, 'F');
      default:  // Written to suppress warning about no return in every branch
         cerr << "\n*** Temperature scale invalid.  Should never happen!\n";
         exit (1);
   }  // end switch
}  // end Fahrenheit


// Function: Celsius
// Computes and returns the equivalent Celsius temperature
Temperature Temperature::Celsius() const
{
   switch (myScale)
   {
      case 'C': 
         return Temperature(myDegrees, 'C');
      case 'F':
         return Temperature((myDegrees - 32.0) / 1.8, 'C');
      case 'K':
         return Temperature(myDegrees - 273.15, 'C');
      default:  // Written to suppress warning about no return in every branch
         cerr << "\n*** Temperature scale invalid.  Should never happen!\n";
         exit (1);
   }  // end switch
}  // end Celsius


// Function:  Kelvin
// Computes and returns the equivalent Kelvin temperature
Temperature Temperature::Kelvin() const
{
   switch (myScale)
   {
      case 'K': 
         return Temperature(myDegrees, 'K');
      case 'C':
         return Temperature(myDegrees + 273.15, 'K');
      case 'F':
         return Temperature((myDegrees - 32.0) / 1.8 + 273.15, 'K');
      default:  // Written to suppress warning about no return in every branch
         cerr << "\n*** Temperature scale invalid.  Should never happen!\n";
         exit (1);
   }  // end switch
}  // end Kelvin


// Function: operator==
// Compares two Temperatures for equality
bool operator==(const Temperature & leftOperand,   // RECEIVED: two temperature
                const Temperature & rightOperand)  //    objects
{
   Temperature localTemp;          // the equivalent of rightOperand,
                                   // but in leftOperand.myScale
   switch (leftOperand.myScale)
   {
      case 'C': localTemp = rightOperand.Celsius();
                break;
      case 'F': localTemp = rightOperand.Fahrenheit();
                break;
      case 'K': localTemp = rightOperand.Kelvin();
                break;
   }  // end switch
   return leftOperand.myDegrees == localTemp.myDegrees;
}  // end operator==


// Function: operator<
// Returns true if the object's value is lower than the operand's value,
//         false otherwise
bool operator< (const Temperature & leftOperand,   // RECEIVED: two temperature
                const Temperature & rightOperand)  //   objects
{
   Temperature localTemp;          // the equivalent of rightOperand,
                                   // but in leftOperand.myScale
   switch (leftOperand.myScale)
   {
      case 'C': 
         localTemp = rightOperand.Celsius();
         break;
      case 'F': 
         localTemp = rightOperand.Fahrenheit();
         break;
      case 'K': 
         localTemp = rightOperand.Kelvin();
         break;
   }  // end switch
   return leftOperand.myDegrees < localTemp.myDegrees;
}  // end operator<


// Function: operator+
// Computes and returns a new temperature with added degrees
Temperature operator+(const Temperature & leftOperand, double rightOperand)
                      // RECEIVED: left-hand operand is a temperature object
                      //           right-hand operand is number
{
   return Temperature(leftOperand.myDegrees + rightOperand, 
                      leftOperand.myScale);
}  // end operator+


// Function: operator- (unary)
// Computes and returns a new temperature with negated degrees value
Temperature Temperature::operator-() const
{
   return Temperature(-myDegrees, myScale);
}  // end operator+


// Function: operator<<
// Prints a temperature object to an output stream
ostream & operator<< (ostream & out,  // RECEIVED/PASSED BACK: output stream
                      const Temperature & theTemp)  // RECEIVED: temperature
{
   out << theTemp.myDegrees << ' ' << theTemp.myScale;
   return out;
}  // end operator<<

// Function: operator>>
// Reads a temperature object from an input stream
istream & operator>> (istream & in,      // RECEIVED/PASSED BACK: input stream
                      Temperature & theTemp)       // PASSED BACK: temperature
{
   double inDegrees;                  // temporary variables to
   char inScale;                      //  store the input values

   in >> inDegrees >> inScale;        // read the values from the stream

   if (islower(inScale))              // if scale is lowercase
      inScale = toupper(inScale);     //   convert it to uppercase

   switch(inScale)
   {
      case 'F': case 'C': case 'K':   // if scale is valid
         theTemp.myScale = inScale;     //    assign input values
         theTemp.myDegrees = inDegrees; //    to data members
         break;
      default:                        // otherwise
         in.setstate(ios::failbit);   //   set fail bit in stream, and
   }  // end switch                   //   leave data members unchanged
   return in;
}  // end operator>>
