//*******************************************************************************************************************************************
// The methods of this class instantiate all of the basic processes of E-Z Reader.
//*******************************************************************************************************************************************

package ezreader10;

import java.util.*;

class Process {
    
    double duration; // duration (ms)
    double durationCopy; // copy of the process duration is retained (i.e., not decremented)
    double length; // saccade length (number of character spaces)
    String name; // process label (e.g., "L1", "M2", etc.)
    int word; // within-sentence word number associated with the process
    
    // Initialize classes:
    Display display = new Display();
    Gamma g = new Gamma();
    Random r = new Random();
    Saccade saccade = new Saccade();
                
    //**************************************************************************
    // PRE-ATTENTIVE VISUAL PROCESSING
    //**************************************************************************
    
    void initializeV(int N) {
        
        name = "V";
        word = N;
        
        // Calculate duration (ms):
        duration = EZReader10.V;
    }
    
    //**************************************************************************
    // FAMILIARITY CHECK
    //**************************************************************************
    
    void initializeL1(ArrayList<Sentence> text, int S, int N, boolean ongoingI) {
        
        name = "L1";
        word = N;

        // Determine if word can be predicted (i.e., if previous word been integrated):
        double clozeValue;
        if (ongoingI == true) clozeValue = 0;                                         
        else clozeValue = 1.0;
        
        // Calculate duration (ms):
        double PrGuess = r.nextDouble();
        if (clozeValue > 0 && PrGuess < (text.get(S).get(N).iv.cloze)) duration = 0;
        else {
            double mu = EZReader10.Alpha1 - (EZReader10.Alpha2 * text.get(S).get(N).iv.log10Frequency) - (EZReader10.Alpha3 * 
                    (text.get(S).get(N).iv.cloze * clozeValue));
            if (mu < EZReader10.minValue) mu = EZReader10.minValue;
            duration = g.nextDouble(mu, EZReader10.SigmaGamma);
        }
    }
    
    //**************************************************************************
    // LEXICAL ACCESS
    //**************************************************************************

    void initializeL2(ArrayList<Sentence> text, int S, int N, boolean ongoingI) {
        
        name = "L2";
        word = N;
        
        // Determine if word can be predicted (i.e., if previous word been integrated):        
        double clozeValue;
        if (ongoingI == true) clozeValue = 0;
        else clozeValue = 1.0;
        
        // Calculate duration (ms):
        double mu = EZReader10.Alpha1 - (EZReader10.Alpha2 * text.get(S).get(N).iv.log10Frequency) - (EZReader10.Alpha3 * 
                (text.get(S).get(N).iv.cloze* clozeValue));
        if (mu < EZReader10.minValue) mu = EZReader10.minValue;
        mu *= EZReader10.Delta;
        duration = g.nextDouble(mu, EZReader10.SigmaGamma);
    }
    
    //**************************************************************************
    // POST-LEXICAL INTEGRATION
    //**************************************************************************
    
    void initializeI(ArrayList<Sentence> text, int S, int N) {
        
        name = "I";
        word = N;
        
        // Calculate duration of I for target and non-target words (ms):
        if (N == text.get(S).target) duration = g.nextDouble(EZReader10.ITarget, EZReader10.SigmaGamma);
        else duration = g.nextDouble(EZReader10.I, EZReader10.SigmaGamma);
    }
    
    //**************************************************************************
    // ATTENTION SHIFT
    //**************************************************************************
    
    void initializeA(int N) {
        
        name = "A";
        word = N;
        
        // Calculate duration (ms):
        duration = g.nextDouble(EZReader10.A, EZReader10.SigmaGamma);
    }
    
    //**************************************************************************
    // LABILE SACCADIC PROGRAMMING
    //**************************************************************************
    
    void initializeM1(ArrayList<Process> active, double currentPos, double targetPos, int N) {
        
        name = "M1";
        word = N;

        // Calculate intended saccade length:
        length = targetPos - currentPos;
        
        // Calculate duration (ms):
        duration = g.nextDouble(EZReader10.M1, EZReader10.SigmaGamma);
        durationCopy = duration;
        
        // Cancel any pending M1 and adjust the new M1 duration accordingly:
        for (int i = 0; i < active.size(); i++) if (active.get(i).name.equals("M1")) {
            
            double programmingTimeCompleted = active.get(i).durationCopy - active.get(i).duration;
            double engageTime = duration * EZReader10.Xi;
            if (programmingTimeCompleted > engageTime) duration -= engageTime;
            else duration -= programmingTimeCompleted;
            
            active.remove(i);
        }
    }
    
    //**************************************************************************
    // NON-LABILE SACCADIC PROGRAMMING
    //**************************************************************************
    
    void initializeM2(double intendedSaccade, double launchSiteFixDur, int N) {
        
        name = "M2";
        word = N;
        
        // Calculate saccadic error (character spaces):
        double randomError = saccade.randomError(intendedSaccade);
        double systematicError = saccade.systematicError(intendedSaccade, launchSiteFixDur);
        
        // Calculate actual saccade length (character spaces):
        length = intendedSaccade + randomError + systematicError;
        
        // Calculate duration (ms):
        duration = g.nextDouble(EZReader10.M2, EZReader10.SigmaGamma);
    }
    
    //**************************************************************************
    // SACCADE EXECUTION
    //**************************************************************************
    
    void initializeS(double actualSaccade, int N) {
        
        name = "S";
        word = N;
        
        // Calculate length (character spaces):
        length = actualSaccade;
        
        // Calculate duration (ms):
        duration = EZReader10.S;
    }
    
    //**************************************************************************
    // ADJUST LEXICAL-PROCESSING RATE
    //**************************************************************************
    
    double calcRate(ArrayList<Sentence> text, int S, int N, double currentPos) {
        
        double acuity = 1.0;
        double fovea = 0; // character number being fixated
        double load = 0;
        double rate = 0;
        
        double firstLetter = text.get(S).get(N).iv.position1;
        double lastLetter = text.get(S).get(N).iv.positionN;
        double middle;
        double wordLength = text.get(S).get(N).iv.length;
        
        // Identify cummulative character number being fixated:
        for (double i = 0; i <= text.get(S).get(text.get(S).numberWords - 1).iv.positionN; i++) 
            if (currentPos >= i && currentPos < i + 1.0) fovea = i;
    
        if (wordLength == 1) { // i.e., word = "a"
            middle = firstLetter + 0.5;
            acuity = Math.abs(middle - currentPos) * EZReader10.Epsilon1;
            rate += (1.0 + acuity);
        }
        else {
            if (firstLetter > fovea) { // i.e., whole word is in RVF
                for (double i = firstLetter; i < lastLetter; i++) {
                    middle = i + 0.5;
                    acuity = Math.abs(middle - currentPos) * EZReader10.Epsilon1;
                    if (i == firstLetter) load = EZReader10.Epsilon3;
                    else if (i == lastLetter - 1) load = EZReader10.Epsilon2;
                    else load = (EZReader10.Epsilon2 + EZReader10.Epsilon3);
                    rate += (1.0 + (acuity * load));
                }
            }
            else if (lastLetter < fovea) { // i.e., whole word is in LVF
                for (double i = firstLetter; i < lastLetter; i++) {
                    middle = i + 0.5;
                    acuity = Math.abs(middle - currentPos) * EZReader10.Epsilon1;
                    if (i == firstLetter) load = EZReader10.Epsilon2;
                    else if (i == lastLetter - 1) load = EZReader10.Epsilon3;
                    else load = (EZReader10.Epsilon2 + EZReader10.Epsilon3);
                    rate += (1.0 + (acuity * load));
                }
            }  
            else { // i.e., fovea bisects word
                for (double i = firstLetter; i < lastLetter; i++) {
                    middle = i + 0.5;
                    acuity = Math.abs(middle - currentPos) * EZReader10.Epsilon1;
                    if (i == firstLetter || i == lastLetter - 1) { // i.e., first and last letter
                        if (i == fovea) load = EZReader10.Epsilon3; // i.e., first/last letter fixated
                        else load = EZReader10.Epsilon2; // i.e., internal letter fixated
                    }
                    else { // i.e., internal letter
                        if (i == fovea || i == fovea - 1.0 || i == fovea + 1.0) load = EZReader10.Epsilon3 + EZReader10.Epsilon3; // i.e., only lateral masking
                        else load = EZReader10.Epsilon2 + EZReader10.Epsilon3; // i.e., medial and lateral masking
                    }
                    rate += (1.0 + (acuity * load));
                }
            }
        }

        // Return updated lexical-processing rate:
        rate /= wordLength;
        return rate;
    }
}

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