/***************************************************************************
 *   Copyright (C) 2009 by Paul Lutus                                      *
 *   lutusp@arachnoid.com                                                  *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

package plcalc;

import javax.swing.*;
import java.awt.*;

/**
 *
 * @author lutusp
 */
public class Finance {

    PLCalcMainPanel parent;
    double payment_type;

    public Finance(PLCalcMainPanel p) {
        parent = p;
        process(""); // format default entries
    }

    public void process(String com) {
        double fv, pv, np, pmt, ir;
        payment_type = (parent.PayEndButton.isSelected()) ? 0.0 : 1.0;
        pv = parent.getNum(parent.PVField);
        fv = parent.getNum(parent.FVField);
        np = parent.getNum(parent.NPField);
        pmt = parent.getNum(parent.PMTField);
        ir = parent.getNum(parent.IRField);
        if ("PV".equals(com)) {
            pv = comp_pv(np, ir, pmt, fv);
        } else if ("FV".equals(com)) {
            fv = comp_fv(np, ir, pmt, pv);
        } else if ("NP".equals(com)) {
            np = comp_per(ir, pmt, fv, pv);
        } else if ("PMT".equals(com)) {
            pmt = comp_pmt(np, ir, fv, pv);
        } else if ("IR".equals(com)) {
            ir = comp_int(np, pmt, fv, pv);
        }
        formatResult(parent.PVField, pv, false);
        formatResult(parent.FVField, fv, false);
        formatResult(parent.NPField, np, false);
        formatResult(parent.PMTField, pmt, false);
        formatResult(parent.IRField, ir, true);
    }

    void formatResult(JTextField f, double v, boolean wide) {
        String fmt = (wide) ? "%.6f" : "%.2f";
        String s = String.format(fmt, v);
        f.setText(s);
        f.setForeground((v < 0) ? Color.red : Color.black);
    }

    double comp_pv(double np, double ir, double pmt, double fv) {
        ir /= (double) 100.0;
        double pv;
        if (ir == 0.0) {
            pv = -fv - np * pmt;
        } else {
            double qa = Math.pow(1 + ir, -np);
            double qb = Math.pow(1 + ir, np);
            pv = -qa * (fv + ((-1 + qb) * pmt * (1 + ir * payment_type)) / ir);
        }
        return pv;
    }

    double comp_fv(double np, double ir, double pmt, double pv) {
        ir /= (double) 100.0;
        double fv;
        if (ir == 0.0) {
            fv = -np * pmt - pv;
        } else {
            double q = Math.pow(1 + ir, np);
            fv = -q * pv - (((-1 + q) * pmt * (1 + ir * payment_type)) / ir);
        }
        return fv;
    }

    double comp_pmt(double np, double ir, double fv, double pv) {
        ir /= (double) 100.0;
        double pmt = 0;
        if (ir == 0.0) {
            if (np != 0.0) {
                pmt = -(fv + pv) / np;
            }
        } else {
            double q = Math.pow(1 + ir, np);
            pmt = -(ir * (fv + (q * pv))) / ((-1 + q) * (1 + ir * payment_type));
        }
        return pmt;
    }

    double comp_per(double ir, double pmt, double fv, double pv) {
        ir /= (double) 100.0;
        double np = 0;
        if (ir == 0.0) {
            if (pmt != 0.0) {
                np = -(fv + pv) / pmt;
            }
        } else {
            np = Math.log((-fv * ir + pmt + ir * pmt * payment_type) / (pmt + ir * pv + ir * pmt * payment_type)) / Math.log(1 + ir);
        }
        return np;
    }

    double comp_int(double np, double pmt, double fv, double pv) {
        double ir = 1e300; // error value
        int maxtries = 150;
        int i, j;
        double guess, gd, val, oldval, delta, olddelta;

        if ((fv == 0.0) && (pv == 0.0)) {
            return ir; // error
        }
        // try to predict fv first
        i = 0;
        j = 0;
        guess = 1.0;
        gd = 3;
        val = comp_fv(np, guess, pmt, pv);
        delta = Math.abs(val - fv);
        do {
            oldval = val;
            olddelta = delta;
            guess += gd;
            if (guess != 0.0) {
                val = comp_fv(np, guess, pmt, pv);
                delta = Math.abs(val - fv);
            }
            j++;
            if (Math.abs(val - oldval) > 1e-4) {
                j = 0;
            }
            if (delta > olddelta) {
                gd *= -.5;
            }
        } while ((i++ < maxtries) && (j < 3));

        if (i < maxtries) {
            return guess;
        } else {
            // try to predict pv
            i = 0;
            j = 0;
            guess = 1.0;
            gd = 3;
            val = comp_pv(np, guess, pmt, fv);
            delta = Math.abs(val - pv);
            do {
                oldval = val;
                olddelta = delta;
                guess += gd;
                if (guess != 0.0) {
                    val = comp_pv(np, guess, pmt, fv);
                    delta = Math.abs(val - pv);
                }
                j++;
                if (Math.abs(val - oldval) > 1e-4) {
                    j = 0;
                }
                if (delta > olddelta) {
                    gd *= -.5;
                }
            } while ((i++ < maxtries) && (j < 3));
            if (i < maxtries) {
                return guess;
            }
        }
        return ir;
    }
}
