package com.approximatrix.draco.io;

import static org.junit.Assert.*;

import javax.swing.SwingUtilities;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import com.approximatrix.draco.analysis.RunANOVA;
import com.approximatrix.draco.analysis.StandardAnalysis;
import com.approximatrix.draco.analysis.ui.AnalysisChildDialog;
import com.approximatrix.draco.application.ChildFrame;
import com.approximatrix.draco.application.plugin.PluginData;
import com.approximatrix.draco.application.plugin.ui.PluginDataProvidingWindow;
import com.approximatrix.draco.fit.LSFit;
import com.approximatrix.draco.fit.RunLS;
import com.approximatrix.draco.fit.RunNLLS;
import com.approximatrix.draco.fit.RunTSLS;
import com.approximatrix.draco.fit.SimpleMLEFit;
import com.approximatrix.draco.fit.ui.FitChildDialog;
import com.approximatrix.draco.general.ui.InfoWindow;
import com.approximatrix.draco.main.ui.SyntheticMainWindow;
import com.approximatrix.draco.plot.ui.PlotDialog;

public class LoadDracoSQLTest {

	protected static final String FULL_LOAD_FILE = "resources/full.sql-draco";
	protected static final Class[] ANALYSES = {RunANOVA.class};
	protected static final Class[] REGRESSIONS = {RunLS.class, RunNLLS.class, RunTSLS.class, SimpleMLEFit.class, SimpleMLEFit.class};
	protected static final int PLOT_COUNT = 2;
	protected static final int REGRESSION_PLUGIN_COUNT = 1;
	protected static final int ANALYSIS_PLUGIN_COUNT = 0;
	protected static final int PLUGIN_COUNT = REGRESSION_PLUGIN_COUNT+ANALYSIS_PLUGIN_COUNT;
		
	private SyntheticMainWindow smw = null;
	private LoadDracoSQL loader = null;
	
	@Before
	public void setUp() throws Exception {
		smw = new SyntheticMainWindow();
		loader = new LoadDracoSQL(FULL_LOAD_FILE, smw);
	}

	@After
	public void tearDown() throws Exception {
		for(ChildFrame frame : smw.frames)
			frame.dispose();
		smw = null;
	}

	@Test
	public void testDoStep() {
		// Initialize loading
		loader.doStep();
		assertTrue(loader.stepSuccess());
		
		// Load the table model
		loader.doStep();
		assertTrue(loader.stepSuccess());
		assertNotNull(loader.getLoadedData());
		smw.setWindowTableModel(loader.getLoadedData());
		
		// Load constants
		loader.doStep();
		assertTrue(loader.stepSuccess());
		assertEquals(1.0,loader.getLoadedData().getConstantValue("const1").doubleValue());
		assertEquals(0.02,loader.getLoadedData().getConstantValue("const2").doubleValue());
		assertEquals(300.0,loader.getLoadedData().getConstantValue("const3").doubleValue());
		
		// Load analyses
		loader.doStep();
		assertTrue(loader.stepSuccess());
		validateAnalyses();
		
		// Load regressions
		loader.doStep();
		assertTrue(loader.stepSuccess());
		validateRegressions();
		
		// Load plots
		loader.doStep();
		assertTrue(loader.stepSuccess());
		validatePlots();
		
		// Load texts
		loader.doStep();
		assertTrue(loader.stepSuccess());
		assertNotNull(smw.getWindowsByClass(InfoWindow.class));
		assertTrue(smw.getWindowsByClass(InfoWindow.class).length > 0);
		
		// Load Plugins
		loader.doStep();
		assertTrue(loader.stepSuccess());
		validatePlugins();
		
		// Close
		loader.doStep();
		assertTrue(loader.stepSuccess());
	}

	private boolean[] buildFoundArray(int length) {
		boolean[] ret = new boolean[length];
		for(int i=0;i<length;i++)
			ret[i] = false;
		return ret;
	}
	
	private void updateFound(boolean[] found, Class[] toFind, Class current) {
		for(int i=0;i<toFind.length;i++) {
			if(current == toFind[i] && !found[i]) {
				found[i] = true;
				return;
			}
		}
		
		// This point should not be reached
		fail("An unexpected class was loaded...");
	}
	
	private boolean allFound(boolean[] found) {
		for(int i=0;i<found.length;i++) {
			if(!found[i])
				return false;
		}
		return true;
	}
	
	private void validatePlugins() {
		Object[] pluginObjects = smw.getWindowsByClass(PluginDataProvidingWindow.class);
		assertNotNull(pluginObjects);
		assertEquals(PLUGIN_COUNT,pluginObjects.length);
		for(int i=0;i<pluginObjects.length;i++) {
			try {
				assertNotNull(((PluginDataProvidingWindow)pluginObjects[i]).getTitle());
				PluginData pd = ((PluginDataProvidingWindow)pluginObjects[i]).getPluginData();
				assertNotNull(pd);
				
				assertNotNull(pd.getResults());
				assertNotNull(pd.getType());
				assertNotNull(pd.getName());
				
			} catch(ClassCastException cce) {
				fail("A class cast exception has prevented the test from running...");
			}
		}
	}
	
	private void validatePlots() {
		Object[] plotObjects = smw.getWindowsByClass(PlotDialog.class);
		assertNotNull(plotObjects);
		assertEquals(PLOT_COUNT,plotObjects.length);
	}
	
	private void validateAnalyses() {
		boolean[] found = buildFoundArray(ANALYSES.length); 
		
		Object[] analysisObjects = smw.getWindowsByClass(AnalysisChildDialog.class);
		assertNotNull(analysisObjects);
		
		for(int i=0;i<analysisObjects.length;i++) {
			try {
				StandardAnalysis analysis = ((AnalysisChildDialog)analysisObjects[i]).getAnalysis();
				assertNotNull(analysis);
				assertNotNull(analysis.getResults());
				assertTrue(analysis.isSuccessful());
				
				updateFound(found, ANALYSES, analysis.getClass());
				
			} catch(ClassCastException cce) {
				fail("A class cast exception has prevented the test from running...");
			}
		}
		
		assertTrue("Some expected analyses were not loaded...",allFound(found));
	}

	private void validateRegressions() {
		boolean[] found = buildFoundArray(REGRESSIONS.length); 
		
		Object[] fitObjects = smw.getWindowsByClass(FitChildDialog.class);
		assertNotNull(fitObjects);
		
		for(int i=0;i<fitObjects.length;i++) {
			try {
				LSFit analysis = ((FitChildDialog)fitObjects[i]).getAnalysis();
				assertNotNull(analysis);
				assertNotNull(analysis.getResults());
				assertTrue(analysis.isSuccessful());
				
				updateFound(found, REGRESSIONS, analysis.getClass());
				
			} catch(ClassCastException cce) {
				fail("A class cast exception has prevented the test from running...");
			}
		}
		
		assertTrue("Some expected regressions were not loaded...",allFound(found));
	}
	
}
