/*
 *  Draco Econometrics
 *  Copyright (C) 2006-2007 Approximatrix, LLC
 *  http://www.approximatrix.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 3 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, see <http://www.gnu.org/licenses/>.
 */

package com.approximatrix.draco.fit.ui;

import com.approximatrix.draco.application.worker.WorkerEvent;
import com.approximatrix.draco.application.worker.WorkerListener;
import com.approximatrix.draco.datamodel.StatTableModel;
import com.approximatrix.draco.fit.LSFit;
import com.approximatrix.draco.fit.LSIterative;
import com.sun.java.SwingWorker;

import java.awt.BorderLayout;
import javax.swing.JPanel;
import javax.swing.JDialog;
import javax.swing.JProgressBar;
import javax.swing.JButton;
import javax.swing.SwingUtilities;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.JFrame;
import javax.swing.event.EventListenerList;

import java.awt.GridLayout;
import java.awt.Dialog;



public class IterativeProgress extends ProgressDialog {
	
	private JPanel jContentPane = null;
	private JPanel jPanel = null;
	private JProgressBar jIterationProgressBar = null;
	private JPanel jPanel1 = null;
	private JPanel jPanel2 = null;
	private JButton jStopButton = null;
	private JLabel jLabel = null;
	private JLabel jLabel1 = null;
	private JTextField jIterationTextField = null;
	private JTextField jToleranceTextField = null;
	
	// For iterations
	int max_iterations = 1;
	double tolerance = Double.POSITIVE_INFINITY;
	LSIterative solver = null;

	/**
	 * This constructor is used for modal operation by immediately accepting a 
	 * model from the start.
	 * 
	 * @param title the title for the progress dialog
	 * @param master the master JFrame
	 * @param first_iteration the first iteration of the solving process
	 * @param max_iterations the maximum number of iterations to perform
	 * @param tolerance the tolerance to use for stopping calculations
	 */
	public IterativeProgress(String title, JFrame master, LSIterative first_iteration, int max_iterations, double tolerance) {
		super(master);
		this.solver = first_iteration;
		this.max_iterations = max_iterations;
		this.tolerance = tolerance;
		
		initialize();
		build_worker();
	}

	/**
	 * This method initializes this
	 * 
	 * @return void
	 */
	private void initialize() {
		this.setSize(300, 180);
		this.setPreferredSize(new java.awt.Dimension(300, 180));
		this.setLocationRelativeTo(super.getParent());
		this.setResizable(true);
		this.setTitle("Iterative Progress");
		this.setContentPane(getJContentPane());
		this.addWindowListener(new java.awt.event.WindowAdapter() {
			public void windowClosing(java.awt.event.WindowEvent e) {
				if(worker != null)
					worker.interrupt();
				fireWorkerEvent();
				dispose();
			}
		});
	}

	/**
	 * This method initializes jContentPane
	 * 
	 * @return javax.swing.JPanel
	 */
	private JPanel getJContentPane() {
		if (jContentPane == null) {
			jContentPane = new JPanel();
			jContentPane.setLayout(new BorderLayout());
			jContentPane.add(getJPanel(), java.awt.BorderLayout.NORTH);
			jContentPane.add(getJPanel1(), java.awt.BorderLayout.CENTER);
			jContentPane.add(getJPanel2(), java.awt.BorderLayout.SOUTH
					);
		}
		return jContentPane;
	}

	/**
	 * This method initializes jPanel	
	 * 	
	 * @return javax.swing.JPanel	
	 */
	private JPanel getJPanel() {
		if (jPanel == null) {
			jPanel = new JPanel();
			jPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(javax.swing.BorderFactory.createEtchedBorder(javax.swing.border.EtchedBorder.LOWERED), "Iteration Progress", javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, null, null));
			jPanel.add(getJIterationProgressBar(), null);
		}
		return jPanel;
	}

	/**
	 * This method initializes jIterationProgressBar	
	 * 	
	 * @return javax.swing.JProgressBar	
	 */
	private JProgressBar getJIterationProgressBar() {
		if (jIterationProgressBar == null) {
			jIterationProgressBar = new JProgressBar();
			jIterationProgressBar.setPreferredSize(new java.awt.Dimension(275,14));
			jIterationProgressBar.setMaximum(max_iterations);
			jIterationProgressBar.setMinimum(0);
		}
		return jIterationProgressBar;
	}

	/**
	 * This method initializes jPanel1	
	 * 	
	 * @return javax.swing.JPanel	
	 */
	private JPanel getJPanel1() {
		if (jPanel1 == null) {
			GridLayout gridLayout = new GridLayout();
			gridLayout.setRows(2);
			gridLayout.setVgap(5);
			gridLayout.setHgap(5);
			jLabel1 = new JLabel();
			jLabel1.setText("Estimated Parameter Change:");
			jLabel = new JLabel();
			jLabel.setText("Iteration Count:");
			jPanel1 = new JPanel();
			jPanel1.setPreferredSize(new java.awt.Dimension(343,60));
			jPanel1.setLayout(gridLayout);
			jPanel1.setBorder(javax.swing.BorderFactory.createTitledBorder(javax.swing.BorderFactory.createEtchedBorder(javax.swing.border.EtchedBorder.LOWERED), "Current Iteration Data", javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, null, null));
			jPanel1.add(jLabel, null);
			jPanel1.add(getJIterationTextField(), null);
			jPanel1.add(jLabel1, null);
			jPanel1.add(getJToleranceTextField(), null);
		}
		return jPanel1;
	}

	/**
	 * This method initializes jPanel2	
	 * 	
	 * @return javax.swing.JPanel	
	 */
	private JPanel getJPanel2() {
		if (jPanel2 == null) {
			jPanel2 = new JPanel();
			jPanel2.add(getJStopButton(), null);
		}
		return jPanel2;
	}

	/**
	 * This method initializes jStopButton	
	 * 	
	 * @return javax.swing.JButton	
	 */
	private JButton getJStopButton() {
		if (jStopButton == null) {
			jStopButton = new JButton();
			jStopButton.setText("Stop!");
			jStopButton.addActionListener(new java.awt.event.ActionListener() {
				public void actionPerformed(java.awt.event.ActionEvent e) {
					if(jStopButton.getText().compareTo("Stop!")== 0) {
						worker.interrupt();
					} else {
						// Close
						IterativeProgress.this.setVisible(false);
						fireWorkerEvent();
						dispose();
					}
				}
			});
		}
		return jStopButton;
	}

	/**
	 * This method initializes jIterationTextField	
	 * 	
	 * @return javax.swing.JTextField	
	 */
	private JTextField getJIterationTextField() {
		if (jIterationTextField == null) {
			jIterationTextField = new JTextField();
			jIterationTextField.setPreferredSize(new java.awt.Dimension(140,20));
			jIterationTextField.setHorizontalAlignment(javax.swing.JTextField.TRAILING);
			jIterationTextField.setEditable(false);
		}
		return jIterationTextField;
	}

	/**
	 * This method initializes jToleranceTextField	
	 * 	
	 * @return javax.swing.JTextField	
	 */
	private JTextField getJToleranceTextField() {
		if (jToleranceTextField == null) {
			jToleranceTextField = new JTextField();
			jToleranceTextField.setPreferredSize(new java.awt.Dimension(140,20));
			jToleranceTextField.setHorizontalAlignment(javax.swing.JTextField.TRAILING);
			jToleranceTextField.setEditable(false);
		}
		return jToleranceTextField;
	}
	
	/** Updates the Swing components safely from the SwingWorker thread
	 * 
	 * @param iteration the current iteration
	 * @param change a measure of the convergence
	 */
	private void updateStatus(final int iteration, final double change) {
		Runnable doSetProgress = new Runnable() {
            public void run() {
            	jToleranceTextField.setText(Double.toString(change));
            	jIterationTextField.setText(Integer.toString(iteration));
            	jIterationProgressBar.setValue(iteration);
            }
        };
        SwingUtilities.invokeLater(doSetProgress);
	}
	
	/** This routine represents the meat of the SwingWorker thread that performs
	 * all iteration control
	 * 
	 * @return for now, a String
	 */
	private Object doIterations() {
		try {
			double change = Double.POSITIVE_INFINITY;
			int current_iteration = 1;
			
			// Perform the first iteration outside the loop
			solver.perform();
			updateStatus(solver.getCurrentIteration(),solver.getIterationChange());
			//System.out.println("First iteration");
			
			while(current_iteration<max_iterations && change>tolerance && solver.isSuccessful()) {
				solver = solver.getNextIteration();
				solver.perform();
				
				// Get the results
				current_iteration = solver.getCurrentIteration();
				change = solver.getIterationChange();
				
				// Update status
				updateStatus(current_iteration,change);
				
				// Check interuptions
				if (Thread.interrupted()) {
                    throw new InterruptedException();
                }
				//System.out.println("iterating");
			}
        }
        catch (InterruptedException e) {
            updateStatus(max_iterations,0.0);
            return "Interrupted";  // SwingWorker.get() returns this
        }
        
        //System.out.println("done");
        return "All Done";         // or this
	}
	
	/** Constructs the worker thread and starts it on its way
	 * 
	 */
	private void build_worker() {
		/* Invoking start() on the SwingWorker causes a new Thread
		 * to be created that will call construct(), and then
		 * finished().  Note that finished() is called even if
		 * the worker is interrupted because we catch the
		 * InterruptedException in doWork().
		 */
		 worker = new SwingWorker() {
			public Object construct() {
				return doIterations();
			}
			public void finished() {
				Runnable finishup = new Runnable() {
		            public void run() {
				
		            	jIterationProgressBar.setValue(max_iterations);
				
		            	if(!solver.isSuccessful()) {
		            		jToleranceTextField.setText("FAILED");
		            		jIterationTextField.setText("FAILED");
		            	}
				
		            	// Need to close the dialog down
		            	jStopButton.setText("Close");
		            	
		            	// Assign the LSIterative solver to the LSFit analysis
		            	IterativeProgress.super.analysis = IterativeProgress.this.solver;
		            }
				};
				SwingUtilities.invokeLater(finishup);
			}
		};
	}
	
	/** Passes the constructed first iteration into the dialog for
	 * future iterations.  Upon receiving the LSIterative object, the
	 * worker thread is started.
	 * 
	 * @param initial_analysis an initialized LSIterative
	 */
	public void setAnalysis(LSIterative initial_analysis) {
		solver = initial_analysis;
		System.out.println("Building worker...");
		if(solver == null) 
			System.out.println("...but I have no solver :-(");
		build_worker();
	}
	
	/** Returns the current or final LSIterative back to the calling function
	 * 
	 * @return the current LSIterative object
	 */
	public LSIterative getAnalysis() {
		return solver;
	}
	
}  //  @jve:decl-index=0:visual-constraint="10,10"
