import java.util.Scanner;

/*********************************************************************
 * @author Duncan Buell
 *
 * written: 26 February 2008
**/
public class Loan2
{
  private int duration;
  private double payment,principal,rate;

/*********************************************************************
 * This is the standard constructor, which ought for good form to
 * explicitly initiale the instance variables for the class. 
**/
  public Loan2()
  {
    this.duration = 0;
    this.principal = 0.0;
    this.rate = 0.0;
  } // public Loan2()

/*********************************************************************
 * accessor methods for the instance variables
**/
/*********************************************************************
 * This method simply returns the number of months that is the
 * duration of the loan.
 *
 * @return The int number of months that is the loan duration.
**/
  public int getDuration()
  {
    return(this.duration);
  }

/*********************************************************************
 * This method simply returns the double precision value of the
 * current loan payment.
 *
 * @return The double value that is the current loan payment.
**/
  public double getPayment()
  {
    return(this.payment);
  }

/*********************************************************************
 * This method simply returns the double precision value of the
 * current loan principal.
 *
 * @return The double value that is the current loan principal.
**/
  public double getPrincipal()
  {
    return(this.principal);
  }

/*********************************************************************
 * This method simply returns the double precision value of the loan
 * interest rate.
 *
 * @return The double value that is the loan interest rate.
**/
  public double getRate()
  {
    return(this.rate);
  }

/*********************************************************************
 * mutator methods for the instance variables
**/
/*********************************************************************
 * This method takes an input string, converts to a integer, and sets
 * that int value as the number of months duration of the loan.
 * It SHOULD be the case that extensive error testing is done in this
 * method to determine that in fact the input string represents a
 * legal value for a int.  However, we don't do that here.
 *
 * @param The (String) value of what is supposed to be a int.
 * @return A boolean false if the input was illegal, else true.
**/
  public boolean setDuration(String inString)
  {
    boolean returnValue;
    int convertedValue;

    convertedValue = Integer.valueOf(inString);
    returnValue = false;
    if(convertedValue > 0)
    {
      this.duration = convertedValue;
      returnValue = true;
    }

    return(returnValue);
  } // public boolean setDuration(String inString)

/*********************************************************************
 * This method takes an input string, converts to a double, and sets
 * that double value as the payment per month of the loan.
 * Same comments about error checking as for the payment.
 *
 * @param The (String) value of what is supposed to be a double.
 * @return A boolean false if the input was illegal, else true.
**/
  public boolean setPayment(String inString)
  {
    boolean returnValue;
    double convertedValue;

    convertedValue = Double.valueOf(inString);
    returnValue = false;
    if(convertedValue > 0.0)
    {
      this.payment = convertedValue;
      returnValue = true;
    }

    return(returnValue);
  } // public boolean setPayment(String inString)

/*********************************************************************
 * This method takes an input string, converts to a double, and sets
 * that double value as the prinicpal amount of the loan.
 * Same comments about error checking as for the payment.
 *
 * @param The (String) value of what is supposed to be a double.
 * @return A boolean false if the input was illegal, else true.
**/
  public boolean setPrincipal(String inString)
  {
    boolean returnValue;
    double convertedValue;

    convertedValue = Double.valueOf(inString);
    returnValue = false;
    if(convertedValue > 0.0)
    {
      this.principal = convertedValue;
      returnValue = true;
    }

    return(returnValue);
  } // public boolean setPrincipal(String inString)

/*********************************************************************
 * This method takes an input string, converts to a double, and sets
 * that double value as the interest rate of the loan.
 * Same comments about error checking as for the payment.
 *
 * @param The (String) value of what is supposed to be a double.
 * @return A boolean false if the input was illegal, else true.
**/
  public boolean setRate(String inString)
  {
    boolean returnValue;
    double convertedValue;

    convertedValue = Double.valueOf(inString);
    returnValue = false;
    if(convertedValue > 0.0)
    {
      this.rate = convertedValue;
      returnValue = true;
    }

    return(returnValue);
  }

/*********************************************************************
 * general methods
**/
/*********************************************************************
 * This method amortizes the loan until the principal balance is zero.
 * NOTE: This assumes monthly calculation of interest, without regard
 * to the number of days in a month, etc.
**/
  public double amortize(double payment)
  {
    double interestThisMonth,localOldPrincipal,localNewPrincipal;

    localOldPrincipal = this.getPrincipal();
    localNewPrincipal = localOldPrincipal;

    System.out.printf("Mon OldPrinc      Int  Payment NewPrinc%n");
    for(int month = 1; month < this.getDuration(); ++month)
    {
      interestThisMonth = localOldPrincipal * (this.rate/100.0) / 12.0;
      localNewPrincipal = localOldPrincipal + interestThisMonth - payment;
      System.out.printf("%3d %8.2f %8.2f %8.2f %8.2f %n",
                         month,localOldPrincipal,interestThisMonth,
                         payment,localNewPrincipal);
      localOldPrincipal = localNewPrincipal;
    } // for(int month = 1; month < this.getDuration(); ++month)

    return localNewPrincipal;
  } // public double amortize()

/*********************************************************************
 * This method computes the payment per month by the time honored
 * method of BFI (Brute Force and Ignorance).
**/
  public void computePayment()
  {
    final double TOLERANCE = 10.0;
    boolean closeEnough;
    double payment,residual,tooLargePayment,tooSmallPayment;

    tooSmallPayment = 0.0;
    tooLargePayment = this.getPrincipal();
    payment = (tooSmallPayment + tooLargePayment) / 2.0;

    closeEnough = false;
    while(!closeEnough)
    {
      residual = this.amortize(payment);
      if(Math.abs(residual) < TOLERANCE)
      {
        closeEnough = true;
      } 
      else
      {
        if(residual < 0.0)
        {
          tooLargePayment = payment;
          payment = (tooSmallPayment + tooLargePayment) / 2.0;
        }
        else
        {
          tooSmallPayment = payment;
          payment = (tooSmallPayment + tooLargePayment) / 2.0;
        }
      } 
    } // while(!closeEnough)

  } // public void computePayment()

/*********************************************************************
 * This method gets the data and checks that the values are legal.
**/
  public boolean getGoodData(Scanner console)
  {
    boolean goodData;
    String inputString;

    System.out.printf("Input loan principal in dollars: ");
    inputString = console.next();
    goodData = this.setPrincipal(inputString);
    if(false == goodData)
    {
      System.out.println("ERROR: input value " + inputString + " is not valid for the principal");
      return false;
    }
    else
    {
      System.out.println("Loan principal is: " + this.getPrincipal());
    }

    System.out.printf("Input loan rate (input '5' for '5.0%%'): ");
    inputString = console.next();
    goodData = this.setRate(inputString);
    if(false == goodData)
    {
      System.out.println("ERROR: input value " + inputString + " is not valid for the rate");
      return false;
    }
    else
    {
      System.out.println("Loan rate is: " + this.getRate());
    }

    System.out.printf("Input loan duration in months: ");
    inputString = console.next();
    goodData = this.setDuration(inputString);
    if(false == goodData)
    {
      System.out.println("ERROR: input value " + inputString + " is not valid for the duration");
      System.exit(0);
    }
    else
    {
      System.out.println("Duration in months is: " + this.getDuration());
    }

    return true;
  } // public boolean getGoodData(Scanner console)

} // public class Loan2

