#include "Money.h"
#include <iomanip>
using namespace std;

/** Construct a new money instance.
 *  Automatically normalizes so that cents is between -99 and 99 and so that
 *  signs of the dollar and cents values do not disagree.
 *  @param  dollars     value in dollars
 *  @param  cents       value in cents (sign is assumed to match that of dollars)
 */
Money::Money(int dollars, int cents) {
  this->dollars = dollars;
  this->cents = cents;
  normalize();
}


/** Return the amount of dollars.
 *
 *  If overall value is negative, the returned amount will be appropriately negated.
 *
 *  @return  number of dollars (signed)
 */
int Money::getDollars() const {
  return dollars;
}

/** Return the amount of cents.
 *
 *  If overall value is negative, the returned amount will be appropriately negated.
 *
 *  @return  number of cents (signed)
 */
int Money::getCents() const {
  return cents;
}

/** Checks if current value is negative.
 *  @return  true if negative, false otherwise
 */
bool Money::isNegative() const {
  return (dollars < 0 || cents < 0);
}

/** Binary addition of monetary values.
 *  @param    other   monetary value as second operand
 *  @return   sum of monetary values
 */
Money Money::operator+(const Money& other) const {
  return Money(dollars + other.dollars, cents + other.cents);
}

/** Mutating form of addition for monetary values.
 *  @param    other   monetary value as second operand
 */
void Money::operator+=(const Money& other) {
  dollars += other.dollars;
  cents += other.cents;
  normalize();
}


/** Binary subtraction of monetary values.
 *  @param    other   monetary value as second operand
 *  @return   difference of monetary values
 */
Money Money::operator-(const Money& other) const {
  return Money(dollars - other.dollars, cents - other.cents);
}

/** Mutating form of subtraction for monetary values.
 *  @param    other   monetary value as second operand
 */
void Money::operator-=(const Money& other) {
  dollars -= other.dollars;
  cents -= other.cents;
  normalize();
}


/** Unary negation of monetary value.
 *  @return   negation of original value
 */
Money Money::operator-() const {
  return Money(-dollars, -cents);
};

/** Compares two monetary amounts
 *  @param    other   monetary value as second operand
 *  @return   true if first is stricty less than second
 */
bool Money::operator<(const Money& other) const {
  return (dollars < other.dollars ||
	  (dollars == other.dollars && cents < other.cents));
};

/** Compares two monetary amounts
 *  @param    other   monetary value as second operand
 *  @return   true if first is less than or equal to the second
 */
bool Money::operator<=(const Money& other) const {
  return ((*this == other) || (*this < other));
};

/** Compares two monetary amounts
 *  @param    other   monetary value as second operand
 *  @return   true if first is stricty greater than second
 */
bool Money::operator>(const Money& other) const {
  return !(*this <= other);
};

/** Compares two monetary amounts
 *  @param    other   monetary value as second operand
 *  @return   true if first is greater than or equal to the second
 */
bool Money::operator>=(const Money& other) const {
  return !(*this < other);
};

/** Compares two monetary amounts
 *  @param    other   monetary value as second operand
 *  @return   true if two are equivalent
 */
bool Money::operator==(const Money& other) const {
  return (dollars == other.dollars && cents == other.cents);
};

/** Compares two monetary amounts
 *  @param    other   monetary value as second operand
 *  @return   true if two are non-equivalent
 */
bool Money::operator!=(const Money& other) const {
  return !(*this == other);
};

/** Multiply monetary amount by a constant.
 *  Rounds to nearest cent.
 *  @param   factor   multiplicative factor
 *  @return new monetary value
 */
Money Money::operator*(double factor) const {
  double pennies = 100.0 * (dollars + cents/100.0) * factor;
  pennies += (pennies >= 0 ? 0.5 : -0.5);
  long temp = long(pennies);
  return (temp >= 0 ?
	  Money( temp/100, temp%100) :
	  Money(- (-temp)/100, - (-temp%100)));
}


/** Multiply monetary amount by a constant.
 *  Rounds to nearest cent.
 *  @param   factor   multiplicative factor
 *  @return new monetary value
 */
Money operator*(double factor, const Money& m) {
  return m * factor;   // rely on the member form of operator
}


/**
 * Output the value in from such as $5.35 for positive value or
 * -$5.35 for a negative value.
 * @param   os     the output stream
 * @param   value  monetary value to display
 */
ostream& operator<<(ostream& os, const Money& value) {
  int d = value.getDollars();
  int c = value.getCents();
  if (d < 0 || c < 0) {
    os << "-";
    d = -d;
    c = -c;
  }
  os << "$";
  os << d;
  os << ".";
  os << setfill('0') << setw(2) << c;   // force two characters, even if zero
  return os;

}

void Money::normalize() {
  while (cents >= 100) {
    dollars++;
    cents -= 100;
  }
  while (cents <= -100) {
    dollars--;
    cents += 100;
  }
  if (dollars < 0 && cents > 0) {
    cents -= 100;
    dollars += 1;
  }
  if (dollars > 0 && cents < 0) {
    cents += 100;
    dollars -= 1;
  }
}
