// TIME.CPP - Time class.

// Time if day, expressed in hours and minutes, using a 24-hour clock.
// Demonstration of prefix/postfix increment operators and stream output.
// Uses the RangeError exception class.

#include <stdlib.h>			// atoi() function
#include <iostream>
#include <iomanip>
#include <strstream>
using namespace std;

#include "range.h"			// RangeError class


class Time 
{
public:
  Time( unsigned int c = 0 );

	// copy constructor
	Time( const Time & t2 )
	{
		minutes = t2.minutes;
		hours = t2.hours;
	}

	// implicit convert-to-integer operator. Note the unusual syntax.

	operator int( ) const
	{
		return (hours * 100) + minutes;
	}
  
	// implicit convert-to-string

	operator string( ) const
	{
		ostrstream os;
		os << hours << ":" << minutes << '\0';
		return os.str( );
	}

  // prefix increment operator. Returns const reference because
	// we don't want client to inadvertently modify this time object.

	const Time & operator ++( )
	{
		if( ++minutes > MinuteMax )
		{
			minutes = 0;
			hours = (hours + 1) % (HourMax + 1);
		}
		return *this;
	}
 
	// postfix operators always have a dummy int parameter,
	// to distinguish them from the prefix version. Here, we
	// return the current time value, then add 1 to the current time.

	Time operator ++( int )
	{
		Time save( *this );  // construct a copy
		operator ++();       // increment the time
		return save;         // return the copy
	}

	// less-than operator
	
	bool operator <( const Time & rhs ) const
	{
		if( hours < rhs.hours )
			return true;
		else if( hours == rhs.hours )
			return minutes < rhs.minutes;
		else
			return false;
	}

  // Addition/assignment operator. Add n minutes to the time.

  const Time & operator +=( unsigned n )
	{
		unsigned t = minutes + n;
		minutes = t % (MinuteMax + 1);  // remaining minutes
		hours += t / (MinuteMax + 1);   // add to hours
		hours = hours % (HourMax + 1);  // roll over to next day
		return *this;
	}

	void setHours( unsigned h );
  void setMinutes( unsigned m );

	// stream output operator. Must be implemented out-of-line.
  friend ostream & operator <<( ostream & os, const Time & h );

private:
  unsigned hours;
  unsigned minutes;
	static const unsigned MinuteMax;
	static const unsigned HourMax;
};

const unsigned Time::MinuteMax = 59;
const unsigned Time::HourMax = 23;

// Construct from unsigned integer.

Time::Time( unsigned tv )
{
  setHours( tv / 100 );
  setMinutes( tv % 100 );
}

void Time::setHours( unsigned h )
{
  if( h > HourMax )
    throw RangeError(__FILE__,__LINE__,h);
  hours = h;
}

void Time::setMinutes( unsigned m )
{
  if( m > MinuteMax )
    throw RangeError(__FILE__,__LINE__,m);
  minutes = m;
}


// Stream output (global function). Display in "hh:mm" format.

ostream & operator <<( ostream & os, const Time & t )
{
  os.fill('0');
  os << setw(2) << t.hours << ':' << setw(2) << t.minutes;
  return os;
}

//********* TEST PROGRAM ***********************

void TimeTest()
{
    Time t1( 1845 );
    Time t2( 1830 );
		Time t3( t2 );
		cout << boolalpha;

		// demonstrate simple exception handling
    try {
			t1.setHours( 25 );
    }
    catch( const RangeError & R ) {
      cout << R;
    }

		int n = t2;			// implicit conversion to int

		string s = t2;	// implicit conversion to string

		
		// demonstrate operator << function.
		cout << "time t1: ";
		operator <<( cout, t1 );
		cout << endl;

		// this is easier!
		cout << "time t2: " << t2 << endl;

		cout << "t2 < t1: " << (t2 < t1) << endl;
		cout << "t3 < t2: " << (t3 < t2) << endl;

		// does not compile, because t4 could potentially be 
		// used to modify the reference to t3:
		// Time & t4 = ++t3;

		// compiles, because a copy of t3 is returned by ++
		Time & t5 = t3++;

		cout << "++t1: " << ++t1 << endl;
		t1++;
		cout << "t1++: " << t1 << endl;

		t2 += 15;
		cout << "(t2 += 15) = " << t2 << endl;
}


int main()
{
    TimeTest();

    return 0;
}

