/*
 * Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
 *
 * The contents of this file are subject to the CCM Public
 * License (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the
 * License at http://www.redhat.com/licenses/ccmpl.html.
 *
 * Software distributed under the License is distributed on an
 * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
 * or implied. See the License for the specific language
 * governing rights and limitations under the License.
 *
 */
package com.arsdigita.formbuilder.ui;


import com.arsdigita.formbuilder.util.GlobalizationUtil ; 


// The class we are extending
import com.arsdigita.bebop.MetaForm;

// For instantiating the persistent component
import com.arsdigita.formbuilder.util.FormBuilderUtil;

// Used to generate the form from the attribute metadata
import com.arsdigita.formbuilder.FormSectionGenerator;

// The generation of the form is dependent on the PageState
import com.arsdigita.bebop.PageState;

// We generate a Form
import com.arsdigita.bebop.Form;
import com.arsdigita.bebop.FormSection;
import com.arsdigita.bebop.form.Submit;
import com.arsdigita.bebop.form.RadioGroup;
import com.arsdigita.bebop.form.Option;
import com.arsdigita.bebop.form.TextField;
import com.arsdigita.bebop.Label;

import com.arsdigita.bebop.ColumnPanel;


// Persistent components that we use
import com.arsdigita.formbuilder.PersistentOptionGroup;
import com.arsdigita.formbuilder.PersistentLabel;
import com.arsdigita.formbuilder.PersistentWidget;
import com.arsdigita.formbuilder.SimpleQuestionnaire;

import com.arsdigita.formbuilder.AttributeMetaData;
import com.arsdigita.formbuilder.AttributeMetaDataList;

// Initialization of the form
import com.arsdigita.bebop.event.FormInitListener;
import com.arsdigita.bebop.event.FormSectionEvent;

// Processing of the form
import com.arsdigita.bebop.FormData;
import com.arsdigita.bebop.event.FormProcessListener;
import com.arsdigita.bebop.event.FormSectionEvent;

import com.arsdigita.bebop.parameters.NumberParameter;
import com.arsdigita.bebop.parameters.NumberInRangeValidationListener;
import com.arsdigita.bebop.parameters.NotEmptyValidationListener;
// Data type processing
import com.arsdigita.formbuilder.parameters.PersistentParameterListener;

import com.arsdigita.util.UncheckedWrapperException;

// For sharing persitent widget across methods on a request basis
import com.arsdigita.bebop.RequestLocal;

// The interface that the object for which we are building the form must implement
import com.arsdigita.formbuilder.AttributeMetaDataProvider;

import java.math.BigDecimal;


// logging
import org.apache.log4j.Logger;


/**
 * A dynamic form that lets the admin add or edit a persistent component.
 *
 * @author Peter Marklund
 * @version $Id: //core-platform/dev/src/com/arsdigita/formbuilder/ui/QuestionPropertiesForm.java#12 $
 *
 */
public class QuestionPropertiesForm extends MetaForm {

    public static final String versionId = "$Id: //core-platform/dev/src/com/arsdigita/formbuilder/ui/QuestionPropertiesForm.java#12 $ by $Author: dennis $, $DateTime: 2004/04/07 16:07:11 $";

    private static final Logger s_log =
        Logger.getLogger(QuestionPropertiesForm.class.getName());

    private RequestLocal m_nOptions = new RequestLocal();

    private FormProcessListener m_propertiesProcessListener;

    private RequestLocal m_persistentWidget =
        new RequestLocal() {

            protected Object initialValue(PageState pageState) {

                PersistentWidget widget = null;

                // If this is add mode create a new widget
                if (isAddMode(pageState) || isParameterMode(pageState)) {

                    // Get the currently selected widget
                    String widgetClassName = FormBuildingPage.instance().getWidgetSelection(pageState);

                    // Instantiate and return the peristent widget
                    widget = (PersistentWidget) FormBuilderUtil.instantiateObject(widgetClassName);

                } else {
                    // edit, delete, move
                    // Fetch the widget that we are modifying
                    BigDecimal widgetID = FormBuildingPage.instance().getWidgetID(pageState);

                    s_log.debug("fetching widget with id " + widgetID);
                    widget = (PersistentWidget)FormBuilderUtil.instantiateFactory(widgetID);
                }

                return widget;
            }
        };

    public QuestionPropertiesForm(String name) {
        super(name);
    }

    public Form buildForm(PageState pageState) {

        Form form = new Form("add_component");

        // If we are in view mode this form will not be visible
        if (isViewMode(pageState)) {
            return form;
        }

        // Indicate which Widget this is
        Label widgetLabel =
            new Label(FormBuildingPage.instance().getWidgetSelectionName(pageState));
        widgetLabel.setFontWeight(Label.BOLD);
        form.add(widgetLabel, ColumnPanel.FULL_WIDTH | ColumnPanel.LEFT);

        // Add Label for the question text
        FormBuilderUtil.addTextFieldToForm(form, "question_text", "Question text");

        // Get the Widget properties form section
        form.add(getWidgetFormSection(pageState));

        // Add data type form section
        form.add(getDataTypeFormSection(pageState));

        if (isOptionGroupQuestion(pageState) && isAddMode(pageState)) {
            form.add(getOptionGroupFormSection());
        }

        // Add a submit button
        form.add(new Submit(FormBuildingPage.instance().getPageMode(pageState)));

        // In edit mode we need to prefill some widgets
        form.addInitListener(getInitListener());

        // I am experiencing problems using multiple process listeners
        form.addProcessListener(getSingleProcessListener());

        return form;
    }

    private FormInitListener getInitListener() {

        return new FormInitListener() {
                public void init(FormSectionEvent e) {

                    FormData formData = e.getFormData();
                    PageState pageState = e.getPageState();

                    if (FormBuildingPage.instance().getPageMode(pageState).equals("edit")) {

                        // Get the question position we are editing
                        int position =
                            FormBuildingPage.instance().getQuestionPosition(pageState).intValue();

                        // Get the label
                        SimpleQuestionnaire questionnaire =
                            FormBuildingPage.instance().getQuestionnaire(pageState);
                        PersistentLabel label = questionnaire.getQuestionLabel(position);

                        // Set the question text
                        formData.put("question_text", label.getLabel());
                    }

                    if (isEditMode(pageState) || isParameterMode(pageState)) {
                        // Set the parameter name
                        formData.put("parameterName", FormBuildingPage.instance().getParameterName(pageState));
                    }
                }
            };
    }

    private FormProcessListener getSingleProcessListener() {

        return new FormProcessListener() {

                public void process(FormSectionEvent formEvent) {

                    PageState pageState = formEvent.getPageState();

                    // If we are in edit mode and the listener has
                    // attribute metadata - don't set the parameter name
                    if (FormBuildingPage.instance().getQuestionnaire(pageState).listenerHasMetaData()) {
                        formEvent.getFormData().put("parameterName", null);
                    }

                    // Set the Bean properties of the Widget first
                    try {
                        m_propertiesProcessListener.process(formEvent);

                    } catch (com.arsdigita.bebop.FormProcessException e) {
                        throw new UncheckedWrapperException(e);
                    }

                    // If we are in parameter mode adding a question - use the
                    // parameter name from the listener to override any change
                    // the admin made
                    String parameterName = FormBuildingPage.instance().getParameterName(pageState);
                    if (isParameterMode(pageState)) {
                        getPersistentWidget(pageState).setParameterName(parameterName);
                    }

                    // Set the datatype properties of the Widget and save
                    processDataTypes(formEvent);

                    if (QuestionPropertiesForm.this.isAddMode(pageState)) {
                        // Add the widget to the questionnaire
                        processQuestionnaire(formEvent);
                    }

                    // Store number of options if this is an option group
                    if (isOptionGroupQuestion(pageState) && isAddMode(pageState)) {
                        processOptionGroup(formEvent);
                    }
                }
            };
    }

    /**
     * Add question to questionnaire
     */
    private void processQuestionnaire(FormSectionEvent formEvent) {

        FormData formData = formEvent.getFormData();
        PageState pageState = formEvent.getPageState();

        // Retrieve the SimpleQuestionnaire
        SimpleQuestionnaire questionnaire =
            FormBuildingPage.instance().getQuestionnaire(pageState);

        // Get the question position
        Integer questionPosition = FormBuildingPage.instance().getQuestionPosition(pageState);

        // Get the question text
        String questionText = formData.getString("question_text");

        if (questionPosition == null) {

            // Add the question at the default position
            questionnaire.addQuestion(questionText,
                                      getPersistentWidget(pageState));

        } else {

            // Add the question at the specified position
            questionnaire.addQuestion(questionText,
                                      getPersistentWidget(pageState),
                                      questionPosition.intValue());

        }

        // Save the questionnaire
        questionnaire.save();
    }

    /**
     * Set datatype information of the widget and save the widget
     */
    private void processDataTypes(FormSectionEvent formEvent) {

        FormData formData = formEvent.getFormData();
        PageState pageState = formEvent.getPageState();

        // Fetch the widget
        PersistentWidget widget = getPersistentWidget(pageState);

        // If we are editing - first remove any existing listeners
        if (isEditMode(pageState)) {
            widget.clearValidationListeners();
        }

        AttributeMetaData metaData = getParameterMetaData(pageState);

        boolean isRequired = false;
        if (metaData != null) {
            isRequired = metaData.isRequired();

        } else {
            // No parameter metadata - get the value from the admin form

            String formIsRequired = formData.getString("isRequired");
            isRequired = (formIsRequired != null && formIsRequired.equals("yes"));
        }

        // Require there to be more than white space if answer is
        // required
        if (isRequired) {

            String listenerClassName =
                "com.arsdigita.bebop.parameters.NotEmptyValidationListener";
            PersistentParameterListener listener =
                new PersistentParameterListener(listenerClassName);
            widget.addValidationListener(listener);
        }

        // Set ParameterModel
        // We only do this for text fields
        if (widget instanceof com.arsdigita.formbuilder.PersistentTextField) {

            String parameterModel = null;

            if (hasParameterType(pageState)) {
                parameterModel = metaData.getAttributeType().getParameterModelClass().getName();
            } else {
                // No parameter type - get it from the admin form instead

                parameterModel = formData.getString("parameterModel");
            }

            if (parameterModel != null) {

                widget.setParameterModel(parameterModel);
            }

        }

        // Save the widget now since there is no more processing of it
        // to be done
        widget.save();
    }

    /**
     * Store the number of options in a request local variable
     */
    private void processOptionGroup(FormSectionEvent formEvent) {

        FormData formData = formEvent.getFormData();
        PageState pageState = formEvent.getPageState();

        String nOptions = formData.get("nOptions").toString();

        // This value is used in the redirect from the FormBuildingPage
        m_nOptions.set(pageState, nOptions);
    }

    /**
     * Get the FormSection for editing attributes of the Widget
     */
    private FormSection getWidgetFormSection(PageState pageState) {

        // Use the FormGenerator to generate the form from the attribute metadata
        FormSectionGenerator formGenerator =
            new FormSectionGenerator((AttributeMetaDataProvider)getPersistentWidget(pageState));

        // We don't want to add the process listener here
        FormSection widgetFormSection =
            formGenerator.generateFormSection(false,
                                              FormBuildingPage.instance().getPageMode(pageState).equals("add"));

        m_propertiesProcessListener = formGenerator.getSetProcessListener();

        return widgetFormSection;
    }

    public PersistentWidget getPersistentWidget(PageState pageState) {

        return (PersistentWidget)m_persistentWidget.get(pageState);
    }

    private FormSection getDataTypeFormSection(PageState pageState) {

        FormSection typeSection = new FormSection();

        // For all Widgets we may want to indicate
        // that answer is required. Default is not required
        // Only do this if there is no parameter provided
        if (!FormBuildingPage.instance().getQuestionnaire(pageState).listenerHasMetaData()) {

            typeSection.add(new Label(GlobalizationUtil.globalize("formbuilder.ui.is_answer_required")));
            RadioGroup requiredGroup = new RadioGroup("isRequired");
            requiredGroup.addOption(new Option("yes", "Yes"));
            requiredGroup.addOption(new Option("no", "No"));

            if (isAddMode(pageState)) {
                requiredGroup.setOptionSelected("no");
            } else {
                // We are editing so get the current selection
                s_log.debug("setting option selected " + (questionIsRequired(pageState) ? "yes" : "no"));
                requiredGroup.setOptionSelected(questionIsRequired(pageState) ? "yes" : "no");
            }

            typeSection.add(requiredGroup);
        }

        // Data type seems to only make sense for Text Fields
        PersistentWidget widget = getPersistentWidget(pageState);
        if (widget instanceof com.arsdigita.formbuilder.PersistentTextField
            && !hasParameterType(pageState)) {

            typeSection.add(new Label(GlobalizationUtil.globalize("formbuilder.ui.what_is_the_datatype_of_the_answer")));

            RadioGroup typeGroup = new RadioGroup("parameterModel");
            typeGroup.addOption(new Option("com.arsdigita.bebop.parameters.StringParameter", "Text"));
            typeGroup.addOption(new Option("com.arsdigita.bebop.parameters.NumberParameter", "Number"));
            typeGroup.addOption(new Option("com.arsdigita.bebop.parameters.URLParameter", "URL"));
            typeGroup.addOption(new Option("com.arsdigita.bebop.parameters.EmailParameter", "Email"));

            if (isAddMode(pageState)) {
                typeGroup.setOptionSelected("com.arsdigita.bebop.parameters.StringParameter");

            } else if (isEditMode(pageState)) {

                if (widget.getParameterModel() != null) {

                    typeGroup.setOptionSelected(widget.getParameterModel());
                } else {
                    typeGroup.setOptionSelected("text");
                }
            }

            typeSection.add(typeGroup);
        }

        return typeSection;
    }

    private boolean isOptionGroupQuestion(PageState pageState) {

        return getPersistentWidget(pageState)
            instanceof com.arsdigita.formbuilder.PersistentOptionGroup;
    }

    private FormSection getOptionGroupFormSection() {

        FormSection optionSection = new FormSection();

        optionSection.add(new Label(GlobalizationUtil.globalize("formbuilder.ui.number_of_options_150_required")));

        TextField nOptionsField = new TextField(new NumberParameter("nOptions"));
        nOptionsField.addValidationListener(new NotEmptyValidationListener());
        nOptionsField.addValidationListener(new NumberInRangeValidationListener(1, 50));

        optionSection.add(nOptionsField);

        return optionSection;
    }

    public String getNumberOfOptions(PageState pageState) {

        if (isAddMode(pageState)) {
            return m_nOptions.get(pageState).toString();

        } else {

            // Get the number of options from the option group we are editing
            PersistentOptionGroup optionGroup =
                (PersistentOptionGroup)getPersistentWidget(pageState);
            return Integer.toString(optionGroup.getOptions().size());

        }
    }

    /**
     * On this form the parameter mode is also considered to be a kind of add mode
     */
    private boolean isAddMode(PageState pageState) {

        String pageMode = FormBuildingPage.instance().getPageMode(pageState);

        return pageMode.equals("add") || pageMode.equals("parameter");
    }

    private boolean isParameterMode(PageState pageState) {
        return FormBuildingPage.instance().getPageMode(pageState).equals("parameter");
    }

    private boolean isViewMode(PageState pageState) {
        return FormBuildingPage.instance().getPageMode(pageState).equals("view");
    }

    private boolean isEditMode(PageState pageState) {
        return FormBuildingPage.instance().getPageMode(pageState).equals("edit");
    }

    /**
     * A question is considered required if its widget has a NotEmptyValidationListener
     * added to it.
     */
    private boolean questionIsRequired(PageState pageState) {
        PersistentWidget widget = getPersistentWidget(pageState);

        // True if NotEmptyValidationListener added
        return FormBuilderUtil.isRequired(widget);
    }

    public AttributeMetaData getParameterMetaData(PageState pageState) {

        String parameterName = FormBuildingPage.instance().getParameterName(pageState);

        if (parameterName == null) {
            return null;
        }

        AttributeMetaDataList metaDataList =
            FormBuildingPage.instance().getQuestionnaire(pageState).getListenerMetaData();

        if (metaDataList == null) {
            return null;
        } else {
            return metaDataList.getByParameterName(parameterName);
        }
    }

    public boolean hasParameterType(PageState pageState) {

        AttributeMetaData metaData = getParameterMetaData(pageState);

        if (metaData == null) {
            return false;
        }

        return !(metaData.getAttributeType() == null);
    }
}
