/*
 *	Modulname:	SurveyPane
 *	Autor:		Eyer Leander
 *	Datum:		08.05.2006
 *
 *	(c) Copyright 2005
 */
package survey;

import survey.gui.*;
import survey.gui.Renderer;
import survey.model.*;
import survey.model.Label;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Iterator;
import java.util.Vector;
import java.util.HashMap;
import java.util.Set;

import org.w3c.dom.Element;

/**
 * The Main Panel displaying a survey
 */
public class Survey extends JPanel implements QuestionListener {

    // global definition of fonts
    public static Font FONT_SURVEY_TITLE = new Font("SansSerif", Font.BOLD, 16);
    public static Font FONT_PAGE_TITLE = new Font("SansSerif", Font.PLAIN, 14);
    //public static Font FONT_LABEL = new Font("SansSerif", Font.BOLD, 12);
    public static Font FONT_LABEL = new Font("SansSerif", Font.PLAIN, 12);
    public static Font FONT_QUESTION_NUMBER = new Font("SansSerif", Font.BOLD, 12);
    public static Font FONT_QUESTION_TEXT = new Font("SansSerif", Font.PLAIN, 12);
    public static Font FONT_QUESTION_ANSWER = new Font("SansSerif", Font.PLAIN, 12);

    /**
     * Listeners
     */
    private Vector<SurveyListener> listeners = new Vector<SurveyListener>();

    /**
     * The Datamodel
     */
    private Model model;

    /**
     * The button for finishing the survey
     */
    private JButton btnFinish;

    // Page management
    /**
     * The panel containing the question objects
     */
    private JPanel pageArea;
    /**
     * The card layout used to switch between pages
     */
    private CardLayout pager;
    /**
     * Active Page Index
     */
    private int cPage = 0;

    /**
     * The vector of pages
     */
    private Vector<JPanel> pageComponents = new Vector<JPanel>();

    /**
     * The label indicating the current page
     */
    private JLabel pageStatusLabel;

    /**
     * Navigation
     */
    private JButton btnBack;
    private JButton btnNext;

    /**
     * Constructor
     */
    public Survey(String configFilePath) throws Exception {
	this.model = new Model(new File(configFilePath));
	createUI();
	start();
    }


    /**
     * Constructor
     */
    public Survey(Element surveyConfigNode) throws Exception {
	this.model = new Model(surveyConfigNode);
	createUI();
	start();
    }



    /**
     * Add a listener which will be informed when the survey is finished
     */
    public void addListener(SurveyListener listener) {
	listeners.add(listener);
    }

    /**
     * Remove a listener
     */
    public void removeListener(SurveyListener listener) {
	listeners.remove(listener);
    }

    /**
     * Change the Editable State of a question
     * @param id
     * @param editable
     */
    public void setEditable(String id, boolean editable) {
	Question question = model.getQuestionByID(id);
	if (question != null) {
	    question.setEditable(editable);
	} else {
	    System.out.println("[setEditable] No Question With Id <" + id + ">");
	}

    }

    /** Returns the model of the survey */
    public Model getModel() {
	return model;
    }

    /** Set the answer of an element */
    public void setAnswer(String id, Object answer) {
	Question question = model.getQuestionByID(id);
	if (question != null) {
	    question.getContentType().setAnswer(answer);
	    Renderer rend = question.getRenderer();
	    if (rend != null) {
		rend.updateRenderer();
	    }
	} else {
	    System.out.println("[setAnswer] No Question With Id <" + id + ">");
	}
    }

    public void updateRenderers() {
	for (Question question : model.getQuestions().values()) {
	    Renderer rend = question.getRenderer();
	    if (rend != null)
		rend.updateRenderer();
	}
    }

    public void gotoPage(int index) {
		cPage = index;
		switchPage();
    }


    /**
     * Store the results of the survey
     */
    public void saveResult(String outputXmlFile) throws IOException {
        File result = new File(outputXmlFile);
        saveResult(new PrintWriter(new FileWriter(result)), true);
    }

    /**
     * Store the results of the survey
     */
    public void saveResult(PrintWriter writer, boolean printHeader) throws IOException {
        model.saveXML(writer, printHeader);

        writer.flush();
        writer.close();
    }


    /**
     * Start the survey
     */
    private void start() {
	//display the first survey page
	cPage = 0;
	switchPage();
    }

    /**
     * Create the User Interface of the survey
     */
    private void createUI() {
	//basic configuration
	setLayout(new BorderLayout(12, 12));
	setBorder(BorderFactory.createEmptyBorder(12, 12, 12, 12));

	//the title of the survey
	String title = model.getTitle();
	if (title != null && !title.equals("")) {
	    JLabel titleLabel = new JLabel(title);
	    titleLabel.setFont(FONT_SURVEY_TITLE);
	    add(titleLabel, BorderLayout.NORTH);
	}

	//the central area (with the questions) is warpped within a scrollpane
	pageArea = new JPanel();
	pager = new CardLayout();
	pageArea.setLayout(pager);

	//create the survey pages
	createPages();
	for (int i = 0; i < pageComponents.size(); i++) {
	    JScrollPane scroll = new JScrollPane(pageComponents.get(i),
						 JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
						 JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
	    pageArea.add(Integer.toString(i), scroll);
	}

	//scrollbar (for the case when we don't have enough space for the whole page)
	add(pageArea, BorderLayout.CENTER);

	//control buttons
	JPanel panButtons = new JPanel(new GridLayout(1, 0, 6, 6));
	btnBack = new JButton("Back");
	btnBack.addActionListener(new ActionListener() {
		public void actionPerformed(ActionEvent e) {
		    cPage--;
		    switchPage();
		}
	    });
	panButtons.add(btnBack);
	btnNext = new JButton("Next");
	btnNext.addActionListener(new ActionListener() {
		public void actionPerformed(ActionEvent e) {
		    cPage++;
		    switchPage();
		}
	    });
	panButtons.add(btnNext);
	btnFinish = new JButton("Finish");
	btnFinish.addActionListener(new ActionListener() {
		public void actionPerformed(ActionEvent e) {
		    finish();
		}
	    });
	panButtons.add(btnFinish);


	JPanel statusArea = new JPanel(new BorderLayout());
	statusArea.add(panButtons, BorderLayout.EAST);
	pageStatusLabel = new JLabel();
	pageStatusLabel.setFont(new Font("SansSerif", Font.PLAIN, 12));
	statusArea.add(pageStatusLabel, BorderLayout.WEST);
	add(statusArea, BorderLayout.SOUTH);

	if (pageComponents.size() == 1) {
	    btnBack.setVisible(false);
	    btnNext.setVisible(false);
	    pageStatusLabel.setVisible(false);
	}
    }

    /**
     * Finish the survey
     */
    private void finish() {
	for (SurveyListener listener : listeners) {
	    listener.surveyFinished();
	}
    }

    /**
     * Switch the visible page (this method should be called after the cPage variable
     * has been changed)
     */
    private void switchPage() {
		pager.show(pageArea, Integer.toString(cPage));
		pageStatusLabel.setText("Page " + (cPage + 1) + " of " + pageComponents.size());
		configButtons();
    }

    /**
     * Configure the buttons (set them enabled / disabled)
     */
    private void configButtons() {
	//back button depends only on current page index
	if (cPage == 0) {
	    btnBack.setEnabled(false);
	} else {
	    btnBack.setEnabled(true);
	}

	//next button depends both on current index and mandatory questions
	boolean allMandatoryAnswered = model.getPageAt(cPage).areAllMandatoryAnswered();
	if (cPage == pageComponents.size() - 1) {
		btnNext.setEnabled(false);
		btnFinish.setEnabled(allMandatoryAnswered);
	} else {
		btnNext.setEnabled(allMandatoryAnswered);
		btnFinish.setEnabled(false);
	}
    }

    /**
     * Break the question to the different pages
     */
    private void createPages() {

	Iterator<Page> modelPages = model.getPages();
	while (modelPages.hasNext()) {
	    Page modelPage = modelPages.next();
	    modelPage.addListener(this);

	    String title = modelPage.getLabel().trim();
	    JPanel cPage = startNewPage(title);

	    //datastructure to hold the layout groups
	    HashMap<String, LayoutGroup> layoutGroups = new HashMap<String, LayoutGroup>();
            //and layout objects
	    Vector<Renderer> renderers = new Vector<Renderer>();

	    /*
	    //create the page label if set
	    if (modelPage.getLabel().trim().equals("") == false) {
		JLabel pageLabel = new JLabel("<html><body>" + modelPage.getLabel() + "</body></html>");
		pageLabel.setFont(FONT_PAGE_TITLE);
		cPage.add(pageLabel);
		cPage.add(Box.createVerticalStrut(24));
	    }
	    */

	    //add the questions
	    Iterator<SurveyElement> surveyElements = modelPage.getPageElements();
	    while (surveyElements.hasNext()) {
		SurveyElement element = surveyElements.next();

		if (element instanceof Question) {
		    Question question = (Question) element;

		    QuestionType type = question.getContentType();
                    Renderer renderer = null;

		    // create the views
		    if (type instanceof TypeTextField) {
			renderer = new RendererTextField(question, (TypeTextField) type);
		    } else if (type instanceof TypeTextArea) {
			renderer = new RendererTextArea(question, (TypeTextArea) type);
		    } else if (type instanceof TypeMultiChoice) {
			renderer = new RendererMultipleChoice(question, (TypeMultiChoice) type);
		    } else if (type instanceof TypeYesNo) {
			renderer = new RendererYesNo(question, (TypeYesNo) type);
		    } else if (type instanceof TypeNasa) {
			renderer = new RendererNasa(question, (TypeNasa) type);
			}
		    renderers.add(renderer);
		    cPage.add(renderer);
		    //cPage.add(Box.createVerticalStrut(36));
		    cPage.add(Box.createVerticalStrut(15));

		    //handle layout groups
		    String layoutGroup = question.getLayoutGroup();
		    if (layoutGroup.equals("") == false) {
			LayoutGroup group = layoutGroups.get(layoutGroup);
			if (group == null) {
			    group = new LayoutGroup();
			    layoutGroups.put(layoutGroup, group);
			}

			group.addMember(renderer);
		    }
		} else {
		    Label label = (Label) element;
                    LabelRenderer renderer = new LabelRenderer(label);
		    cPage.add(renderer);
		    cPage.add(Box.createVerticalStrut(36));
		}
	    }

	    cPage.add(Box.createVerticalGlue());

	    //align the layoutgroups
	    Set<String> layoutGroupNames = layoutGroups.keySet();
	    for (String key:layoutGroupNames) {
		LayoutGroup group = layoutGroups.get(key);
		group.layout();
	    }

	    //restrain the size of the questions
	    for (Renderer rend:renderers) {
		rend.restrainSize();
	    }
	}

    }

    /**
     * Create a new page object
     */
    private JPanel startNewPage(String title) {
	JPanel cPage = new JPanel();
	//cPage.setBorder(BorderFactory.createEmptyBorder(12, 12, 12, 12));
	cPage.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), title) );
	cPage.setLayout(new BoxLayout(cPage, BoxLayout.Y_AXIS));
	pageComponents.add(cPage);
	return cPage;
    }

    /**
     * This method will be called when the state of a question is changed
     * (i.e. the question becomes answered)
     *
     * @param question The question whose state changed
     */
    public void questionStateChanged(Question question) {
	configButtons();
    }
}

