/*
 * Copyright (c) 2012, 2018, Werner Keil, Anatole Tresch and others.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 *
 * Contributors: @atsticks, @keilw
 */
package org.javamoney.calc.common;

import java.util.Objects;

import javax.money.MonetaryAmount;
import javax.money.MonetaryOperator;


/**
 * The present value of a growing perpetuity formula is the cash flow after the first period divided
 * by the difference between the discount rate and the growth rate.
 *
 * A growing perpetuity is a series of periodic payments that grow at a proportionate rate and are
 * received for an infinite amount of time. An example of when the present value of a growing
 * perpetuity formula may be used is commercial real estate. The rental cash flows could be
 * considered indefinite and will grow over time.
 *
 * It is important to note that the discount rate must be higher than the growth rate when using the
 * present value of a growing perpetuity formula. This is due to the present value of a growing
 * perpetuity formula being an infinite geometric series as explained in one of the following
 * sections. In theory, if the growth rate is higher than the discount rate, the growing perpetuity
 * would have an infinite value.
 *
 * @author Anatole
 * @author Werner
 * @see <a href="http://www.financeformulas.net/Present_Value_of_Growing_Perpetuity.html">http://www.financeformulas.net/Present_Value_of_Growing_Perpetuity.html</a>
 */
public final class PresentValueGrowingAnnuity implements MonetaryOperator {

    private final Rate discountRate;
    private final Rate growthRate;

    /**
     * Constructor.
     *
     * @param discountRate The discount rate, not null.
     * @param growthRate   The growth rate, not null.
     */
    private PresentValueGrowingAnnuity(Rate discountRate, Rate growthRate) {
        this.discountRate = Objects.requireNonNull(discountRate);
        this.growthRate = Objects.requireNonNull(growthRate);
    }

    /**
     * Access a MonetaryOperator for calculation.
     *
     * @param discountRate The discount rate, not null.
     * @param growthRate   The growth rate, not null.
     * @return the operator, never null.
     */
    public static PresentValueGrowingAnnuity of(Rate discountRate, Rate growthRate) {
        return new PresentValueGrowingAnnuity(discountRate, growthRate);
    }

    /**
     * Performs the calculation.
     *
     * @param dividend     the dividend payment
     * @param discountRate The discount rate, not null.
     * @param growthRate   The growth rate, not null.
     * @return the resulting amount, never null.
     */
    public static MonetaryAmount calculate(MonetaryAmount dividend, Rate discountRate, Rate growthRate) {
        Objects.requireNonNull(dividend, "dividend required");
        Objects.requireNonNull(discountRate, "discountRate required");
        Objects.requireNonNull(growthRate, "growthRate required");
        return dividend.divide(discountRate.get().subtract(growthRate.get()));
    }


    @Override
    public MonetaryAmount apply(MonetaryAmount dividend) {
        return calculate(dividend, discountRate, growthRate);
    }

    @Override
    public String toString() {
        return "PresentValueGrowingAnnuity{" +
                "discountRate=" + discountRate +
                ", growthRate=" + growthRate +
                '}';
    }
}
