// ***********************************************************
//      Time24 class implementation
//      with overloaded operators as friend functions
// ***********************************************************

#include "except.h"     // for RangeError exception
#include "time24.h"

// Explicit-value constructor
// Initialize time data
// Precondition: h and m >= 0
Time24::Time24(int h, int m) : hour(h), minute(m)
{
   // Precondition check   
   if ((h < 0) || (m < 0))
      throw RangeError("Time24 constructor: negative argument");

   // put hour and minute in correct range
   NormalizeTime();
}  // Explicit-value constructor

// Function: AddTime
// Add m minutes to the time
// Precondition: m >= 0
void Time24::AddTime(int m)
{
   // Precondition check   
   if (m < 0)
      throw RangeError("Time24 AddTime(): negative argument");

   // add m to minute. minute may exceed 59, so normalize
   minute += m;
   NormalizeTime();
}  // end AddTime

// Function: Duration
// Returns the difference between this time and t
// Precondition: t is after current time
Time24 Time24::Duration(const Time24& t) const
{
   // convert current time and time t to minutes
   int currTime = hour * 60 + minute;
   int tTime = t.hour * 60 + t.minute;

   // Precondition check
   if (tTime < currTime)
      throw RangeError ("Time24 duration(): argument is an earlier time");

   // create an anonymous object as the return value
   // will be normalized automatically
   return Time24(0, tTime-currTime);
}  // end Duration

// Function: ReadTime
// Input time in format <hour>:<minute>
// Precondition: hour and minute are not negative
void Time24::ReadTime(istream& in)
{
   char colonSeparator;

   in >> hour >> colonSeparator >> minute;

   // Precondition check   
   if ((hour < 0) || (minute < 0))
      throw RangeError("Time24 ReadTime(): negative argument");

   // make sure hour and minute are in range
   NormalizeTime();
}  // end ReadTime

// Function: WriteTime
// Output time in the format <hour>:<minute>
void Time24::WriteTime(ostream& out) const
{
   out << (hour < 10 ? " " : "") << hour << ':'
       << (minute < 10 ? "0" : "") << minute;

}  // end WriteTime

// Function: GetHour
// Returns the hour
int Time24::GetHour() const
{
   return hour;
}  // end GetHour

// Function: GetMinute
// Returns the minute
int Time24::GetMinute() const
{
   return minute;
}  // end GetMinute

// Function: operator==
// Returns true if lhs is same as rhs
bool operator== (const Time24& lhs, const Time24& rhs)
{
   return lhs.hour == rhs.hour && lhs.minute == rhs.minute;
}

// Function: operator<
// Returns true if lhs is less than rhs
bool operator<  (const Time24& lhs, const Time24& rhs)
{
   // convert the hour and minute values for each operand to 
   // minutes. compare lhs in minutes with rhs in minutes 
   return (lhs.hour*60 + lhs.minute) < (rhs.hour*60 + rhs.minute);
}

// Function: operator+ (for time objects)
// Returns lhs + rhs time
Time24 operator+ (const Time24& lhs, const Time24& rhs)
{
   // create an anonymous object with hour = lhs.hour + rhs.hour 
   // and minute = lhs.minute+rhs.minute.
   return Time24(lhs.hour+rhs.hour, lhs.minute+rhs.minute);
}

// Function: operator+ (for added minutes)
// Returns lhs + rhs minutes
Time24 operator+ (const Time24& lhs, int min){
   // create an anonymous object with hour = lhs.hour and  
   // minute = lhs.minute + min.
   return Time24(lhs.hour, lhs.minute + min);
}

// Function: operator+ (for lhs minutes)
// Returns rhs + lhs minutes using previous operator+
Time24 operator+ (int min, const Time24& rhs)
{
   // return the value rhs + min that is computed by
   //    Time24 operator+ (const Time24& lhs, int min)
   return rhs + min; 
}

// Function: operator-
// Returns lhs  - rhs time
// Precondition: lhs  >= rhs 
Time24 operator- (const Time24& lhs, const Time24& rhs)
{
   if (lhs < rhs)
      throw RangeError("Time24 operator-: Cannot subtract later from earlier time");

   // convert each object to minutes and compute the
   // difference in minutes. return the Time24 object
   // having 0 as hours and the value of the difference
   // as minutes
   return Time24(0, (lhs.hour*60 + lhs.minute) -
                    (rhs.hour*60 + rhs.minute));
}

// Function: operator+=  (for time objects)
// Returns a reference to lhs
// Postcondition: rhs time has been added to lhs
Time24& Time24::operator+= (const Time24& rhs)
{
   // add *this and rhs using overloaded + operator
   *this = *this + rhs;

   // return a reference to the current object
   return *this;
}

// Function: operator+= (for added minutes)
// Returns a reference to lhs
// Postcondition: rhs minutes has been added to lhs
Time24& Time24::operator+= (int min)
{
   // add *this and min using overloaded + operator
   *this = *this + min;

   // return a reference to the current object
   return *this;
}

// Function: operator>>
// Input has the form hour:minute 
// Precondition: hour and minute are not negative
istream& operator>> (istream& istr, Time24& t)
{
   char separatorChar;

   istr >> t.hour >> separatorChar >> t.minute;
   // make sure hour and minute are in range
   t.NormalizeTime();

   // return the stream
   return istr;
}  // end operator<<
   
// Function: operator<<
// Output in the form hour:minute 
ostream& operator<< (ostream& ostr, const Time24& t)
{
   ostr << (t.hour < 10 ? " " : "") << t.hour << ':'
        << (t.minute < 10 ? "0" : "") << t.minute;
   return ostr;
}  // end operator<<

// Private Function: NormalizeTime
// Set minute and hour within their proper ranges
// Precondition: minute and hour are not negative (ensured by caller)
void  Time24::NormalizeTime()
{
   int extraHours = minute / 60;

   // set minute in range 0 to 59
   minute %= 60;

   // update hour. set in range 0 to 23
   hour = (hour + extraHours) % 24;
}  // end NormalizeTime
