package tests.distributions;

import PIANOS.datastructures.*;
import PIANOS.exceptions.*;
import java.io.*;
import java.util.ArrayList;

//This class is used to test all distributions, DistributionFactory and DistributionSkeleton

public class DistributionTester{

    private static FileWriter writer = null;

    public static void run() throws IOException{

	writer = new FileWriter(new File("tests/distributions/results.txt"));

	write("Distribution class test results");
	write("\nDistributions are tested first");
	write("Check that the Fortran function calls are correct");

	try{
	    write("\n\n---BetaDistribution---\n\n");
	    testDistribution(new BetaDistribution());
	}catch (IllegalParametersException e){
	    write("IllegalParametersException detected in BetaDistribution class");
	    write(e.toString());
	}

	try{
	    write("\n\n---BinomialDistribution---\n\n");
	    testDistribution(new BinomialDistribution());
	}catch (IllegalParametersException e){
	    write("IllegalParametersException detected in BinomicalDistribution class");
	    write(e.toString());
	}

	try{
	    write("\n\n---ContinuousUniformDistribution---\n\n");
	    testDistribution(new ContinuousUniformDistribution());
	}catch (IllegalParametersException e){
	    write("IllegalParametersException detected in ContinuousUniformDistribution class");
	    write(e.toString());
	}

	try{
	    write("\n\n---DiscreteUniformDistribution---\n\n");
	    testDistribution(new DiscreteUniformDistribution());
	}catch (IllegalParametersException e){
	    write("IllegalParametersException detected in DiscreteUniformDistribution class");
	    write(e.toString());
	}

	try{
	    write("\n\n---PoissonDistribution---\n\n");
	    testDistribution(new PoissonDistribution());
	}catch (IllegalParametersException e){
	    write("IllegalParametersException detected in PoissonDistribution class");
	    write(e.toString());
	}

	write("\n\n----------------------------------------------------------------------------\n\n");
	write("DistributionFactory tests. 1st test: check that factory returns right distributions.");

	DistributionFactory fact = null;

	try{
	   fact = new DistributionFactory("tests/distributions/empty.txt");
	}catch (Exception e){
	    write("Exception detected when trying to construct a new DistributionFactory with an empty file");
	    write("\n" + e.toString());
	}

	write("\n\nTesting factory with distribution names 'discrete_uniform', 'DISCRETE_UNIFOM', 'DiScReTe_UnIfOrM'\n\n");
	try{
	    write(fact.getDistribution("discrete_uniform").getClass().getName() + "\n");
	}catch (MissingDistributionException e){
	    write("Distribution name 'discrete_uniform' failed!");
	}try{
	    write(fact.getDistribution("DISCRETE_UNIFORM").getClass().getName() + "\n");
	}catch (MissingDistributionException e){
	    write("Distribution name 'DISCRETE_UNIFOM' failed!");
	}try{
	    write(fact.getDistribution("DiScReTe_UnIfOrM").getClass().getName() + "\n");
	}catch (MissingDistributionException e){
 	    write("Distribution name 'DiScReTe_UnIfOrM' failed!");
	}

	write("\n\nTesting factory with distribution names 'binomial', 'BINOMIAL', 'BiNoMiAl'\n\n");
	try{
	    write(fact.getDistribution("binomial").getClass().getName() + "\n");
	}catch (MissingDistributionException e){
	    write("Distribution name 'binomial' failed!");
	}try{
	    write(fact.getDistribution("BINOMIAL").getClass().getName() + "\n");
	}catch (MissingDistributionException e){
	    write("Distribution name 'BINOMIAL' failed!");
	}try{
	    write(fact.getDistribution("BiNoMiAl").getClass().getName() + "\n");
	}catch (MissingDistributionException e){
 	    write("Distribution name 'BiNoMiAl' failed!");
	}

	write("\n\nTesting factory with distribution names 'poisson', 'POISSON', 'PoIsSoN'\n\n");
	try{
	    write(fact.getDistribution("poisson").getClass().getName() + "\n");
	}catch (MissingDistributionException e){
	    write("Distribution name 'poisson' failed!");
	}try{
	    write(fact.getDistribution("POISSON").getClass().getName() + "\n");
	}catch (MissingDistributionException e){
	    write("Distribution name 'POISSON' failed!");
	}try{
	    write(fact.getDistribution("PoIsSoN").getClass().getName() + "\n");
	}catch (MissingDistributionException e){
 	    write("Distribution name 'PoIsSoN' failed!");
	}

	write("\n\nTesting factory with distribution names 'continuous_uniform', 'CONTINUOUS_UNIFORM', 'CoNtInUoUs_UnIfOrM'\n\n");
	try{
	    write(fact.getDistribution("continuous_uniform").getClass().getName() + "\n");
	}catch (MissingDistributionException e){
	    write("Distribution name 'continuous_uniform' failed!");
	}try{
	    write(fact.getDistribution("CONTINUOUS_UNIFORM").getClass().getName() + "\n");
	}catch (MissingDistributionException e){
	    write("Distribution name 'CONTINUOUS_UNIFORM' failed!");
	}try{
	    write(fact.getDistribution("CoNtInUoUs_UnIfOrM").getClass().getName() + "\n");
	}catch (MissingDistributionException e){
 	    write("Distribution name 'CoNtInUoUs_UnIfOrM' failed!");
	}

	write("\n\nTesting factory with distribution names 'beta', 'BETA', 'BeTa'\n\n");
	try{
	    write(fact.getDistribution("beta").getClass().getName() + "\n");
	}catch (MissingDistributionException e){
	    write("Distribution name 'beta' failed!");
	}try{
	    write(fact.getDistribution("BETA").getClass().getName() + "\n");
	}catch (MissingDistributionException e){
	    write("Distribution name 'BETA' failed!");
	}try{
	    write(fact.getDistribution("BeTa").getClass().getName() + "\n");
	}catch (MissingDistributionException e){
 	    write("Distribution name 'BeTa' failed!");
	}

	write("\n\n----------------------------------------------------------------------------\n\n");
	write("DistributionFactory test. 2nd test: non empty user defined distribution files.");
	write("\nUser defined distrubution files are syntaxly correct");
	write("\nDistributionFactory should read the file and construct a correct UserDefinedDistribution object.");
	write("\nUserDefinedDistribution and DistributionSkeleton are tested here too.");

	write("\n\nUser defined distribution file: 'user1.txt'\n\n");

	try{
	    fact = new DistributionFactory("tests/distributions/user1.txt");
	}catch (Exception e){
	    write("Exception detected!");
	    write("\n" + e.toString());
	}

	testUser("\n\n--Testing ! user_alpha 2 real integer .true.--\n\n", "AlpHa", 2, fact);
	testUser("\n\n--Testing ! user_BETA 2 real integer .true.--\n\n", "beta", 2, fact);
	testUser("\n\n--Testing ! user_GaMmA 2 real integer .true.--\n\n", "GAMMA", 2, fact);
	testUser("\n\n--Testing ! USER_delta 2 real integer .true.--\n\n", "delta", 2, fact);
	testUser("\n\n--Testing ! UsEr_EtA 2 real interger .true.--\n\n", "ETA", 2, fact);
	testUser("\n\n--Testing ! user_theta 2 REAL INTEGER .true.--\n\n", "TheTa", 2, fact);
	testUser("\n\n--Testing ! user_iota 2 ReAl InTeGeR .true.--\n\n", "iota", 2, fact);
	testUser("\n\n--Testing ! user_kappa 1 integer .true.--\n\n", "kappa", 1, fact);
	testUser("\n\n--Testing ! user_lambda 3 real interger real .true.--\n\n", "lambda", 3, fact);
	testUser("\n\n--Testing ! user_mu 2 real interger .false.--\n\n", "MU", 2, fact);
	testUser("\n\n--Testing ! user_nu 2 integer real .false.--\n\n", "Nu", 2, fact);
	testUser("\n\n--Testing ! user_xhi 2 real integer .FALSE.--\n\n", "XhI", 2, fact);
	testUser("\n\n--Testing ! user_tau 2 integer real .TRUE.--\n\n", "taU", 2, fact);
	testUser("\n\n--Testing !         user_phi       2       real    integer       .true.--\n\n", "PHI", 2, fact);
	testUser("\n\n--Testing !user_sigma 2 integer real .true.--\n\n", "SigMa", 2, fact);
	testUser("\n\n--Testing ! user_omega 2 REAL INTEGER .TRUE.--\n\n", "omega", 2, fact);

	write("\n\n----------------------------------------------------------------------------\n\n");
	write("DistributionFactory test. 3rd test: invalid definition format.");

	write("\n\nFile: user2.txt");
	write("\nInvalid format: !use_alpha 2 REAL REAL .TRUE.");
	write("\nResult: ");

	try{
	    fact = new DistributionFactory("tests/distributions/user2.txt");
	}catch (Exception e){
	    write("Exception detected!");
	    write("\n" + e.toString());
	    write("\nWORKING AS INTENDED!");
	}


	write("\n\nFile: user3.txt");
	write("\nInvalid format: !user_alpha 2 INTEGER .TRUE.");
	write("\nResult: ");

	try{
	    fact = new DistributionFactory("tests/distributions/user3.txt");
	}catch (Exception e){
	    write("Exception detected!");
	    write("\n" + e.toString());
	    write("\nWORKING AS INTENDED!");
	}


	write("\n\nFile: user4.txt");
	write("\nInvalid format: !user_alpha 3 REAL REAL .TRUE.");
	write("\nResult: ");

	try{
	    fact = new DistributionFactory("tests/distributions/user4.txt");
	}catch (Exception e){
	    write("Exception detected!");
	    write("\n" + e.toString());
	    write("\nWORKING AS INTENDED!");
	}

	write("\n\nFile: user5.txt");
	write("\nInvalid format: !user_alpha 2 REAL REAL TRUE");
	write("\nResult: ");

	try{
	    fact = new DistributionFactory("tests/distributions/user5.txt");
	}catch (Exception e){
	    write("Exception detected!");
	    write("\n" + e.toString());
	    write("\nWORKING AS INTENDED!");
	}

	write("\n\nFile: user5.txt");
	write("\nInvalid format: !user_alpha 2 REAL REAL");
	write("\nResult: ");

	try{
	    fact = new DistributionFactory("tests/distributions/user6.txt");
	}catch (Exception e){
	    write("Exception detected!");
	    write("\n" + e.toString());
	    write("\nWORKING AS INTENDED!");
	}

	write("\n\n----------------------------------------------------------------------------\n\n");
	write("DistributionFactory test. 4th test: misc tests");
	write("\nTesting of functions which are not defined in any document.");

	write("\n\nProblem: 0 parameters for a function");
	write("\nFile: user7.txt");
	write("\nFormat: !user_alpha 0 .TRUE.");

	try{
	    fact = new DistributionFactory("tests/distributions/user7.txt");
	    write("\nInput accepted! Testing generator and frequency function!\n");
	    write("\nGeneration code:\n");
	    String[] temp = {"Result"};
	    write(fact.getDistribution("user_alpha").getGenCode(temp));
	    write("\n\nFrequency code:\n");
	    String[] temp2 = {"Point", "Result"};
	    write(fact.getDistribution("user_alpha").getFreqCode(temp2));
	    write("\n\nConclusion: 0 parameters are allowed to a distribution\n");
	}catch (Exception e){
	    write("Exception detected!");
	    write("\n" + e.toString());
	    write("\n??? WORKING AS INTENDED ???");
	}

	write("\n\nProblem: distribution defined twice.");
	write("\nFile: user8.txt");
	write("\nFormat: !user_alpha 2 real integer .false.");
	write("\nFormat: !user_alpha 2 real integer .true.");

	try{
	    fact = new DistributionFactory("tests/distributions/user8.txt");
	    write("\nInput accepted! Testing generator and frequency function!\n");
	    write("\nGeneration code:\n");
	    String[] temp = {"Param1", "Param2", "Result"};
	    write(fact.getDistribution("user_alpha").getGenCode(temp));
	    write("\n\nFrequency code:\n");
	    String[] temp2 = {"Param1", "Param2", "Point", "Result"};
	    write(fact.getDistribution("user_alpha").getFreqCode(temp2));
	}catch (Exception e){
	    write("Exception detected!");
	    write("\n" + e.toString());
	    write("\nWORKING AS INTENDED");
	}



	write("\n\nTest has been completed");

    }

    private static void write(String toWrite) throws IOException{
	writer.write(toWrite);
	writer.flush();
    }

    private static void write(ArrayList<String> toWrite) throws IOException{
	for(String iterator : toWrite)
	    writer.write(iterator + "\n");
	writer.flush();
    }

    private static void testDistribution(Distribution dist) throws IOException, IllegalParametersException{
	int parameters = dist.getNumberOfParameters();

	write("The distribution has " + parameters + " parameters");
	String[] genPara = new String[parameters + 1];
	String[] freqPara = new String[parameters + 2];

	for (int i = 0; i < parameters; i++){
	    genPara[i] = "Param" + (i+1);
	    freqPara[i] = "Param" + (i+1);
	}

	genPara[parameters] = "Result variable";
	freqPara[parameters] = "Point";
	freqPara[parameters + 1] = "Result variable";

	try{
	write("\n\nTesting introduction\n\n");
	write(dist.getIntroduction());

	write("\n\nTesting generator function\n\n");
	write(dist.getGenCode(genPara));

	write("\n\nTesting frequency function\n\n");
	write(dist.getFreqCode(freqPara));
	}catch (MissingFunctionException e){
	    //This can't happen, it's impossible. Hard coded distributions just simply won't throw this....
	}
    }

    private static void testUser(String tested, String name, int parameters, DistributionFactory fact) throws IOException{

		String[] oneGen = {"Param1", "Result"};
		String[] oneFreq = {"Param1", "Point", "Result"};
		String[] twoGen = {"Param1", "Param2", "Result"};
		String[] twoFreq = {"Param1", "Param2", "Point", "Result"};
		String[] threeGen = {"Param1", "Param2", "Param3", "Result"};
		String[] threeFreq = {"Param1", "Param2", "Param3", "Point", "Result"};


		try{
			write(tested);
			write("Call name is user_" + name + "\n");
			if (parameters == 1){
	    		write("Generation code:\n");
	    		write(fact.getDistribution("user_" + name).getGenCode(oneGen));
	    		write("\n\nFrequency code:\n");
	    		write(fact.getDistribution("user_" + name).getFreqCode(oneFreq));
			}else if (parameters == 2){
	    		write("Generation code:\n");
	    		write(fact.getDistribution("user_" + name).getGenCode(twoGen));
	    		write("\n\nFrequency code:\n");
	    		write(fact.getDistribution("user_" + name).getFreqCode(twoFreq));
			}else if (parameters == 3){
	    		write("Generation code:\n");
	    		write(fact.getDistribution("user_" + name).getGenCode(threeGen));
	    		write("\n\nFrequency code:\n");
	    		write(fact.getDistribution("user_" + name).getFreqCode(threeFreq));
			}
		}catch (MissingDistributionException e){
		    write("Distribution name " + name + " failed! Call name was " + name);
		}catch (IllegalParametersException e){
			write("Illegal number of parameters!");
			write(e.toString());
		}catch (MissingFunctionException e){
			write("Generator function was not found for " + name);
		}

    }
}
