package net.sf.egonet.web.page;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.TreeSet;
import java.util.Iterator;
import org.apache.wicket.markup.html.form.Button;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.link.Link;
import com.google.common.collect.Lists;
import net.sf.egonet.model.Alter;
import net.sf.egonet.model.Answer;
import net.sf.egonet.model.Question;
import net.sf.egonet.model.Study;
import net.sf.egonet.persistence.Answers;
import net.sf.egonet.persistence.Interviewing;
import net.sf.egonet.persistence.Interviews;
import net.sf.egonet.persistence.Studies;
import net.sf.egonet.web.component.FocusOnLoadBehavior;
import net.sf.egonet.web.panel.AnswerFormFieldPanel;
import net.sf.egonet.web.panel.InterviewingPanel;
import static net.sf.egonet.web.page.InterviewingQuestionIntroPage.possiblyReplaceNextQuestionPageWithPreface;
public class InterviewingAlterPage extends InterviewingPage {
public static class Subject implements Serializable, Comparable<Subject> {
// eventually need a way for one of these to represent a question intro
public Long interviewId;
public Question question;
public ArrayList<Question> questionList; // used for multiple questions per page
public ArrayList<Alter> alters; // only one alter when not list style, never empty
public TreeSet<Question> sectionQuestions;
public Alter getAlter() {
return question.getAskingStyleList() ? null : alters.get(0);
}
@Override
public String toString() {
return question.getType()+" : "+question.getTitle()+
(getAlter() == null ? "" : " : "+getAlter().getName());
}
@Override
public int hashCode() {
return question.hashCode()+(question.getAskingStyleList() ? 0 : getAlter().hashCode());
}
@Override
public boolean equals(Object object) {
return object instanceof Subject && equals((Subject) object);
}
public boolean equals(Subject subject) {
return interviewId.equals(subject.interviewId) &&
question.equals(subject.question) &&
(question.getAskingStyleList() || getAlter().equals(subject.getAlter()));
}
@Override
public int compareTo(Subject subject) {
if(getAlter() != null && subject.getAlter() != null &&
(! getAlter().equals(subject.getAlter())) &&
sectionQuestions.contains(subject.question))
{
return getAlter().compareTo(subject.getAlter());
}
return question.compareTo(subject.question);
}
public Question lastQuestionInList() {
if ( questionList==null || questionList.isEmpty())
return(null);
return ( questionList.get(questionList.size()-1));
}
public Question firstQuestionInList() {
if ( questionList==null || questionList.isEmpty())
return(null);
return ( questionList.get(0));
}
}
private Subject subject;
private InterviewingPanel interviewingPanel;
private Label pageLevelPrompt;
private boolean gotoNextUnAnswered;
private boolean multipleQuestionsPerPage = false;
public InterviewingAlterPage(Subject subject) {
super(subject.interviewId);
this.subject = subject;
gotoNextUnAnswered = multipleQuestionsPerPage = false;
build();
setQuestionId("Question: " + subject.question.getTitle());
}
/**
* had to add the nextQuestion button explicitly instead of simply creating
* a form and having the submit button created implicitly because we need an
* object to add FocusOnLoadBehavior to.
* There are three presentation options:
* 1. One Question and One Alter
* 2. One Question and a list of Alters
* 3. A list of questions and One Alter
*/
private void build() {
Button nextQuestion;
Button nextUnanswered;
Form form = new Form("form"); // {
// public void onSubmit() {
// onSave(gotoNextUnAnswered);
// }
// };
nextQuestion = new Button("nextQuestion") {
public void onSubmit() {
onSave(gotoNextUnAnswered);
}
};
nextUnanswered = new Button("nextUnanswered") {
public void onSubmit() {
gotoNextUnAnswered = true;
}
};
form.add(nextQuestion);
form.add(nextUnanswered);
nextQuestion.add(new FocusOnLoadBehavior()); // whole reason for nextQuestion button
pageLevelPrompt = new Label("pageLevelPrompt","");
form.add(pageLevelPrompt);
setPageLevelPrompt ( subject.question.getListRangePrompt());
ArrayList<AnswerFormFieldPanel> answerFields = Lists.newArrayList();
multipleQuestionsPerPage = getQuestionsForOnePage ( subject.question, subject );
if ( subject.alters.size()==1 && multipleQuestionsPerPage ) {
ArrayList<Alter> alters = Lists.newArrayList(subject.alters.get(0));
for ( Question question : subject.questionList ) {
Answer answer =
Answers.getAnswerForInterviewQuestionAlters(Interviews.getInterview(subject.interviewId),
question, alters);
if(answer == null) {
answerFields.add(AnswerFormFieldPanel.getInstance("question", question, alters, subject.interviewId));
} else {
answerFields.add(AnswerFormFieldPanel.getInstance("question",
question, answer.getValue(), answer.getOtherSpecifyText(), answer.getSkipReason(), alters, subject.interviewId));
}
}
} else {
for(Alter alter : subject.alters) {
ArrayList<Alter> alters = Lists.newArrayList(alter);
Answer answer =
Answers.getAnswerForInterviewQuestionAlters(Interviews.getInterview(subject.interviewId),
subject.question, alters);
if(answer == null) {
answerFields.add(AnswerFormFieldPanel.getInstance("question", subject.question, alters, subject.interviewId));
} else {
answerFields.add(AnswerFormFieldPanel.getInstance("question",
subject.question, answer.getValue(), answer.getOtherSpecifyText(), answer.getSkipReason(), alters, subject.interviewId));
}
}
if(! answerFields.isEmpty()) {
answerFields.get(0).setAutoFocus();
}
}
if ( multipleQuestionsPerPage ) {
interviewingPanel = new InterviewingPanel("interviewingPanel",answerFields,subject.interviewId);
} else {
interviewingPanel =
new InterviewingPanel("interviewingPanel",subject.question,answerFields,subject.interviewId);
}
form.add(interviewingPanel);
add(form);
add(new Link("backwardLink") {
public void onClick() {
EgonetPage page =
askPrevious(subject.interviewId,subject, new InterviewingAlterPage(subject));
if(page != null) {
setResponsePage(page);
}
}
});
Link forwardLink = new Link("forwardLink") {
public void onClick() {
EgonetPage page =
askNext(subject.interviewId, subject, false, new InterviewingAlterPage(subject));
if(page != null) {
setResponsePage(page);
}
}
};
add(forwardLink);
if(! AnswerFormFieldPanel.okayToContinue(
interviewingPanel.getAnswerFields(),interviewingPanel.pageFlags()))
{
forwardLink.setVisible(false);
nextUnanswered.setVisible(false);
}
}
/**
* both the "Next Question" and "Next UnAnswered Question" buttons call this
* to save the current data and advance
* @param gotoNextUnAnswered if true proceed to next UNANSWERED question
* if false proceed to next questions
*/
public void onSave(boolean gotoNextUnAnswered) {
List<String> pageFlags = interviewingPanel.pageFlags();
List<AnswerFormFieldPanel> answerFields = interviewingPanel.getAnswerFields();
boolean okayToContinue =
AnswerFormFieldPanel.okayToContinue(answerFields, pageFlags);
boolean consistent =
AnswerFormFieldPanel.allConsistent(answerFields, pageFlags);
boolean multipleSelectionsOkay =
AnswerFormFieldPanel.allRangeChecksOkay(answerFields);
boolean countOfListItemOkay = (subject.question==null) ? true :
AnswerFormFieldPanel.checkCountOfListItem(subject.question, answerFields);
if ( countOfListItemOkay ) {
setPageLevelPrompt(" ");
} else {
okayToContinue = false;
setPageLevelPrompt(AnswerFormFieldPanel.getStatusCountOfListItem(subject.question, answerFields));
}
for(AnswerFormFieldPanel answerField : answerFields) {
if ( !multipleSelectionsOkay ) {
answerField.setNotification(answerField.getRangeCheckNotification());
} else if(okayToContinue) {
Answers.setAnswerForInterviewQuestionAlters(
subject.interviewId, /*subject.question,*/answerField.getQuestion(), answerField.getAlters(),
answerField.getAnswer(), answerField.getOtherText(),
answerField.getSkipReason(pageFlags));
} else if(consistent) {
answerField.setNotification(
answerField.answeredOrRefused(pageFlags) ?
"" : "Unanswered");
} else {
answerField.setNotification(
answerField.consistent(pageFlags) ?
"" : answerField.inconsistencyReason(pageFlags));
}
}
if(okayToContinue) {
EgonetPage page =
askNext(subject.interviewId, subject, gotoNextUnAnswered, new InterviewingAlterPage(subject));
if(page != null) {
setResponsePage(page);
}
}
}
public String toString() {
return subject.toString();
}
/**
* advances to the next alter question or alterPair question if we're
* on the last meaningful question. This version calls nextAlterPageForInterviewNEW
* which should largely eliminate the delay between questions
* @param interviewId this interview
* @param currentSubject
* @param unansweredOnly - if true, we only want unanswered questions (duh)
* @param comeFrom
* @return
*/
// public static EgonetPage askNextNEW(Long interviewId, Subject currentSubject,
// Boolean unansweredOnly, EgonetPage comeFrom)
// {
// Subject nextSubject =
// Interviewing.nextAlterPageForInterviewNEW(
// interviewId, currentSubject, true, unansweredOnly);
// if(nextSubject != null) {
// EgonetPage nextPage = new InterviewingAlterPage(nextSubject);
// return possiblyReplaceNextQuestionPageWithPreface(
// interviewId,nextPage,
// currentSubject == null ? null : currentSubject.question,
// nextSubject.question,
// comeFrom,nextPage);
// }
// return InterviewingAlterPairPage.askNext(interviewId,null,unansweredOnly,comeFrom);
// }
public static EgonetPage askNext(Long interviewId, Subject currentSubject,
Boolean unansweredOnly, EgonetPage comeFrom)
{
// to move forward we need to start searching on the last question
if ( currentSubject!=null && currentSubject.questionList != null )
currentSubject.question = currentSubject.lastQuestionInList();
Subject nextSubject =
Interviewing.nextAlterPageForInterview(
interviewId, currentSubject, true, unansweredOnly);
if(nextSubject != null) {
EgonetPage nextPage = new InterviewingAlterPage(nextSubject);
return possiblyReplaceNextQuestionPageWithPreface(
interviewId,nextPage,
currentSubject == null ? null : currentSubject.question,
nextSubject.question,
comeFrom,nextPage);
}
return InterviewingAlterPairPage.askNext(interviewId,null,unansweredOnly,comeFrom);
}
/**
* new version of askPrevious which should greatly reduce the delays between questions
* @param interviewId
* @param currentSubject
* @param comeFrom
* @return
*/
// public static EgonetPage askPreviousNEW(Long interviewId, Subject currentSubject, EgonetPage comeFrom)
// {
// Subject previousSubject =
// Interviewing.nextAlterPageForInterviewNEW(
// interviewId, currentSubject, false, false);
// EgonetPage previousPage;
// if(previousSubject != null) {
// previousPage = new InterviewingAlterPage(previousSubject);
// } else {
// Study study = Studies.getStudyForInterview(interviewId);
// Integer max = study.getMaxAlters();
// if(max != null && max > 0) {
// previousPage = new InterviewingAlterPromptPage(interviewId);
// } else {
// previousPage = InterviewingEgoPage.askPrevious(interviewId, null, comeFrom);
// }
// }
// return possiblyReplaceNextQuestionPageWithPreface(
// interviewId,previousPage,
// previousSubject == null ? null : previousSubject.question,
// currentSubject == null ? null : currentSubject.question,
// previousPage,comeFrom);
// }
public static EgonetPage askPrevious(Long interviewId, Subject currentSubject, EgonetPage comeFrom)
{
// to move backward we need to start searching on the first question
if ( currentSubject!=null && currentSubject.questionList != null )
currentSubject.question = currentSubject.firstQuestionInList();
Subject previousSubject =
Interviewing.nextAlterPageForInterview(
interviewId, currentSubject, false, false);
EgonetPage previousPage;
if(previousSubject != null) {
previousPage = new InterviewingAlterPage(previousSubject);
} else {
Study study = Studies.getStudyForInterview(interviewId);
Integer max = study.getMaxAlters();
if(max != null && max > 0) {
previousPage = new InterviewingAlterPromptPage(interviewId);
} else {
previousPage = InterviewingEgoPage.askPrevious(interviewId, null, comeFrom);
}
}
return possiblyReplaceNextQuestionPageWithPreface(
interviewId,previousPage,
previousSubject == null ? null : previousSubject.question,
currentSubject == null ? null : currentSubject.question,
previousPage,comeFrom);
}
/**
* the subject has a treeSet sectionQuestions, which is the group of questions this
* question (firstQuestion) belongs to. The end of the group is the last question within
* this treeset. Some questions might have a 'keepOnSamePage' flag set, and we will want to
* group all of those together into an array of answerPanels.
* @param firstQuestion - just what is says, first question on this 'page'
* @param subject - an object with a question and an alter and the listOfQuestions
* @return true if we will need to display more than one question
*/
private boolean getQuestionsForOnePage ( Question firstQuestion, Subject subject ) {
Iterator<Question> iter;
Question question;
boolean moreThanOne = false;
boolean hit = false;
boolean done = false;
subject.questionList = new ArrayList<Question>();
// If we don't allow multipe questions per page,
// just return a list with one Question and a false value
if ( !Question.ALLOW_MULTIPLE_QUESTIONS_PER_PAGE ) {
subject.questionList.add(firstQuestion);
return(false);
}
iter = subject.sectionQuestions.iterator();
while ( iter.hasNext() && !done ) {
question = iter.next();
if ( question.equals(firstQuestion)) {
subject.questionList.add(question);
hit = true;
} else if (hit) {
if ( question.getKeepOnSamePage()) {
subject.questionList.add(question);
moreThanOne = true;
} else {
done = true;
}
}
}
// in a similar manner, if we are going backwards and are on
// a question that needs to stay on the same page as the previous one,
// we will need to go back
if ( firstQuestion.getKeepOnSamePage()) {
iter = subject.sectionQuestions.descendingIterator();
hit = done = false;
while ( iter.hasNext() && !done ) {
question = iter.next();
if ( question.equals(firstQuestion)) {
hit = true;
} else if (hit) {
if ( question.getKeepOnSamePage()) {
subject.questionList.add(0,question);
moreThanOne = true;
} else {
subject.questionList.add(0,question);
moreThanOne = true;
done = true;
}
}
}
}
// System.out.println ( "Exitting InterviewingAlterPage.getQuestionsForOnePage");
// for ( Question q : subject.questionList) {
// System.out.println ( "Contains " + q.getTitle());
// }
return(moreThanOne);
}
/**
* this is used in list-of-alters pages, where one prompt
* will apply to the screen as a whole, not individual answers.
* Examples are "Select Yes once" or "Yes selected too many times"
* @param text - string to display
*/
public void setPageLevelPrompt(String text) {
pageLevelPrompt.setModelObject(text);
}
}