/*
 *	Modulname:	SurveyElement
 *	Autor:		Eyer Leander
 *	Datum:		08.05.2006
 *
 *	(c) Copyright 2005
 */
package survey.model;

import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import survey.utils.XmlUtil;
import survey.gui.Renderer;

import java.io.PrintWriter;
import java.util.Vector;
import java.util.Iterator;

/**
 * A single question of the survey
 */
public class Question extends SurveyElement {

	/** Navigation back to survey model */
	private Model model;

	/** Renderer displaying this question */
	private Renderer renderer;

	/** Can the question be edited by the user */
	private boolean editable = true;

	/** The label of the survey */
	private String label = "";

	/** The ID of this survey element */
	private String id = "";

	/** Layout Group of this question */
	private String layoutGroup = "";
	
	/** Is an answer requried or not */
	private boolean mandatory = false;

	/** Dependencies of this question */
	private DependencyHandler dependencyHandler;

	/** Whatever content this node has */
	private QuestionType questionType;

	/** State Change Listener */
	private Vector<QuestionListener> listeners = new Vector<QuestionListener>();

	/**
	 * Constructor
	 * @param xmlNode
	 * @throws Exception
	 */
	public Question(Model model, Element xmlNode) throws Exception {
		this.model = model;
		dependencyHandler = new DependencyHandler(model);
		parseXML(xmlNode);
	}

	/** Set a new renderer */
	public void setRenderer(Renderer rend) {
		this.renderer = rend;
	}

	/** Give access to the renderer */
	public Renderer getRenderer() {
		return renderer;
	}

	/** Add a Listener */
	public void addListener(QuestionListener listener) {
		listeners.add(listener);
	}

	/** Remove a Listener */
	public void removeListener(QuestionListener listener) {
		listeners.remove(listener);
	}

	/** Access to the dependency handling object of this question */
	public DependencyHandler getDependencyHandler() {
		return dependencyHandler;
	}

	/** Fire an State Changed Event for this question */
	public void fireStateChagned() {
		for (QuestionListener listener:listeners) {
			listener.questionStateChanged(this);
		}
	}

	/**
	 * Return if an answer has been set for this question
	 */
	public boolean hasBeenAnswered() {
		// we only ask this property if the question is actually displayed
		if (dependencyHandler.isFullfilled() == false)
			return true;
		if (editable == false)
			return true;
		return questionType.hasBeenAnswered();
	}


	/**
	 * Create this question element from the information in the xml node
	 * @param elementRoot
	 * @throws Exception
	 */
	private void parseXML(Element elementRoot) throws Exception {
		id = elementRoot.getAttribute("id");
		if (elementRoot.hasAttribute("layoutGroup")) {
			layoutGroup = elementRoot.getAttribute("layoutGroup");
		}
		if (elementRoot.hasAttribute("mandatory")) {
			mandatory = elementRoot.getAttribute("mandatory").equalsIgnoreCase("true");
		}

		//try to find a label node
		NodeList labelNodes = elementRoot.getElementsByTagName("label");
        if (labelNodes.getLength() != 0) {
			Element labelNode = (Element) labelNodes.item(0);
			//setLabel(labelNode.getTextContent());

			String text = XmlUtil.getSerializedXMlElementString(labelNode, 100*1024);
			    //the serialized code still contains the <label> and </label> notes, we have to remove them
			    try {
				text = text.substring(text.indexOf('>') + 1);
				text = text.substring(0, text.lastIndexOf('<'));
			    } catch (Exception e) {}
			setLabel(text);
		}

		//find the type node
		Element typeNode = (Element) elementRoot.getElementsByTagName("type").item(0);
		String className = typeNode.getAttribute("className");

		//replace with template if the type is set to a template
		if (className.startsWith("temp.")) {
			className = className.substring(5);
			typeNode = model.getTemplate(className);
			className = typeNode.getAttribute("className");
		}

		//instantiate other known types
		if (className.equals("survey.MultipleChoice")) {
			questionType = new TypeMultiChoice(this, (Element) typeNode.getElementsByTagName("multipleChoiceConfig").item(0));
		} else if (className.equals("survey.TextField")) {
  	        questionType = new TypeTextField(this);
		} else if (className.equals("survey.TextArea")) {
			questionType = new TypeTextArea(this);	
		} else if (className.equals("survey.YesNo")) {
			questionType = new TypeYesNo(this);
		} else if (className.equals("survey.Nasa")) {
			questionType = new TypeNasa(this, (Element) typeNode.getElementsByTagName("nasaConfig").item(0));
		}


		//load dependencies
		NodeList dependencies = elementRoot.getElementsByTagName("dependency");
		for (int i = 0; i < dependencies.getLength(); i++) {
			Element dependency = (Element) dependencies.item(i);
			String questionID = dependency.getAttribute("id");
			String answer = dependency.getAttribute("answer");
			dependencyHandler.addDependency(questionID, answer);
		}

	}

	/** The writer we use to store the results */
    public void saveXML(PrintWriter writer) {
		if (questionType == null) {
			return;
		}

		// write the answer of this question
		writer.println("<QA id=\"" + id + "\">");
		writer.println("	<Question>" + encodeForXML(label) + "</Question>");
		writer.println("	<Answer>" + encodeForXML(questionType.getAnswer().toString()) + "</Answer>");
        writer.println("</QA>");
	}

	/** Enbcode for xml export	**/
	public static String encodeForXML(String str) {
		StringBuffer out = new StringBuffer(str.length() + 10);

		for (int i = 0; i < str.length(); i++) {
			char ch = str.charAt(i);

			switch (ch) {
				case '<':
					out.append("&lt;");
					break;

				case '>':
					out.append("&gt;");
					break;

				case '&':
					out.append("&amp;");
					break;

				case '"':
					out.append("&quot;");
					break;

				case '\'':
					out.append("&apos;");
					break;

				case '\r':
				case '\n':
					out.append(ch);
					break;

				default:
					if (((int)ch < 32) || ((int)ch > 126)) {
						out.append("&#x");
						out.append(Integer.toString((int)ch, 16));
						out.append(';');
					}
					else {
						out.append(ch);
					}
			}
		}
		return out.toString();
	}


	public String getLabel() {
		return label;
	}

	public void setLabel(String label) {
		this.label = label;
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public boolean isMandatory() {
		return mandatory;
	}

	public void setMandatory(boolean mandatory) {
		this.mandatory = mandatory;
	}

	/** Get the object encoding the type of this survey element */
	public QuestionType getContentType() {
		return questionType;
	}

	public String getLayoutGroup() {
		return layoutGroup;
	}

	public boolean isEditable() {
		return editable;
	}

	public void setEditable(boolean editable) {
		this.editable = editable;
		if (renderer != null) {
			renderer.setEditable(editable);
		}
	}
}

