package com.amyllykoski;

/**
 * DAC MAD Q1: Basics of Programming: Android (ART - 40544)
 * 
 * @author Antti Myllykoski, October 2, 2013.
 * 
 *         Homework Week 2. Savings program.
 * 
 *         Requests the user enter a financial goal, time frame in years,
 *         current savings, and how much they estimate they can save each week.
 *         Assuming 1.5% APR interest, calculate how much the use should put
 *         away each week.
 * 
 *         Given the APR and other parameters, will the user meet their goals?
 *         If no, then output: ÒYouÕll never make it at this
 *         rate". If yes, then output: "You will reach your goal"
 */
public class Savings {

	private double goal;
	private int numberOfYears;
	private double currentSavings;
	private double weeklySavings;

	/**
	 * Messages to the user
	 */
	private static final String ERR_NBR_OF_PARAMS = "Please enter required parameters.";
	private static final String ERR_NUMBERS = "All arguments must be positive numbers (0 included).";
	private static final String USAGE = "Usage: java Savings <goal> <number of years> <current savings> <weekly savings>";
	private static final String YUP = "You will reach your goal.";
	private static final String NOPE = "You'll never make it at this rate.";

	// Assumed APR interest rate
	private static final double APR = 1.5 / 100;

	// Weekly interest rate based on APR
	private static final int WEEKS_IN_YEAR = 48; // 12 months, each 4 weeks
	private static final double WEEKLY = APR / WEEKS_IN_YEAR;

	/**
	 * Builder class for constructing the Savings object with the given
	 * arguments.
	 */
	private static class Builder {

		private double goal;
		private int numberOfYears;
		private double currentSavings;
		private double weeklySavings;

		public Builder goal(final String goal) {
			this.goal = Double.parseDouble(goal);
			if (this.goal < 0)
				throw new IllegalArgumentException(ERR_NUMBERS);
			return this;
		}

		public Builder numberOfYears(final String numberOfYears) {
			this.numberOfYears = Integer.parseInt(numberOfYears);
			if (this.numberOfYears < 0)
				throw new IllegalArgumentException(ERR_NUMBERS);
			return this;
		}

		public Builder currentSavings(final String currentSavings) {
			this.currentSavings = Double.parseDouble(currentSavings);
			if (this.currentSavings < 0)
				throw new IllegalArgumentException(ERR_NUMBERS);
			return this;
		}

		public Builder weeklySavings(final String weeklySavings) {
			this.weeklySavings = Double.parseDouble(weeklySavings);
			if (this.weeklySavings < 0)
				throw new IllegalArgumentException(ERR_NUMBERS);
			return this;
		}

		public Savings build() {
			return new Savings(this);
		}
	}

	/**
	 * Constructor.
	 * 
	 * @param b
	 *            builder object containing the parameters for the calculation
	 * @throws IllegalArgumentException
	 * @throws NumberFormatException
	 */
	public Savings(Builder b) throws IllegalArgumentException,
			NumberFormatException {

		this.goal = b.goal;
		this.numberOfYears = b.numberOfYears;
		this.currentSavings = b.currentSavings;
		this.weeklySavings = b.weeklySavings;

	}

	/**
	 * Calculates the savings using given parameters and compares the result to
	 * the given goal. Assumes that interest is compounded every week in order
	 * to cover latest weekly savings.
	 * 
	 * @return true, if goal <= calculated savings, else false
	 */
	private boolean willReachTarget() {

		if (this.goal < this.currentSavings)
			return true;

		int nbrOfPayments = this.numberOfYears * WEEKS_IN_YEAR;
		double savings = this.currentSavings;

		for (int i = 0; i < nbrOfPayments; i++) {
			savings = (1 + WEEKLY) * savings + this.weeklySavings;
		}

		// System.out.println(this);
		// System.out.println("Calculated savings: $" + (int) savings);
		return savings > goal;
	}

	/**
	 * Returns the correct result text based on the calculation.
	 * 
	 * @return string describing whether the goal is reached or not
	 */
	public String getVerdict() {
		return willReachTarget() ? YUP : NOPE;
	}

	/*
	 * For debugging.
	 * 
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		StringBuilder builder2 = new StringBuilder();
		builder2.append("Savings [goal=");
		builder2.append(goal);
		builder2.append(", numberOfYears=");
		builder2.append(numberOfYears);
		builder2.append(", currentSavings=");
		builder2.append(currentSavings);
		builder2.append(", weeklySavings=");
		builder2.append(weeklySavings);
		builder2.append("]");
		return builder2.toString();
	}

	/**
	 * @param args
	 *            goal, time frame, currentSavings, weeklySavings
	 */
	public static void main(String[] args) {

		try {

			if (args.length != 4)
				throw new IllegalArgumentException(ERR_NBR_OF_PARAMS);

			Savings savings = new Savings.Builder().goal(args[0])
					.numberOfYears(args[1]).currentSavings(args[2])
					.weeklySavings(args[3]).build();

			System.out.println(savings.getVerdict());

		} catch (Exception e) {
			System.out.println("Illegal argument " + e.getLocalizedMessage());
			System.out.println(USAGE);
		}

	}
}
