package org.commcare.util;

import org.commcare.cases.model.Case;
import org.commcare.core.interfaces.UserSandbox;
import org.commcare.modern.util.Pair;
import org.commcare.session.CommCareSession;
import org.commcare.suite.model.ComputedDatum;
import org.commcare.suite.model.EntityDatum;
import org.commcare.suite.model.SessionDatum;
import org.commcare.suite.model.StackFrameStep;
import org.commcare.suite.model.Text;
import org.javarosa.core.model.condition.EvaluationContext;
import org.javarosa.core.model.instance.TreeReference;

/**
 * @author Phillip Mates (pmates@dimagi.com)
 */
public class FormDataUtil {
    public static String getTitleFromSession(UserSandbox userSandbox,
                                             CommCareSession session,
                                             EvaluationContext evalContext) {
        CommCareSession sessionCopy = new CommCareSession(session);

        Pair<EntityDatum, String> datumEntityAndValue =
                getDatumAndCaseId(sessionCopy, evalContext);
        EntityDatum entityDatum = datumEntityAndValue.first;
        String value = datumEntityAndValue.second;

        if (entityDatum == null) {
            if (value == null) {
                return null;
            } else {
                return getCaseName(userSandbox, value);
            }
        } else {
            return loadTitleFromEntity(entityDatum, value, evalContext, sessionCopy, userSandbox);
        }
    }

    private static Pair<EntityDatum, String> getDatumAndCaseId(CommCareSession session,
                                                               EvaluationContext evaluationContext) {
        String datumValue = null;
        while (session.getFrame().getSteps().size() > 0) {
            SessionDatum datum = session.getNeededDatum();
            if (isCaseIdComputedDatum(datum, datumValue, session.getPoppedStep())) {
                // Loads case id for forms that create cases, since case id was
                // a computed datum injected into the form. Assumes that it was
                // the 1st computed datum... another good heuristic would be
                // session.getPoppedStep().getId().startsWith("case_id")
                datumValue = session.getPoppedStep().getValue();
            } else if (datum instanceof EntityDatum) {
                String tmpDatumValue = session.getPoppedStep().getValue();
                if (tmpDatumValue != null) {
                    datumValue = tmpDatumValue;
                }
                if (((EntityDatum)datum).getLongDetail() == null) {
                    return new Pair<>(null, datumValue);
                } else {
                    return new Pair<>((EntityDatum)datum, datumValue);
                }
            }
            session.popStep(evaluationContext);
        }
        return new Pair<>(null, datumValue);
    }

    private static boolean isCaseIdComputedDatum(SessionDatum datum,
                                                 String currentDatumValue,
                                                 StackFrameStep poppedStep) {
        return datum instanceof ComputedDatum &&
                (currentDatumValue == null || poppedStep.getId().equals(datum.getDataId()));
    }

    private static String loadTitleFromEntity(EntityDatum entityDatum, String value,
                                              EvaluationContext evalContext,
                                              CommCareSession sessionCopy,
                                              UserSandbox userSandbox) {
        TreeReference elem = entityDatum.getEntityFromID(evalContext, value);
        if (elem == null) {
            return null;
        }

        Text detailText = sessionCopy.getDetail(entityDatum.getLongDetail()).getTitle().getText();
        boolean isPrettyPrint = true;

        //CTS: this is... not awesome.
        //But we're going to use this to test whether we _need_ an evaluation context
        //for this. (If not, the title doesn't have prettyprint for us)
        try {
            String outcome = detailText.evaluate();
            if (outcome != null) {
                isPrettyPrint = false;
            }
        } catch (Exception e) {
            //Cool. Got us a fancy string.
        }

        if (isPrettyPrint) {
            // Get the detail title for that element
            EvaluationContext elementContext = new EvaluationContext(evalContext, elem);
            return detailText.evaluate(elementContext);
        } else {
            return getCaseName(userSandbox, value);
        }
    }


    private static String getCaseName(UserSandbox userSandbox, String caseId) {
        try {
            Case ourCase = userSandbox.getCaseStorage().getRecordForValue(Case.INDEX_CASE_ID, caseId);
            if (ourCase != null) {
                return ourCase.getName();
            } else {
                return null;
            }
        } catch (Exception e) {
            return null;
        }
    }
}

@@ -112,46 +112,8 @@
 ox;%0A
-import org.commcare.modern.util.Pair;%0A
 impo
@@ -497,16 +497,131 @@
 e;%0A%0A/**%0A
+ * Use the session state descriptor attached to saved forms to load case%0A * information, such as the case name.%0A *%0A
  * @auth
@@ -979,806 +979,69 @@
 n);%0A
-%0A
        
- Pair%3CEntityDatum,
  String
-%3E
  datum
-EntityAndValue =%0A                getDatumAndCaseId(sessionCopy, evalContext);%0A        EntityDatum entityDatum = datumEntityAndValue.first;%0A        String value = datumEntityAndValue.second;%0A%0A        if (entityDatum == null) %7B%0A            if (value == null) %7B%0A                return null;%0A            %7D else %7B%0A                return getCaseName(userSandbox, value);%0A            %7D%0A        %7D else %7B%0A            return loadTitleFromEntity(entityDatum, value, evalContext, sessionCopy, userSandbox);%0A        %7D%0A    %7D%0A%0A    private static Pair%3CEntityDatum, String%3E getDatumAndCaseId(CommCareSession session,%0A                                                               EvaluationContext evaluationContext) %7B%0A        String datumValue = null;%0A        while (session
+Value = null;%0A%0A        while (sessionCopy
 .get
@@ -1109,24 +1109,28 @@
 um = session
+Copy
 .getNeededDa
@@ -1192,32 +1192,36 @@
 umValue, session
+Copy
 .getPoppedStep()
@@ -1563,32 +1563,36 @@
 mValue = session
+Copy
 .getPoppedStep()
@@ -1697,32 +1697,36 @@
 mValue = session
+Copy
 .getPoppedStep()
@@ -1941,43 +1941,106 @@
     
-return new Pair%3C%3E(null, datumValue)
+// In the absence of a case detail, use the plain case name as the title%0A                    break
 ;%0A  
@@ -2089,26 +2089,35 @@
  return 
-new Pair%3C%3E
+loadTitleFromEntity
 ((Entity
@@ -2135,24 +2135,63 @@
 , datumValue
+, evalContext, sessionCopy, userSandbox
 );%0A         
@@ -2232,16 +2232,20 @@
  session
+Copy
 .popStep
@@ -2249,22 +2249,16 @@
 tep(eval
-uation
 Context)
@@ -2273,38 +2273,127 @@
   %7D%0A
-        return new Pair%3C%3E(null
+%0A        if (datumValue == null) %7B%0A            return null;%0A        %7D else %7B%0A            return getCaseName(userSandbox
 , da
@@ -2399,24 +2399,34 @@
 atumValue);%0A
+        %7D%0A
     %7D%0A%0A    p

Condense title loading code