//*******************************************************************************************************************************************
// This methods of this class calculate various statistics related to the model's performance.
//*******************************************************************************************************************************************

package ezreader10;

import java.io.PrintStream;
import java.util.*;

class Stats {
    
    // Initialize class:
    Display display = new Display();
    
    //**************************************************************************
     
    void analyses(PrintStream diskWriter, ArrayList<Sentence> text) {
        
        // Note: This first method is essentially a list of all possible statistical analyses.
        
        // Calculate word-based means for all words:
        wordMeans(text);
        
        // Initialize array list for frequency-class means and RMSD:
        ArrayList<Word> frequencyClassMeans = new ArrayList<>();
        for (int i = 0; i < EZReader10.NFrequencyClasses; i++) {
            Word mean = new Word();
            frequencyClassMeans.add(mean);
        }
        
        // Calculate frequency-class means:
        frequencyClassMeans(diskWriter, text, frequencyClassMeans);
        
        // Calculate RMSD:
        RMSD(diskWriter, text, frequencyClassMeans);
        
        // Calculate proportion of trials containing interword regressions:
        PrRegressions(diskWriter, text);
        
        // Display the word-based means and distributions for all words:
        if (EZReader10.displayWordStatistics == true) {
            display.wordMeans(diskWriter, text);            
            display.distPr1(diskWriter, text);
            display.distPr2(diskWriter, text);
            display.distSFD(diskWriter, text);
        }
        
        // Calculate and display distributions for words of each length:
        if (EZReader10.displayMeanDistributions == true) {
                           
            // Initialize array list for distributions:
            ArrayList<Word> dists = new ArrayList<>();
            for (int i = 0; i < EZReader10.maxLength; i++) {
                Word dist = new Word();
                dists.add(dist);
            }
            
            // Calculate and display Pr1 distributions:    
            distPr1(text, dists);
            display.meanDistPr1(diskWriter, dists);
            
            // Calculate and display Pr2 distributions:
            distPr2(text, dists);
            display.meanDistPr2(diskWriter, dists);
            
            // Calculate and display SFD distributions:
            distSFD(text, dists);
            display.meanDistSFD(diskWriter, dists);                       
        }
    }
    
    //**************************************************************************
    
    void distPr1(ArrayList<Sentence> text, ArrayList<Word> meanDistPr1) {
        
        // Counters for the number of words of each length:
        int N[] = new int[EZReader10.maxLength];
        for (int i = 0; i < EZReader10.maxLength; i++) N[i] = 0;
        
        // Calculate distributions:
        for (int length = 0; length < EZReader10.maxLength; length++) {
            
            for (int i = 0; i < EZReader10.NSentences; i++) {
                for (int j = 1; j < text.get(i).numberWords - 1; j++) {
                    if (text.get(i).get(j).iv.length == length) {
                        if (text.get(i).get(j).dv.Pr1 != 0) {
                            for (int k = 0; k < EZReader10.maxLength; k++) meanDistPr1.get(length).dv.distPr1[k] += 
                                    text.get(i).get(j).dv.distPr1[k];
                            N[length]++;
                        }
                    }
                }
            }
        }
        
        for (int length = 0; length < EZReader10.maxLength; length++) {
            for (int i = 0; i < EZReader10.maxLength; i++) if (N[length] > 0) meanDistPr1.get(length).dv.distPr1[i] /= N[length];
        }
    }
    
    //**************************************************************************
     
    void distPr2(ArrayList<Sentence> text, ArrayList<Word> meanDistPr2) {
        
        // Counters for the number of words of each length:
        int N[] = new int[EZReader10.maxLength];
        for (int i = 0; i < EZReader10.maxLength; i++) N[i] = 0;
        
        // Caclulate distributions:
        for (int length = 0; length < EZReader10.maxLength; length++) {
            for (int i = 0; i < EZReader10.NSentences; i++) {
                for (int j = 1; j < text.get(i).numberWords - 1; j++) {
                    if (text.get(i).get(j).iv.length == length) {
                        for (int k = 0; k < EZReader10.maxLength; k++) meanDistPr2.get(length).dv.distPr2[k] += 
                                text.get(i).get(j).dv.distPr2[k];
                        N[length]++;
                    }
                }
            }
        }
        
        for (int length = 0; length < EZReader10.maxLength; length++) {
            for (int i = 0; i < EZReader10.maxLength; i++) if (N[length] > 0) meanDistPr2.get(length).dv.distPr2[i] /= N[length];
        }
    }
    
    //**************************************************************************
     
    void distSFD(ArrayList<Sentence> text, ArrayList<Word> meanDistSFD) {
        
        // Calculate distributions:
        for (int length = 0; length < EZReader10.maxLength; length++) {
            
            for (int i = 0; i < EZReader10.NSentences; i++) {
                for (int j = 1; j < text.get(i).numberWords - 1; j++) {
                    if (text.get(i).get(j).iv.length == length) {
                        for (int k = 0; k < EZReader10.maxLength; k++) {
                            if (text.get(i).get(j).dv.distSFD[k] > 0) {
                                meanDistSFD.get(length).dv.distSFD[k] += text.get(i).get(j).dv.distSFD[k]; 
                                meanDistSFD.get(length).dv.NSFD[k]++;
                            }
                        }
                    }
                }
            }
        }
        
        for (int length = 0; length < EZReader10.maxLength; length++) {
            for (int i = 0; i < EZReader10.maxLength; i++) if (meanDistSFD.get(length).dv.NSFD[i] > 0) meanDistSFD.get(length).dv.distSFD[i] /= 
                    meanDistSFD.get(length).dv.NSFD[i];
        }
    }
    
    //**************************************************************************
     
    void frequencyClassMeans(PrintStream diskWriter, ArrayList<Sentence> text, ArrayList<Word> frequencyClass) {
        
        // Counters for number of words in each frequency class:
        int[] N = new int[] { 0, 0, 0, 0, 0, 0, 0, 0 };  
    
        // Caculate DV totals for each frequency class:
        for (int i = 0; i < EZReader10.NSentences; i++) {
            for (int j = 1; j < text.get(i).numberWords - 1; j++) {
                int k = text.get(i).get(j).iv.frequencyClass;
                frequencyClass.get(k).dv.SFD += text.get(i).get(j).dv.SFD;
                frequencyClass.get(k).dv.FFD += text.get(i).get(j).dv.FFD;
                frequencyClass.get(k).dv.GD += text.get(i).get(j).dv.GD;
                frequencyClass.get(k).dv.Pr1 += text.get(i).get(j).dv.Pr1;
                frequencyClass.get(k).dv.Pr2 += text.get(i).get(j).dv.Pr2;
                frequencyClass.get(k).dv.PrS += text.get(i).get(j).dv.PrS;
                N[k]++;
            }
        }
        
        // Calculate frequency-class means:
        for (int i = 0; i < EZReader10.NFrequencyClasses; i++) {
            frequencyClass.get(i).dv.SFD /= N[i];
            frequencyClass.get(i).dv.FFD /= N[i];
            frequencyClass.get(i).dv.GD /= N[i];
            frequencyClass.get(i).dv.Pr1 /= N[i];
            frequencyClass.get(i).dv.Pr2 /= N[i];
            frequencyClass.get(i).dv.PrS /= N[i];
        }
        
        // Display mean DVs for words of each frequency class:
        if (EZReader10.displayFrequencyClassMeans == true) display.frequencyClassMeans(diskWriter, frequencyClass);
    }

    //**************************************************************************
    
    void PrRegressions(PrintStream diskWriter, ArrayList<Sentence> text) {
        
        // Calculate probability of inter-word regression:
        double PrRegression = 0;
        for (int i = 0; i < EZReader10.NSentences; i++) PrRegression += text.get(i).regressionN;
        PrRegression /= (EZReader10.NSentences * EZReader10.NSubjects);
        
        // Display probability of inter-word regressions:
        System.out.printf(" PrRegression %5.4f\n", PrRegression);
        if (EZReader10.displayPrRegression == true) display.PrRegression(diskWriter, PrRegression);
    }
    
    //**************************************************************************

    void RMSD(PrintStream diskWriter, ArrayList<Sentence> text, ArrayList<Word> frequencyClass) {
        
        double sum = 0;
        
        // Calculation squared deviations:
        for (int i = 0; i < EZReader10.NFrequencyClasses; i++) {
            sum += Math.pow(((EZReader10.obsMeanSFD[i] - frequencyClass.get(i).dv.SFD) / EZReader10.obsSDSFD[i]), 2);
            sum += Math.pow(((EZReader10.obsMeanFFD[i] - frequencyClass.get(i).dv.FFD) / EZReader10.obsSDFFD[i]), 2);            
            sum += Math.pow(((EZReader10.obsMeanGD[i] - frequencyClass.get(i).dv.GD) / EZReader10.obsSDGD[i]), 2);
            sum += Math.pow(((EZReader10.obsMeanPr1[i] - frequencyClass.get(i).dv.Pr1) / EZReader10.obsSDPr1[i]), 2);
            sum += Math.pow(((EZReader10.obsMeanPr2[i] - frequencyClass.get(i).dv.Pr2) / EZReader10.obsSDPr2[i]), 2);
            sum += Math.pow(((EZReader10.obsMeanPrS[i] - frequencyClass.get(i).dv.PrS) / EZReader10.obsSDPrS[i]), 2);
        }
        
        // Calculate RMSD:
        double RMSD = Math.sqrt(sum / 48.0); // Note: 48 = # means
        
        // Display RMSD:
        System.out.printf(" RMSD %5.4f\n", RMSD);
        if (EZReader10.displayRMSD == true) display.RMSD(diskWriter, RMSD);
    }

    //**************************************************************************
    // Note: The word-based measures are calculated using the definitions provided by Inhoff & Radach (1998).
    
    void wordDVs(ArrayList<Sentence> text, int S, ArrayList<Fixation> trace) {
        
        // Convert trace into word-based DVs:
        for (int i = 0; i < text.get(S).numberWords; i++) { 
            
            // Initialize word class for DVs:
            Word w = new Word();

            // Count total number of fixations:
            for (int j = 0; j < trace.size(); j++) if (trace.get(j).word == i) w.dv.NFixations++;
            
            // Count number of first-pass fixations:
            for (int j = 0; j < trace.size(); j++) {
                if (trace.get(j).word == i) {
                    // Did the fixation occur after a regression back from a later word?
                    boolean postRegression = false;
                    for (int k = 0; k < j; k++) if (trace.get(k).word > i) postRegression = true; // i.e., post-regression fixation
                    // if the fixation on a word that was previously fixated but then regressed out of? 
                    boolean previouslyFixated = false;
                    for (int k = 0; k < j - 1; k++) if (trace.get(k).word == i) previouslyFixated = true;
                    // If either of the preceding 2 conditions are true, then the fixation did not occur during the first pass
                    // unless the previous fixation was also on the same word.
                    if (postRegression == false && (previouslyFixated == false || trace.get(j - 1).word == i))
                            w.dv.NFirstPassFixations++; 
                }
            }
            
            // Determine value and position of SFD:
            if (w.dv.NFirstPassFixations == 1 && w.dv.NFixations == 1) { // i.e., word only fixated once, during first pass
                for (int j = 0; j < trace.size(); j++) if (trace.get(j).word == i) {
                    // SFD:
                    w.dv.SFD = trace.get(j).duration; 
                    // Record SFD position and increment counter:
                    w.dv.distSFD[(int)(trace.get(j).position - text.get(S).get(trace.get(j).word).iv.position0)] = w.dv.SFD;
                    w.dv.NSFD[(int)(trace.get(j).position - text.get(S).get(trace.get(j).word).iv.position0)]++;
                }
            }
                
            // Determine value and position of FFD, and if a refixation occurred:
            if (w.dv.NFirstPassFixations > 0) {
                for (int j = 0; j < trace.size(); j++) if (trace.get(j).word == i && w.dv.FFD == 0) {
                    // FFD:
                    w.dv.FFD = trace.get(j).duration;
                    // Increment FFD position:
                    w.dv.distPr1[(int)(trace.get(j).position - text.get(S).get(trace.get(j).word).iv.position0)]++;
                    // Increment refixation count:
                    if (j < trace.size() - 1 && trace.get(j + 1).word == i) 
                            w.dv.distPr2[(int)(trace.get(j).position - text.get(S).get(trace.get(j).word).iv.position0)]++;
                }
            } 
            
            // Calculate GD:
            if (w.dv.NFirstPassFixations > 0) {
                for (int j = 0; j < trace.size(); j++) if (trace.get(j).word == i) {
                    if (w.dv.GD == 0 || trace.get(j - 1).word == i) w.dv.GD += trace.get(j).duration;
                }
            } 
                
            // Calculate TT:
            for (int j = 0; j < trace.size(); j++) if (trace.get(j).word == i) w.dv.TT += trace.get(j).duration;
            
            // Calculate GoPast:
            int inFixation = -1;
            for (int j = 0; j < trace.size(); j++) if (trace.get(j).word == i && inFixation == -1) inFixation = j;
            int outFixation = -1;
            for (int j = 0; j < trace.size(); j++) if (trace.get(j).word > i && outFixation == -1) outFixation = j;           
            if (inFixation > -1 && outFixation > -1) for (int j = inFixation; j < outFixation; j++) w.dv.GoPast += 
                    trace.get(j).duration;
        
            // Update counters for fixation-probability measures:
            switch (w.dv.NFirstPassFixations) {
                case 0:
                    w.dv.PrS++;
                    break;
                case 1:
                    w.dv.Pr1++;
                    break;
                default:
                    w.dv.Pr2++;
                    break;
            }
            if (w.dv.NFixations > 0) w.dv.PrF++;
                        
            // Add values to running totals used to calculate means across subjects:
            text.get(S).get(i).dv.SFD += w.dv.SFD;
            text.get(S).get(i).dv.FFD += w.dv.FFD;
            text.get(S).get(i).dv.GD += w.dv.GD;
            text.get(S).get(i).dv.TT += w.dv.TT;
            text.get(S).get(i).dv.GoPast += w.dv.GoPast;
            text.get(S).get(i).dv.Pr1 += w.dv.Pr1;
            text.get(S).get(i).dv.Pr2 += w.dv.Pr2;
            text.get(S).get(i).dv.PrS += w.dv.PrS;
            text.get(S).get(i).dv.PrF += w.dv.PrF;
            text.get(S).get(i).dv.NFirstPassFixations += w.dv.NFirstPassFixations;
            text.get(S).get(i).dv.NFixations += w.dv.NFixations;
            for (int j = 0; j < EZReader10.maxLength; j++) {
                text.get(S).get(i).dv.distPr1[j] += w.dv.distPr1[j];
                text.get(S).get(i).dv.distPr2[j] += w.dv.distPr2[j];
                text.get(S).get(i).dv.distSFD[j] += w.dv.distSFD[j];
                text.get(S).get(i).dv.NSFD[j] += w.dv.NSFD[j];
            }
        }
    }
    
    //**************************************************************************
     
    void wordMeans(ArrayList<Sentence> text) {
        
        // Calculate word-based means:
        for (int i = 0; i < EZReader10.NSentences; i++) {
            for (int j = 0; j < text.get(i).numberWords; j++) {
                
                // SFD:
                if (text.get(i).get(j).dv.Pr1 > 0) text.get(i).get(j).dv.SFD /= text.get(i).get(j).dv.Pr1;
         
                // TT:
                if (text.get(i).get(j).dv.PrF > 0) text.get(i).get(j).dv.TT /= text.get(i).get(j).dv.PrF;
                
                // GoPast:
                if (text.get(i).get(j).dv.PrF > 0) text.get(i).get(j).dv.GoPast /= text.get(i).get(j).dv.PrF;
                
                // FFD and GD:
                if ((text.get(i).get(j).dv.Pr1 + text.get(i).get(j).dv.Pr2) > 0) {
                    text.get(i).get(j).dv.FFD /= (text.get(i).get(j).dv.Pr1 + text.get(i).get(j).dv.Pr2);
                    text.get(i).get(j).dv.GD /= (text.get(i).get(j).dv.Pr1 + text.get(i).get(j).dv.Pr2);

                    // Calculate distributions:
                    for (int k = 0; k < EZReader10.maxLength; k++) {
                        if (text.get(i).get(j).dv.distPr1[k] > 0) text.get(i).get(j).dv.distPr2[k] /= text.get(i).get(j).dv.distPr1[k];
                        text.get(i).get(j).dv.distPr1[k] /= (text.get(i).get(j).dv.Pr1 + text.get(i).get(j).dv.Pr2);                        
                        if (text.get(i).get(j).dv.NSFD[k] > 0) text.get(i).get(j).dv.distSFD[k] /= text.get(i).get(j).dv.NSFD[k];
                    }
                }
             
                // Calculate fixation-probability measures:
                if (EZReader10.includeRegressionTrials == true) text.get(i).regressionN = 0;
                text.get(i).get(j).dv.Pr1 /= (EZReader10.NSubjects - text.get(i).regressionN);
                text.get(i).get(j).dv.Pr2 /= (EZReader10.NSubjects - text.get(i).regressionN);
                text.get(i).get(j).dv.PrS /= (EZReader10.NSubjects - text.get(i).regressionN);
                text.get(i).get(j).dv.PrF /= (EZReader10.NSubjects - text.get(i).regressionN);
            }
        }
    }
}

//*******************************************************************************************************************************************


