/*
 * 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 ; 


import com.arsdigita.formbuilder.FormBuilderDispatcher;

// Bebop components that we use on this page
import com.arsdigita.bebop.Form;
import com.arsdigita.bebop.MetaForm;
import com.arsdigita.bebop.Label;
import com.arsdigita.bebop.form.TextField;
import com.arsdigita.bebop.form.Submit;
import com.arsdigita.bebop.form.RadioGroup;
import com.arsdigita.bebop.form.CheckboxGroup;
import com.arsdigita.bebop.form.OptionGroup;
import com.arsdigita.bebop.form.Option;

import com.arsdigita.bebop.ColumnPanel;
import com.arsdigita.bebop.PageState;
import com.arsdigita.bebop.BlockStylable;
import com.arsdigita.bebop.RequestLocal;


import com.arsdigita.bebop.parameters.NotEmptyValidationListener;

// Persistent components
import com.arsdigita.formbuilder.PersistentOptionGroup;
import com.arsdigita.formbuilder.PersistentOption;
import java.math.BigDecimal;

import com.arsdigita.formbuilder.util.FormBuilderUtil;

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


// To make the title dynamic
import com.arsdigita.bebop.event.PrintListener;
import com.arsdigita.bebop.event.PrintEvent;

// For the widget id, widget class and number of options
import com.arsdigita.bebop.parameters.BigDecimalParameter;
import com.arsdigita.bebop.parameters.StringParameter;
import com.arsdigita.bebop.parameters.IntegerParameter;

import java.util.Iterator;

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


/**
 * On this page the admin can specify the options of an OptionGroup
 * (examples of OptionGroups are radio groups, checbox groups and selects).
 * Each option has a value (gets submitted) and a label (gets displayed).
 *
 * @author Peter Marklund
 * @version $Id: //core-platform/dev/src/com/arsdigita/formbuilder/ui/OptionGroupPage.java#13 $
 *
 */
public class OptionGroupPage extends TemplatePage {

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

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

    private static OptionGroupPage s_instance;
    private OptionGroup m_defaultOptionGroup;

    // State parameters of this page
    public BigDecimalParameter m_formID;
    public BigDecimalParameter m_widgetID;
    public StringParameter m_widgetClass;
    public IntegerParameter m_nOptions;
    public StringParameter m_pageMode;
    private static final String PERSISTENT_WIDGET_PREFIX = "com.arsdigita.formbuilder.Persistent";
    private static final int PERSISTENT_WIDGET_PREFIX_LENGTH = PERSISTENT_WIDGET_PREFIX.length();

    RequestLocal m_optionGroup = new RequestLocal() {
            public Object initialValue(PageState pageState) {

                String widgetID = getWidgetID(pageState).toString();

                return (PersistentOptionGroup)
                    FormBuilderUtil.instantiateObjectOneArg(getWidgetClass(pageState),
                                                            new BigDecimal(widgetID));
            }
        };

    /**
     * Private Constructor since we are using the Singleton design pattern
     */
    private OptionGroupPage() {
        super(getStaticTitle());
    }

    /**
     * Provide access to the single instance of this page.
     */
    public static OptionGroupPage instance() {

        if (s_instance == null) {
            s_instance = new OptionGroupPage();
        }

        return s_instance;
    }

    public static String getStaticTitle() {
        return "Specify Options for the OptionGroup";
    }

    public String getRelativeURL() {
        return "options-specify"  + FormBuilderDispatcher.getURLEnding();
    }

    protected void buildPage() {

        add(FormBuilderUtil.createIndexLink());

        setupGlobalStateParameters();

        setDynamicTitle();

        add(getOptionsForm());

        add(FormBuilderUtil.createIndexLink());
    }

    private void setupGlobalStateParameters() {

        // Initialize the state parameters
        m_formID = new BigDecimalParameter("form_id");
        m_widgetID = new BigDecimalParameter("widget_id");
        m_widgetClass = new StringParameter("widget_class");
        m_nOptions = new IntegerParameter("n_options");
        m_pageMode = new StringParameter("page_mode");

        addGlobalStateParam(m_formID);
        addGlobalStateParam(m_widgetID);
        addGlobalStateParam(m_widgetClass);
        addGlobalStateParam(m_nOptions);
        addGlobalStateParam(m_pageMode);
    }

    private void setDynamicTitle() {

        // Make the title show the admin name of the form
        Label titleLabel = new Label(GlobalizationUtil.globalize("formbuilder.ui.specify_options"));
        titleLabel.addPrintListener(
                                    new PrintListener() {
                                        public void prepare(PrintEvent e) {
                                            Label inner_titleLabel = (Label)e.getTarget();

                                            String className =
                                                OptionGroupPage.this.getWidgetClass(e.getPageState()).substring(PERSISTENT_WIDGET_PREFIX_LENGTH);


                                            inner_titleLabel.setLabel("Specify Options for the " + className);
                                        }
                                    }
                                    );
        setTitle(titleLabel);
    }

    private MetaForm getOptionsForm() {

        return new OptionsForm("specify_options");
    }

    private class OptionsForm extends MetaForm {

        RequestLocal m_isMultipleChoice = new RequestLocal() {
                public Object initialValue(PageState pageState) {
                    // Get the widget class from the URL
                    String className = OptionGroupPage.this.getWidgetClass(pageState);

                    // Instantiate a new option group
                    PersistentOptionGroup optionGroup =
                        (PersistentOptionGroup)FormBuilderUtil.instantiateObject(className);

                    return new Boolean(optionGroup.isMultiple());
                }
            };

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

        public Form buildForm(PageState pageState) {

            Form form = new Form("options_form", new ColumnPanel(3));

            // Create the default selection radio group
            if(isMultipleChoice(pageState)) {
                m_defaultOptionGroup = new CheckboxGroup("defaultSelection");
            } else {
                m_defaultOptionGroup = new RadioGroup("defaultSelection");
            }
            form.add(m_defaultOptionGroup, BlockStylable.FULL_WIDTH);

            // Headers for the two columns
            form.add(new Label(GlobalizationUtil.globalize("formbuilder.ui.default_selection")));
            form.add(new Label(GlobalizationUtil.globalize("formbuilder.ui.value")));
            form.add(new Label(GlobalizationUtil.globalize("formbuilder.ui.label")));

            if (getPageMode(pageState).equals("add") || getPageMode(pageState).equals("parameter")) {
                // Get the number of options from the URL
                String nOptions = OptionGroupPage.this.getNOptions(pageState).toString();
                // Output all the value/label input fields
                for (int i = 1; i <= Integer.parseInt(nOptions); ++i) {

                    addOption(i, form, "", "");
                }
            } else if (getPageMode(pageState).equals("edit")) {
                Iterator optionIter = getOptionGroup(pageState).getOptions().iterator();
                int counter = 1;
                while (optionIter.hasNext()) {
                    PersistentOption option = (PersistentOption)optionIter.next();

                    addOption(counter, form, option.getParameterValue(), option.getLabel());

                    ++counter;
                }
            }
            // Add a submit button
            form.add(new Submit("submit options"));

            form.addProcessListener(getProcessListener());

            return form;
        }

        private void addOption(int number, Form form, String defaultValue, String defaultLabel) {

            // I don't want any label for the default radio option
            // The option must be in the group
            Option defaultOption = new Option(Integer.toString(number), "");
            defaultOption.setGroup(m_defaultOptionGroup);
            form.add(defaultOption);

            // name and label are mandatory
            TextField optionValue = new TextField("value" + number);
            TextField optionLabel = new TextField("label" + number);
            optionValue.addValidationListener(new NotEmptyValidationListener());
            optionLabel.addValidationListener(new NotEmptyValidationListener());
            form.add(optionValue);
            form.add(optionLabel);

            // If we are editing prefill with values
            optionValue.setDefaultValue(defaultValue);
            optionLabel.setDefaultValue(defaultLabel);
        }

        private boolean isMultipleChoice(PageState pageState) {

            return ((Boolean)m_isMultipleChoice.get(pageState)).booleanValue();
        }
    }

    //*** State Parameters
    public BigDecimal getFormID(PageState pageState) {
        return (BigDecimal)pageState.getValue(m_formID);
    }

    public BigDecimal getWidgetID(PageState pageState) {
        return (BigDecimal)pageState.getValue(m_widgetID);
    }

    public String getWidgetClass(PageState pageState) {
        return (String)pageState.getValue(m_widgetClass);
    }

    public Integer getNOptions (PageState pageState) {
        return (Integer)pageState.getValue(m_nOptions);
    }

    private FormProcessListener getProcessListener() {

        return new FormProcessListener() {
                public void process(FormSectionEvent formEvent) {

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

                    int nOptions = getNOptions(pageState).intValue();
                    String formID = getFormID(pageState).toString();
                    Object defaultOption = formData.get("defaultSelection");

                    // Fetch the option group
                    PersistentOptionGroup optionGroup = getOptionGroup(pageState);

                    String pageMode = getPageMode(pageState);

                    if (pageMode.equals("add") || pageMode.equals("parameter")) {

                        // Loop over the options and add them to the option group
                        for (int i = 1; i <= nOptions; ++i) {

                            String optionValue = formData.getString("value" + i);
                            String optionLabel = formData.getString("label" + i);

                            PersistentOption option =
                                PersistentOption.create(optionValue, optionLabel);

                            optionGroup.addOption(option);

                            if (isSelected(i, defaultOption)) {
                                optionGroup.setOptionSelected(option, true);
                            }
                        }

                    } else if (pageMode.equals("edit")) {

                        // If we are editing - loop over the options and set their values and labels
                        Iterator optionIter = optionGroup.getOptions().iterator();
                        int counter = 1;
                        while (optionIter.hasNext()) {
                            PersistentOption option =
                                (PersistentOption)optionIter.next();

                            String optionValue = formData.getString("value" + counter);
                            String optionLabel = formData.getString("label" + counter);

                            option.setParameterValue(optionValue);
                            option.setLabel(optionLabel);

                            option.save();

                            ++counter;
                        }
                    }
                    // Save the option group
                    optionGroup.save();

                    // Redirect to the preview page
                    String urlParameterString =
                        "form_id=" + formID;
                    FormBuilderUtil.redirect(pageState,
                                             FormBuildingPage.instance(),
                                             urlParameterString);
                }

                private boolean isSelected(int optionNumber, Object defaultSelection) {

                    String numberString = Integer.toString(optionNumber);

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

                    if (defaultSelection instanceof java.lang.String) {
                        return ((String)defaultSelection).equals(numberString);
                    } else {
                        // Multiple defaults selected
                        Object[] selectionArray = (Object[])defaultSelection;
                        for (int i=0; i < selectionArray.length; ++i) {
                            if (((String)selectionArray[i]).equals(numberString)) {
                                return true;
                            }
                        }

                        return false;
                    }
                }
            };
    }

    private String getPageMode(PageState pageState) {
        return (String)pageState.getValue(m_pageMode);
    }

    private PersistentOptionGroup getOptionGroup(PageState pageState) {
        return (PersistentOptionGroup)m_optionGroup.get(pageState);
    }
}
