/*
 *	Modulname:	Survey
 *	Autor:		Eyer Leander
 *	Datum:		08.05.2006
 *
 *	(c) Copyright 2005
 */
package survey.model;

import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Vector;

import com.sun.xml.wss.impl.XMLUtil;
import survey.utils.XmlUtil;

/**
 * Data structure representing the survey
 */
public class Model {

    /** Remember the xml definition of the survey */
    private Element xmlRoot;

    /** The Title of the survey */
    private String title;

    /** List of pages in this survey */
    private Vector<Page> pages = new Vector<Page>();

    /** Hash Map for easy lookup of survey elements */
    private HashMap<String, Question> idMap = new HashMap<String, Question>();

    /** Templates */
    private HashMap<String, Element> templateMap = new HashMap<String, Element>();

    /**
     * Constructor
     */
    public Model(File configFile) throws Exception {
	parseFromXML(configFile);
    }


    /**
     * Constructor
     */
    public Model(Element surveyConfigNode) throws Exception {
	parseFromXML(surveyConfigNode);
    }

    /**
     * Path to the config file
     */
    private void parseFromXML(File configFile) throws Exception {
	resetStructure();

	//prepare input stream
	FileInputStream input = new FileInputStream(configFile);

	//prepare parser
	DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
	dbFactory.setIgnoringComments(true);
	dbFactory.setNamespaceAware(false);
	dbFactory.setValidating(false);
	DocumentBuilder builder = dbFactory.newDocumentBuilder();

	//parse the xml file
	org.w3c.dom.Document xmlDocument = builder.parse(input);
	input.close();

	Element surveyConfigNode = xmlDocument.getDocumentElement();

	parseFromXML(surveyConfigNode);
    }

    /**
     * From the Survey Config Element
     */
    private void parseFromXML(Element surveyConfigNode) throws Exception {

	//extract the survey
        xmlRoot = surveyConfigNode;

	//read the title
	title = surveyConfigNode.getAttribute("title");

	//read all type templates
	NodeList templateList = surveyConfigNode.getElementsByTagName("templates");
        for (int i = 0; i < templateList.getLength(); i++) {
	    Element templateNode = (Element) templateList.item(i);
	    NodeList templates = templateNode.getElementsByTagName("typeTemplate");
            for (int j = 0; j < templates.getLength(); j++) {
		Element elementNode = (Element) templates.item(j);
		String name = elementNode.getAttribute("name");
		templateMap.put(name, elementNode);
		//System.out.println("adding template <" + name + ">");
	    }
	}

	//read all pages
	NodeList pageNodes = surveyConfigNode.getElementsByTagName("page");
	for (int p = 0; p < pageNodes.getLength(); p++) {
	    Page page = new Page();
	    pages.add(page);
	    Element pageNode = (Element) pageNodes.item(p);
            if (pageNode.hasAttribute("label")) {
		page.setLabel(pageNode.getAttribute("label"));
	    }

	    NodeList elementNodes = pageNode.getChildNodes();
	    for (int i = 0; i < elementNodes.getLength(); i++) {
		if (elementNodes.item(i) instanceof Element) {
		    Element elementNode = (Element) elementNodes.item(i);
		    if (elementNode.getTagName().equals("element")) {
			Question question = new Question(this, elementNode);
			page.addQuestion(question);
			idMap.put(question.getId(), question);
		    } else {
			Label label = new Label(elementNode);
			page.addLabel(label);
		    }
		}
	    }
	}
    }

    /** Store the results of the survey */
    public void saveXML(PrintWriter writer, boolean printHeader) {
	//todo add code to insert xml definition
	if (printHeader)
	    writer.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");

	writer.println("<Survey>");

	try {
	    String config = XmlUtil.getSerializedXMlElementString(xmlRoot, 8 * 1024 * 1024);
	    writer.println(config);
	} catch (Exception e) {
	    e.printStackTrace();
	}

	writer.println("<Answers>");
        for (Page page:pages) {
	    Iterator<Question> it = page.getQuestions();
	    while (it.hasNext()) {
		it.next().saveXML(writer);
	    }
	}
	writer.println("</Answers>");
	writer.println("</Survey>");
    }

    /** Reset the structure to an empty survey */
    private void resetStructure() {
	idMap.clear();
	pages.clear();
	title = "Unnamed";
    }

    /** Retrive a template */
    public Element getTemplate(String name) {
	return templateMap.get(name);
    }

    /** Read The title of the survey */
    public String getTitle() {
	return title;
    }

    /** Set the title of the survey */
    public void setTitle(String title) {
	this.title = title;
    }

    //-----------------------------------------------------------------------------
    //	Page Handling	

    /**
     * @return an iterator over all pages of the survey
     */
    public Iterator<Page> getPages() {
	return pages.iterator();
    }

    /**
     * @return the number of pages in the survey
     */
    public int countPages() {
	return pages.size();
    }

    /**
     * @return the page at the given index
     */
    public Page getPageAt(int index) {
	return pages.get(index);
    }

    /**
     * Add a page to the survey
     */
    public void addPage(Page page) {
	pages.add(page);
    }

    /**
     * Insert a page in the specified location
     */
    public void insertPageAt(Page page, int index) {
	pages.insertElementAt(page, index);
    }

    /**
     * Remove all pages from the survey
     */
    public void removeAllPages() {
	pages.clear();
    }

    /**
     * Remove the specified page
     */
    public void removePage(Page page) {
	pages.remove(page);
    }

    /**
     * Remove the page at the given index
     */
    public void removePageAt(int index) {
	pages.remove(index);
    }

    /** Load a question object using it's id */
    public Question getQuestionByID(String id) {
	return idMap.get(id);
    }

    /** Returns a hashmap of the questions */
    public HashMap<String, Question> getQuestions() {
	return idMap;
    }
}

