/*
 *  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.io.XMLio;

import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.xml.sax.SAXException;  
import org.xml.sax.SAXParseException;

// Necessary for loading analyses

import com.approximatrix.draco.analysis.*;
import com.approximatrix.draco.analysis.ui.*;
import com.approximatrix.draco.main.ui.MainWindow;
import com.approximatrix.draco.datamodel.StatTableModel;
import com.approximatrix.draco.fit.*;
import com.approximatrix.draco.fit.ui.*;
import com.approximatrix.draco.io.builders.AnalysisBuilder;

import java.beans.PropertyVetoException;

public class AnalysisIO {
	
	public static void SaveFitAnalysis(Element parent, FitChildDialog analysis_window) {
		Document base = parent.getOwnerDocument();
		
		Element analysis = base.createElement(AnalysisTags.ANALYSIS_ELEMENT);
		Element type = base.createElement(AnalysisTags.ANALYSIS_TYPE_ELEMENT);
		Text textNode = base.createTextNode(analysis_window.getAnalysis().getClass().getName());
		type.appendChild(textNode);
		analysis.appendChild(type);
		textNode = null;
		
		if(analysis_window.getAnalysis().getClass().getName().compareTo(SimpleMLEFit.class.getName())==0) {
			Element subtype = base.createElement(AnalysisTags.ANALYSIS_SUBTYPE_ELEMENT);
			textNode = base.createTextNode(((SimpleMLEFit)analysis_window.getAnalysis()).getFunction().getClass().getName());
			subtype.appendChild(textNode);
			analysis.appendChild(subtype);
		}
		
		Element description = base.createElement(AnalysisTags.ANALYSIS_NAME_ELEMENT);
		textNode = base.createTextNode(analysis_window.getTitle());
		description.appendChild(textNode);
		analysis.appendChild(description);
		textNode = null;
		
		// Analysis details
		LSFit data = analysis_window.getAnalysis();
		
		Element results = base.createElement(AnalysisTags.ANALYSIS_RESULTS_ELEMENT);
		textNode = base.createTextNode(data.getResults());
		results.appendChild(textNode);
		analysis.appendChild(results);
		textNode = null;
		
		Element dependent_var = base.createElement(AnalysisTags.ANALYSIS_DEPENDENT_VAR_ELEMENT);
		textNode = base.createTextNode(data.getDependentVariable());
		dependent_var.appendChild(textNode);
		analysis.appendChild(dependent_var);
		textNode = null;
		
		Element independent_var = base.createElement(AnalysisTags.ANALYSIS_INDEPENDENT_VAR_ELEMENT);
		int count = 0;
		if(data.getIndependentVariables() != null)
			count = data.getIndependentVariables().length;
		independent_var.setAttribute(AnalysisTags.ANALYSIS_CNT_ATTR, 
									 Integer.toString(count));
		for(int i=0;i<count;i++) {
			Element one_indep = base.createElement(AnalysisTags.ANALYSIS_ENTRY_ELEMENT);
			one_indep.setAttribute(AnalysisTags.ANALYSIS_NAME_SUBELEMENT, data.getIndependentVariables()[i]);
			independent_var.appendChild(one_indep);
		}
		analysis.appendChild(independent_var);

		if(data.getClass().isAssignableFrom(RunTSLS.class)) {
			Element instrument = base.createElement(AnalysisTags.ANALYSIS_INSTRUMENT_VAR_ELEMENT);
			if(((RunTSLS)data).getInstruments() != null)
				count = ((RunTSLS)data).getInstruments().length;
			instrument.setAttribute(AnalysisTags.ANALYSIS_CNT_ATTR, 
					 Integer.toString(count));
			for(int i=0;i<count;i++) {
				Element one_instr = base.createElement(AnalysisTags.ANALYSIS_ENTRY_ELEMENT);
				one_instr.setAttribute(AnalysisTags.ANALYSIS_NAME_SUBELEMENT, ((RunTSLS)data).getInstruments()[i]);
				instrument.appendChild(one_instr);
			}
			analysis.appendChild(instrument);
		}
		
		Element parameters = base.createElement(AnalysisTags.ANALYSIS_PARAMETERS_ELEMENT);
		count = 0;
		if(data.getCoef() != null && data.getCoefLabels() != null)
			count = data.getCoef().length;
		parameters.setAttribute(AnalysisTags.ANALYSIS_CNT_ATTR, 
									 Integer.toString(count));
		for(int i=0;i<count;i++) {
			Element one_parameter = base.createElement(AnalysisTags.ANALYSIS_ENTRY_ELEMENT);
			one_parameter.setAttribute(AnalysisTags.ANALYSIS_NAME_SUBELEMENT, data.getCoefLabels()[i]);
			one_parameter.setAttribute(AnalysisTags.ANALYSIS_VALUE_SUBELEMENT,Double.toString(data.getCoef()[i]));
			
			parameters.appendChild(one_parameter);
			one_parameter = null;
		}
		analysis.appendChild(parameters);
		
		Element residuals = base.createElement(AnalysisTags.ANALYSIS_RESIDUALS_ELEMENT);
		count = 0;
		if(data.getResiduals() != null)
			count = data.getResiduals().length;
		residuals.setAttribute(AnalysisTags.ANALYSIS_CNT_ATTR, 
									 Integer.toString(count));
		for(int i=0;i<count;i++) {
			Element one_residual = base.createElement(AnalysisTags.ANALYSIS_ENTRY_ELEMENT);
			one_residual.setAttribute(AnalysisTags.ANALYSIS_VALUE_SUBELEMENT,Double.toString(data.getResiduals()[i]));
			residuals.appendChild(one_residual);
		}
		analysis.appendChild(residuals);
		
		Element cov_matrix = base.createElement(AnalysisTags.ANALYSIS_COVARIANCE_MAT_ELEMENT);
		double[][] covariance = data.getCovarianceMatrix();
		if(covariance != null) {
			int size = covariance.length;
			cov_matrix.setAttribute(AnalysisTags.ANALYSIS_CNT_ATTR, Integer.toString(size));
			for(int i=0;i<size;i++) {
				Element row = base.createElement(AnalysisTags.ANALYSIS_COVARIANCE_MAT_ROW_SUBELEMENT);
				for(int j=0;j<size;j++) {
					Element one_mat = base.createElement(AnalysisTags.ANALYSIS_ENTRY_ELEMENT);
					one_mat.setAttribute(AnalysisTags.ANALYSIS_VALUE_SUBELEMENT,Double.toString(covariance[i][j]));
					row.appendChild(one_mat);
				}
				cov_matrix.appendChild(row);
				row = null;
			}
		} else {
			cov_matrix.setAttribute(AnalysisTags.ANALYSIS_CNT_ATTR, Integer.toString(0));
		}
		analysis.appendChild(cov_matrix);
		
		// For non-linear least squares, we need to save the equation
		if(analysis_window.getAnalysis().getClass().getName().compareTo(RunNLLS.class.getName())==0) {
			RunNLLS nlls_analysis = ((RunNLLS)analysis_window.getAnalysis());
			Element equation = base.createElement(AnalysisTags.ANALYSIS_EQUATION);
			textNode = base.createTextNode(nlls_analysis.getEquation());
			equation.appendChild(textNode);
			analysis.appendChild(equation);
		}
		
		// Finally, attach the analysis to the parent
		parent.appendChild(analysis);
	}
	
	public static void SaveSimpleAnalysis(Element parent, AnalysisChildDialog analysis_window) {
		Document base = parent.getOwnerDocument();
		
		Element analysis = base.createElement(AnalysisTags.ANALYSIS_ELEMENT);
		Element type = base.createElement(AnalysisTags.ANALYSIS_TYPE_ELEMENT);
		Text textNode = base.createTextNode(analysis_window.getAnalysis().getClass().getName());
		type.appendChild(textNode);
		analysis.appendChild(type);
		textNode = null;
		
		Element description = base.createElement(AnalysisTags.ANALYSIS_NAME_ELEMENT);
		textNode = base.createTextNode(analysis_window.getTitle());
		description.appendChild(textNode);
		analysis.appendChild(description);
		textNode = null;
		
		// Analysis details
		StandardAnalysis data = analysis_window.getAnalysis();
		
		Element results = base.createElement(AnalysisTags.ANALYSIS_RESULTS_ELEMENT);
		textNode = base.createTextNode(data.getResults());
		results.appendChild(textNode);
		analysis.appendChild(results);
		textNode = null;
		
		Element independent_var = base.createElement(AnalysisTags.ANALYSIS_INDEPENDENT_VAR_ELEMENT);
		int count = 0;
		if(data.getVariables() != null)
			count = data.getVariables().length;
		independent_var.setAttribute(AnalysisTags.ANALYSIS_CNT_ATTR, 
									 Integer.toString(count));
		for(int i=0;i<count;i++) {
			Element one_indep = base.createElement(AnalysisTags.ANALYSIS_ENTRY_ELEMENT);
			one_indep.setAttribute(AnalysisTags.ANALYSIS_NAME_SUBELEMENT, data.getVariables()[i]);
			independent_var.appendChild(one_indep);
		}
		analysis.appendChild(independent_var);
		
		// Finally, attach the analysis to the parent
		parent.appendChild(analysis);
	}
	
	
	public static AnalysisLoadStore LoadAnalysis(Element analysis, StatTableModel master) {
		AnalysisLoadStore analysis_data = null;
		
		Element temp = GetSingleElementByTagName(analysis,AnalysisTags.ANALYSIS_TYPE_ELEMENT);
		if(temp == null) return null;
		analysis_data = new AnalysisLoadStore(temp.getTextContent());
		
		temp = GetSingleElementByTagName(analysis,AnalysisTags.ANALYSIS_SUBTYPE_ELEMENT);
		if(temp != null) analysis_data.setAnalysisSubType(temp.getTextContent());
		
		temp = GetSingleElementByTagName(analysis,AnalysisTags.ANALYSIS_NAME_ELEMENT);
		if(temp != null) analysis_data.setName(temp.getTextContent());
		
		temp = GetSingleElementByTagName(analysis,AnalysisTags.ANALYSIS_RESULTS_ELEMENT);
		if(temp != null) analysis_data.setResults(temp.getTextContent());
		
		temp = GetSingleElementByTagName(analysis,AnalysisTags.ANALYSIS_DEPENDENT_VAR_ELEMENT);
		if(temp != null) analysis_data.setDependentVariable(temp.getTextContent());
		
		temp = GetSingleElementByTagName(analysis,AnalysisTags.ANALYSIS_INDEPENDENT_VAR_ELEMENT);
		if(temp != null) {
			String[] text = null;
			NodeList values = temp.getElementsByTagName(AnalysisTags.ANALYSIS_ENTRY_ELEMENT);
			if(values != null) {
				text = new String[values.getLength()];	
				for(int i=0;i<values.getLength() && i<values.getLength();i++) {
					text[i] = ((Element)values.item(i)).getAttribute(AnalysisTags.ANALYSIS_NAME_SUBELEMENT);
				}
				analysis_data.setIndependentVariables(text);
			}
		}
		
		temp = GetSingleElementByTagName(analysis,AnalysisTags.ANALYSIS_INSTRUMENT_VAR_ELEMENT);
		if(temp != null) {
			String[] text = null;
			NodeList values = temp.getElementsByTagName(AnalysisTags.ANALYSIS_ENTRY_ELEMENT);
			if(values != null) {
				text = new String[values.getLength()];	
				for(int i=0;i<values.getLength() && i<values.getLength();i++) {
					text[i] = ((Element)values.item(i)).getAttribute(AnalysisTags.ANALYSIS_NAME_SUBELEMENT);
				}
				analysis_data.setInstruments(text);
			}
		}
		
		temp = GetSingleElementByTagName(analysis,AnalysisTags.ANALYSIS_PARAMETERS_ELEMENT);
		if(temp != null) {
			String count_text = temp.getAttribute(AnalysisTags.ANALYSIS_CNT_ATTR);
			int count = 0;
			try  {
				count = new Integer(count_text).intValue();
			} catch (NumberFormatException nfe) {
				count = 0;
			}
			String[] text1 = new String[count];
			String[] text2 = new String[count];
			NodeList values = temp.getElementsByTagName(AnalysisTags.ANALYSIS_ENTRY_ELEMENT);
			if(values != null) {
				for(int i=0;i<count && i<values.getLength();i++) {
					text1[i] = ((Element)values.item(i)).getAttribute(AnalysisTags.ANALYSIS_NAME_SUBELEMENT);
					text2[i] = ((Element)values.item(i)).getAttribute(AnalysisTags.ANALYSIS_VALUE_SUBELEMENT);
				}
				analysis_data.setCoefLabels(text1);
				analysis_data.setCoefValues(text1);
			}
		}
		
		temp = GetSingleElementByTagName(analysis,AnalysisTags.ANALYSIS_RESIDUALS_ELEMENT);
		if(temp != null) {
			String[] text = null;
			NodeList values = temp.getElementsByTagName(AnalysisTags.ANALYSIS_ENTRY_ELEMENT);
			if(values != null) {
				text = new String[values.getLength()];	
				for(int i=0;i<values.getLength() && i<values.getLength();i++) {
					text[i] = ((Element)values.item(i)).getAttribute(AnalysisTags.ANALYSIS_VALUE_SUBELEMENT);
				}
				analysis_data.setResiduals(text);
			}
		}
		
		temp = GetSingleElementByTagName(analysis,AnalysisTags.ANALYSIS_COVARIANCE_MAT_ELEMENT);
		if(temp != null) {
			String count_text = temp.getAttribute(AnalysisTags.ANALYSIS_CNT_ATTR);
			int count = 0;
			try  {
				count = new Integer(count_text).intValue();
			} catch (NumberFormatException nfe) {
				count = 0;
			}
			String[][] text = new String[count][count];
			NodeList rows = temp.getElementsByTagName(AnalysisTags.ANALYSIS_COVARIANCE_MAT_ROW_SUBELEMENT);
			if(rows != null) {
				for(int i=0;i<count && i<rows.getLength();i++) {
					
					NodeList row_entries = ((Element)rows.item(i)).getElementsByTagName(AnalysisTags.ANALYSIS_ENTRY_ELEMENT);
					if(row_entries != null) {
						for(int j=0;j<count && j<row_entries.getLength();j++) {
							text[i][j] = ((Element)row_entries.item(j)).getAttribute(AnalysisTags.ANALYSIS_VALUE_SUBELEMENT);
						}
					}
				}
				analysis_data.setCovarianceMatrix(text);
			}
		}
		
		// The equation for NLLS
		temp = GetSingleElementByTagName(analysis,AnalysisTags.ANALYSIS_EQUATION);
		if(temp != null) {
			analysis_data.setEquation(temp.getTextContent());
		}
		
		return analysis_data;
	}
	
	private static Element GetSingleElementByTagName(Element parent, String tag) {
		NodeList temp = parent.getElementsByTagName(tag);
		if(temp==null) return null;
		if(temp.getLength() == 0) return null;
		return (Element)temp.item(0);
	}
	
	public static void BuildAnalysis(Element analysis_node, MainWindow parent) {
		
		// Build the AnalysisLoadStore object
		AnalysisLoadStore analysis = LoadAnalysis(analysis_node,parent.getWindowTableModel());
		
		AnalysisBuilder.BuildAnalysis(analysis, parent, parent.getWindowTableModel());
		
	}
}
