package dk.itu.smdp.group2.questionnaire.model;
import java.util.ArrayList;
import dk.itu.smdp.group2.R;
import android.util.Log;
import android.view.View;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.LinearLayout;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.TextView;
/**
* A Question where the user must select a number of answers based
* on the given options to choose from.
*
* @author Emil
*
*/
public class ChoiceQuestion extends Question{
private int min,max;
private int numChecked = 0;
private ArrayList<Tuple<String,String>> options;
private ArrayList<Tuple<String,CompoundButton>> idViews;
// results
private View root;
private RadioGroup radiogroup = null;
private ArrayList<CheckBox> checkboxes = null;
/**
* Initializes the ChoiceQuestion with the given parameters. Further addition
* of options is necessary for a complete ChoiceQuestion.
*
* @param question The question to answer.
* @param desc The more detailed description to the question if necessary.
* Null or empty string will be seen as no description.
* @param mandatory True if the Question should be marked as mandatory for the
* Questionnaire to be complete.
* @param minSelections The minimum number of selections the respondent must select.
* @param maxSelections The maximum number of answers (inclusive) that the
* respondent can select.
*/
public ChoiceQuestion(String question, String description, boolean mandatory, int minSelections, int maxSelections){
super(question,description,mandatory);
min = minSelections;
max = maxSelections;
options = new ArrayList<Tuple<String,String>>();
idViews = new ArrayList<Tuple<String,CompoundButton>>();
}
/**
* Adds a possible option to the ChoiceQuestion for the
* respondent to select.
*
* @param id The unique id of this choice, for possible
* reference in conditions of later Questions in the
* Questionnaire.
* @param text The visible text defining the option.
*/
public void addOption(String id, String text){
options.add(new Tuple<String,String>(id, text));
}
/**
* Checks if this ChoiceQuestion has an option with
* the given ID.
*
* @param id The ID to search for.
* @return True, if this ChoiceQuestion has an option
* with the given ID.
*/
public boolean containsID(String id){
return getValueForID(id) != null;
}
/**
* Checks if the respondent has chosen the option with
* the given ID.
*
* @param id The ID to search for.
* @return True, if this ChoiceQuestion has an option
* with the given ID and it has been selected by the
* respondent.
*/
public boolean isIDChosen(String id) {
for(Tuple<String,CompoundButton> kv : idViews){
if(kv.getFirst().equals(id)){
return kv.getSecond().isChecked();
}
}
return false;
}
@Override
public View generateView() {
// inflate and fetch objects
root = this.getParent().getActivity().getLayoutInflater().inflate(R.layout.question_choice, null);
TextView title = (TextView) root.findViewById(R.id.tvChoiceTitle);
TextView desc = (TextView) root.findViewById(R.id.tvChoiceDesc);
TextView select = (TextView) root.findViewById(R.id.tvChoiceSelec);
LinearLayout options = (LinearLayout) root.findViewById(R.id.svsChoiceLL);
// set values
title.setText(this.getQuestionText());
desc.setText(this.getDescription());
if(getDescription() == null || getDescription().length() == 0) desc.setVisibility(View.GONE);
// create options (dynamically because it is only checkbox/radio)
if(min == 1 && max == 1){ // radio
select.setVisibility(View.GONE);
radiogroup = new RadioGroup(getParent().getActivity());
for(Tuple<String,String> kv : this.options){
RadioButton rb = new RadioButton(getParent().getActivity());
rb.setText(kv.getSecond());
idViews.add(new Tuple<String,CompoundButton>(kv.getFirst(),rb));
radiogroup.addView(rb);
// Put listener
rb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
// make questions visible/invisible if they have this as condition
getParent().checkConditions();
}
});
}
options.addView(radiogroup);
}else{ // checkbox
select.setText("Select "+((min == max) ? min : "between"+min+" and "+max)+" options");
checkboxes = new ArrayList<CheckBox>();
for(Tuple<String,String> kv : this.options){
final CheckBox cb = new CheckBox(getParent().getActivity());
cb.setText(kv.getSecond());
idViews.add(new Tuple<String,CompoundButton>(kv.getFirst(),cb));
// Put listener
cb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
numChecked += isChecked ? 1 : -1;
// uncheck again if too many
if(isChecked && numChecked > max){
cb.setChecked(false);
numChecked--;
}else{
// make questions visible/invisible if they have this as condition
getParent().checkConditions();
}
}
});
options.addView(cb);
checkboxes.add(cb);
}
}
return root;
}
@Override
public boolean isAnswered() {
if(radiogroup != null){
return radiogroup.getCheckedRadioButtonId() != -1;
}else{
int selected = countChecked(checkboxes);
return selected >= min && selected <= max;
}
}
@Override
public void setVisible(boolean visible) {
root.setVisibility(visible ? View.VISIBLE : View.GONE);
}
@Override
public String toString(){
// if the user has not answered the question legally return empty string.
if (!isAnswered()) return "";
String answer = "";
for (Tuple<String, CompoundButton> t : idViews)
if (t.getSecond().isChecked())
answer += (answer.length() == 0 ? "" : ", ") + t.getSecond().getText().toString();
return answer;
}
// PRIVATE HELPERS
private class Tuple<T,V>{
private T t;
private V v;
public Tuple(T t, V v){
this.t = t;
this.v = v;
}
public T getFirst(){return t;}
public V getSecond(){return v;}
}
private String getValueForID(String id){
for(Tuple<String,String> t : options){
if(t.getFirst().equals(id))
return t.getSecond();
}
return null;
}
private int countChecked(ArrayList<CheckBox> arr){
int count = 0;
for(CheckBox cb : arr){
count += cb.isChecked() ? 1 : 0;
}
return count;
}
}