package org.javarosa.engine;

import org.javarosa.core.model.FormDef;
import org.javarosa.core.model.condition.EvaluationContext;
import org.javarosa.core.model.condition.IFunctionHandler;
import org.javarosa.core.model.instance.InstanceInitializationFactory;
import org.javarosa.core.model.utils.DateUtils;
import org.javarosa.engine.models.Action;
import org.javarosa.engine.models.Mockup;
import org.javarosa.engine.models.Session;
import org.javarosa.engine.models.Step;
import org.javarosa.form.api.FormEntryController;
import org.javarosa.form.api.FormEntryModel;

import java.util.Date;
import java.util.Hashtable;
import java.util.Vector;

/**
 *
 * Run an XForm programatically for fun and profit.
 *
 * @author ctsims
 */
public class XFormEnvironment {

    private final FormDef form;

    private String preferredLocale;

    private Step currentStep;
    private int stepCount = 0;

    private Session session;
    private Mockup mockup;
    private Date hardCodedDate;
    boolean recording = true;

    public XFormEnvironment(FormDef form) {
        this.form = form;
    }

    public XFormEnvironment(FormDef form, Session session) {
        this.form = form;
        this.session = session;
        recording = false;
    }

    public XFormEnvironment(FormDef form, Mockup mockup) {
        this(form);
        this.mockup = mockup;
    }

    public FormEntryController setup() {
        return setup(createIIF());
    }
    
    public FormEntryController setup(InstanceInitializationFactory factory) {
        form.setEvaluationContext(getEC());

        form.initialize(true, factory, preferredLocale);

        if(recording) {
            session = new Session();
            currentStep = new Step();
        } else {
            currentStep = session.getSteps().elementAt(0);
        }

        FormEntryModel fem = new FormEntryModel(form);
        FormEntryController fec = FormEntryController.buildRecordingController(fem);

        return fec;
    }

    public Step popStep() {
        if(!recording) {
            Step toRet = currentStep;
            stepCount++;
            if(session.getSteps().size() > stepCount) {
                currentStep = session.getSteps().elementAt(stepCount);
            } else {
                currentStep = null;
            }
            return toRet;
        } else {
            throw new IllegalStateException("Can't get step records in playback mode");
        }
    }

    private InstanceInitializationFactory createIIF() {
        return new MockupProviderFactory(mockup == null ? new Hashtable() : mockup.getInstances());
    }

    private EvaluationContext getEC() {
        EvaluationContext ec = new EvaluationContext(null);
        Date customDate = hardCodedDate;
        if (mockup != null) {
            customDate = mockup.getDate();
        }
        ec.addFunctionHandler(new FunctionExtensions.TodayFunc("today", customDate));
        ec.addFunctionHandler(new FunctionExtensions.TodayFunc("now", customDate));
        return ec;
    }

    public void setLocale(String locale) {
        for(String existingLocale : this.form.getLocalizer().getAvailableLocales()) {
            if(existingLocale.equals(locale)) {
                preferredLocale = locale;
                return;
            }
        }
    }

    public void setToday(String dateString) {
        hardCodedDate = DateUtils.parseDate(dateString);
        System.out.println(hardCodedDate);
    }


    public void commitStep() {
        if(recording) {
            session.addStep(currentStep);
            currentStep = new Step();
        }
    }

    public void recordAction(Action action) {
        if(recording) {
            currentStep.setAction(action);
        }
    }

    public boolean isModePlayback() {
        return !recording;
    }

    public Session getSessionRecording() {
        if(recording) {
            return session;
        } else {
            throw new IllegalStateException("Can't get a recording from a playback session!");
        }
    }
}

@@ -127,67 +127,8 @@
 xt;%0A
-import org.javarosa.core.model.condition.IFunctionHandler;%0A
 impo
@@ -559,42 +559,14 @@
 ble;
-%0Aimport java.util.Vector;
 %0A%0A/**%0A
- *%0A
  * R
@@ -855,24 +855,25 @@
 kup mockup;%0A
+%0A
     private 
@@ -876,56 +876,129 @@
 ate 
-Date hardCodedDat
+boolean recording = tru
 e;%0A
+%0A
     
-boolean recording = true
+// stores date override of 'today()' / 'now()'%0A    private Date hardCodedDate = new Date()
 ;%0A%0A 
@@ -1541,21 +1541,42 @@
 Context(
-getEC
+buildBaseEvaluationContext
 ());%0A%0A  
@@ -2618,39 +2618,184 @@
     
-private EvaluationContext getEC
+/**%0A     * Builds evaluation context that overrides 'today()' and 'now()' with%0A     * custom date, if provided.%0A     */%0A    private EvaluationContext buildBaseEvaluationContext
 () %7B
@@ -2855,24 +2855,25 @@
 (null);%0A
+%0A
         
 Date cus
@@ -2868,40 +2868,101 @@
     
-Date customDate = hardCodedDate;
+// if present, load date from formplayer mockup (which allows for restoring formplayer state)
 %0A   
@@ -3000,22 +3000,25 @@
         
-custom
+hardCoded
 Date = m
@@ -3036,32 +3036,33 @@
 te();%0A        %7D%0A
+%0A
         ec.addFu
@@ -3117,22 +3117,25 @@
 today%22, 
-custom
+hardCoded
 Date));%0A
@@ -3208,22 +3208,25 @@
 w%22, 
-custom
+hardCoded
 Date));%0A
     
@@ -3213,32 +3213,33 @@
 ardCodedDate));%0A
+%0A
         return e
@@ -3675,17 +3675,16 @@
 %0A    %7D%0A%0A
-%0A
     publ

Add comments and clear up some code