//==================================================================
// Future value application - with validation 
//==================================================================
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.control.*;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import java.text.NumberFormat;

public class FutureValueAppVal extends Application 
{
    TextField investmentField;                                          //global fields
    TextField interestRateField;
    TextField yearsField;
    TextField futureValueField;
    
    public static void main(String[] args) 
    {
        Application.launch(args);                                       //launch the application
    }    

    @Override
    public void start(Stage primaryStage) 
    {
        primaryStage.setTitle("Future Value with Validation");

        GridPane grid  = new GridPane();                                //create a gridPane layout
        grid.setAlignment(Pos.TOP_CENTER);                              //set alignment top/center
        Insets padding = new Insets(25, 25, 25, 25);                    //create padding top,left,bottom,right 
        grid.setPadding(padding);                            
        grid.setHgap(10);                                               //horizontal gap between rows
        grid.setVgap(10);                                               //vertical gap between columns

        Label investmentLabel = new Label("Monthly Investment:");
        grid.add(investmentLabel, 0, 0);                                //add node with at column,row
        investmentField = new TextField();
        grid.add(investmentField, 1, 0);

        Label interestRateLabel = new Label("Yearly Interest Rate:");
        grid.add(interestRateLabel, 0, 1);
        interestRateField = new TextField();
        grid.add(interestRateField, 1, 1);

        Label yearsLabel = new Label("Years:");
        grid.add(yearsLabel, 0, 2);
        yearsField = new TextField();
        grid.add(yearsField, 1, 2);

        Label futureValueLabel = new Label("Future Value:");
        grid.add(futureValueLabel, 0, 3);
        futureValueField = new TextField();
        grid.add(futureValueField, 1, 3);
        futureValueField.setEditable(false);                            //make it read only
 
        Button calculateButton = new Button("Calculate");
        calculateButton.setOnAction(event -> calculate());              //on action call calculate()

        Button exitButton = new Button("Exit");
        exitButton.setOnAction(event -> exitApp());                     //on action call exitApp() 

        HBox buttons = new HBox(10);                                    //create an HBox layout with 10px spacing
        buttons.getChildren().add(calculateButton);                     //add calculate button
        buttons.getChildren().add(exitButton);
        buttons.setAlignment(Pos.BOTTOM_RIGHT);                         //set alignment bottom/right
        grid.add(buttons, 0, 4, 2, 1);                                  //add HBox at column,row, colspan,rowspan 

        Scene scene = new Scene(grid);                                  //create a scene, and add the grid layout

        primaryStage.setScene(scene);                                   //add the scene to the stage
        primaryStage.show();                                            //display the window
    }


   private void calculate() 
   {
        String errorMsg = "";
        Validation v    = new Validation();          
        errorMsg += v.isDouble(investmentField.getText(),   "Monthly Investment");
        errorMsg += v.isDouble(interestRateField.getText(), "Yearly Interest Rate");
        errorMsg += v.isDouble(yearsField.getText(),        "Years");

        if (errorMsg.isEmpty()) 
        {
            double investment = Double.parseDouble(investmentField.getText());           //get data from text fields
            double rate       = Double.parseDouble(interestRateField.getText()) /100;
            double years      = Double.parseDouble(yearsField.getText());

            double futureValue = calcFutureValue(investment, rate, years);               //calculate future value

            NumberFormat currency = NumberFormat.getCurrencyInstance();                  //create a formatter
            String valueFormatted = currency.format(futureValue);                        //format number to currency         
            futureValueField.setText(valueFormatted);                                    //set data in read-only text field
        } 
        else 
        {
            Alert alert = new Alert(Alert.AlertType.ERROR);
            alert.setHeaderText("Invalid Data");
            alert.setContentText(errorMsg);
            alert.showAndWait();        
        }
    }

    private void exitApp()          //event handler 
    {
        System.exit(0);
    }
    
    public static double calcFutureValue(double investment, double interest, double years)
    {
        double months          = Math.round(years * 12);        //in case years are fraction
        double monthlyInterest = interest / 12;
        
        double futureValue = 0;
        for (int i = 1; i <= months; i++) 
        {
            futureValue = (futureValue + investment) * (1 + monthlyInterest);
        }
        return futureValue;
    }    
}

//==========================================================================================================
class Validation 
{
    private final String lineEnd;
    
    public Validation()                                     //empty constructor 
    { 
        this.lineEnd = "\n";  
    }    
    public Validation(String lineEnd)                       //constructor with lineEnd 
    {
        this.lineEnd = lineEnd;
    }
    
    public String isPresent(String value, String name)      //test if String value is empty 
    {
        String msg = "";
        if (value.isEmpty()) 
            msg = name + " is required." + lineEnd;
        
        return msg;
    }

    public String isDouble(String value, String name)       //test if value is a double
    {
        String msg = "";
        try {
            Double.parseDouble(value);
        } catch (NumberFormatException e) {
            msg = name + " must be a valid number." + lineEnd;
        }
        return msg;
    }
    
    public String isInteger(String value, String name) {    //test if value is an integer
        String msg = "";
        try {
            Integer.parseInt(value);
        } catch (NumberFormatException e) {
            msg = name + " must be an integer." + lineEnd;
        }
        return msg;
    }
}