import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import java.util.ArrayList;
import java.util.Arrays;

/**
 * Created by shumailovr on 27.04.15.
 */

@Entity
@Table(name = "testing_results")
public class TestingResultsModel {

    private int id;
    private double diffDispersion;
    private double diffMedian;
    private double minDifference;
    private double maxDifference;
    private double averDifference;
    //How much data was preserved as training data out of overall data
    private double trainingDataPercentage;

    public TestingResultsModel() {
    }

    public TestingResultsModel(int id, double diffDispersion, double diffMedian, double minDifference, double maxDifference, double averDifference, double trainingDataPercentage) {
        this.id = id;
        this.diffDispersion = diffDispersion;
        this.diffMedian = diffMedian;
        this.minDifference = minDifference;
        this.maxDifference = maxDifference;
        this.averDifference = averDifference;
        this.trainingDataPercentage = trainingDataPercentage;
    }

    @Id
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    @Column(name = "diff_dispersion")
    public double getDiffDispersion() {
        return diffDispersion;
    }

    public void setDiffDispersion(double diffDispersion) {
        this.diffDispersion = diffDispersion;
    }

    @Column(name = "diff_median")
    public double getDiffMedian() {
        return diffMedian;
    }

    public void setDiffMedian(double diffMedian) {
        this.diffMedian = diffMedian;
    }

    @Column(name = "min_difference")
    public double getMinDifference() {
        return minDifference;
    }

    public void setMinDifference(double minDifference) {
        this.minDifference = minDifference;
    }

    @Column(name = "max_difference")
    public double getMaxDifference() {
        return maxDifference;
    }

    public void setMaxDifference(double maxDifference) {
        this.maxDifference = maxDifference;
    }

    @Column(name = "aver_difference")
    public double getAverDifference() {
        return averDifference;
    }

    public void setAverDifference(double averDifference) {
        this.averDifference = averDifference;
    }

    @Column(name = "training_data_percentage")
    public double getTrainingDataPercentage() {
        return trainingDataPercentage;
    }

    public void setTrainingDataPercentage(double trainingDataPercentage) {
        this.trainingDataPercentage = trainingDataPercentage;
    }


    /**
     * Compares got differences with reserved test data, finds out:
     * 1) Mediana of differences
     * 2) Differences dispersion
     * 3) What was minimum difference
     * 4) What was maximum difference
     * 5) What was average difference
     */
    public static void compareResultsWithTestData() {
        PersistenceManager persistenceManager = new PersistenceManager();

        int totalCount = 0;
        double minDifference = Double.MAX_VALUE;
        double maxDifference = 0;
        double averDifference = 0;
        double distancesSum = 0;
        double diffDispersion = 0;
        double diffMedian;
        ArrayList<Double> differences = new ArrayList<>();
        //dispersioon,mediaan,bias -diffwithoutabs

        ArrayList<TestingEuclideanDistanceModel> euclDistances = persistenceManager.getTestingEuclideanDistances();

        for (TestingEuclideanDistanceModel euclDist : euclDistances) {
            BookDifferenceModel resultDist;
            resultDist = persistenceManager.getBookDifferenceByIsbns(
                    euclDist.getTestingEuclideanDistanceId().getIsbn1(), euclDist.getTestingEuclideanDistanceId().getIsbn2()
            );
            if (resultDist == null) {
                resultDist = persistenceManager.getBookDifferenceByIsbns(
                        euclDist.getTestingEuclideanDistanceId().getIsbn2(), euclDist.getTestingEuclideanDistanceId().getIsbn1()
                );
            }

            System.out.println(resultDist);
            System.out.println("ISBN1: " + euclDist.getTestingEuclideanDistanceId().getIsbn1());
            System.out.println("ISBN2: " + euclDist.getTestingEuclideanDistanceId().getIsbn2());

            double testingDistance = euclDist.getDistance();
            System.out.println("TESTING DISTANCE: " + testingDistance);
            double resultDistance = resultDist.getDifference();
            System.out.println("CALCULATED DISTANCE: " + resultDistance);

            double difference = Math.abs(testingDistance - resultDistance);

            //Save for dispersion calc
            differences.add(difference);

            if (difference < minDifference) {
                minDifference = Math.abs(testingDistance - resultDistance);
            }
            if (difference > maxDifference) {
                maxDifference = Math.abs(testingDistance - resultDistance);
            }

            distancesSum += difference;
            totalCount += 1;
        }

        averDifference = distancesSum / totalCount;

        //Calc dispersion
        double dispSum = 0;
        for (Double difference : differences) {
            dispSum += Math.pow(difference - averDifference, 2);
        }
        diffDispersion = dispSum / (totalCount - 1);

        //Calc median
        Double[] differencesArray = new Double[differences.size()];
        differencesArray = differences.toArray(differencesArray);
        Arrays.sort(differencesArray);
        if (differencesArray.length % 2 == 0)
            diffMedian = (differencesArray[differencesArray.length / 2] + differencesArray[differencesArray.length / 2 - 1]) / 2;
        else
            diffMedian = differencesArray[differencesArray.length/2];

        //Save results of testing
        TestingResultsModel testResult = new TestingResultsModel();
        testResult.setMinDifference(minDifference);
        testResult.setMaxDifference(maxDifference);
        testResult.setAverDifference(averDifference);
        testResult.setDiffDispersion(diffDispersion);
        testResult.setDiffMedian(diffMedian);
        //Change manually when needed
        testResult.setTrainingDataPercentage(90);

        persistenceManager.saveTestingResult(testResult);
    }
}
