package simulate;

import java.awt.Component;
import java.util.Random;

public abstract class MCMove extends Component {
    
    int frequency, nominalFrequency;
    double acceptanceRatio, acceptanceTarget, stepSize, stepSizeMax, stepSizeMin;
    int nTrials, nAccept, nTrialsSum, nAcceptSum, adjustInterval;
    boolean perParticleFrequency;
    Integrator parentIntegrator;
    protected MCMove nextMove;
    protected boolean tunable;
    
    public MCMove() {
        nTrials = 0;
        nAccept = 0;
        setAcceptanceTarget(0.5);
        setFrequency(1);
        setPerParticleFrequency(false);
        setAdjustInterval(100);
    }
    
    public void doTrial(Phase phase) {
        nTrials++;
        thisTrial(phase);
        if(nTrials > adjustInterval*frequency) {adjustStepSize();}
    }
    
    public abstract void thisTrial(Phase phase);
    
    public void adjustStepSize() {
        if(nTrials == 0) {return;}
        nTrialsSum += nTrials;
        nAcceptSum += nAccept;
        if(nTrialsSum != 0) acceptanceRatio = (double)nAcceptSum/(double)nTrialsSum;
        if(!tunable) return;
        if(nAccept > (int)(acceptanceTarget*nTrials)) {stepSize *= 1.05;}
        else{stepSize *= 0.95;}
        stepSize = Math.min(stepSize,stepSizeMax);
        stepSize = Math.max(stepSize,stepSizeMin);
        nTrials = 0;
        nAccept = 0;
    }
    
    
    public void setFrequency(int f) {
        nominalFrequency = f;
        frequency = f;
    }
    public final int getFrequency() {return frequency;}
    public void resetFrequency(Phase phase) {
        frequency = perParticleFrequency ? nominalFrequency*phase.moleculeCount : nominalFrequency;
    }
    public final void setPerParticleFrequency(boolean b) {perParticleFrequency = b;}
    public final boolean isPerParticleFrequency() {return perParticleFrequency;}
    
    /**
     * Fraction of time trials of this type were accepted since acceptanceTarget was set
     */
    public double acceptanceRatio() {return acceptanceRatio;}
    public final void setAcceptanceTarget(double a) {
        nTrialsSum = 0;
        nAcceptSum = 0;
        acceptanceTarget = a;
    }
    public final double getAcceptanceTarget() {return acceptanceTarget;}
    
    public final void setStepSize(double step) {stepSize = step;}
    public final double getStepSize() {return stepSize;}
    
    public final void setStepSizeMax(double step) {stepSizeMax = step;}
    public final double getStepSizeMax() {return stepSizeMax;}
    public final void setStepSizeMin(double step) {stepSizeMin = step;}
    public final double getStepSizeMin() {return stepSizeMin;}
    
    public final void setNextMove(MCMove move) {nextMove = move;}
    public final MCMove nextMove() {return nextMove;}
    
    public final void setAdjustInterval(int i) {adjustInterval = i;}
    public final int getAdjustInterval() {return adjustInterval;}
    
    /**
     * Sets a flag to indicate whether tuning of the move is to be performed
     * Tuning aims to set the acceptance rate to some target value
     * Some moves (e.g., simple insertion trial) are inherently untunable
     */
    public final void setTunable(boolean b) {tunable = b;}
    public final boolean getTunable() {return tunable;}
    
}