/*
 * Copyright (C) 2002-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.editors;


import com.arsdigita.formbuilder.util.GlobalizationUtil ; 


import com.arsdigita.bebop.FormData;
import com.arsdigita.bebop.RequestLocal;
import com.arsdigita.bebop.FormProcessException;
import com.arsdigita.bebop.FormSection;
import com.arsdigita.bebop.Label;
import com.arsdigita.bebop.PageState;

import com.arsdigita.bebop.event.FormInitListener;
import com.arsdigita.bebop.event.FormProcessListener;
import com.arsdigita.bebop.event.FormSectionEvent;

import com.arsdigita.bebop.form.TextField;
import com.arsdigita.bebop.form.TextArea;
import com.arsdigita.bebop.form.RadioGroup;
import com.arsdigita.bebop.form.Option;

import com.arsdigita.bebop.parameters.StringParameter;

import com.arsdigita.domain.DataObjectNotFoundException;

import com.arsdigita.formbuilder.PersistentWidget;
import com.arsdigita.formbuilder.parameters.PersistentParameterListener;


import java.math.BigDecimal;
import com.arsdigita.formbuilder.PersistentFormSection;
import com.arsdigita.bebop.SingleSelectionModel;

import com.arsdigita.formbuilder.ui.PropertiesForm;
import com.arsdigita.util.Assert;
import com.arsdigita.bebop.parameters.URLTokenValidationListener;
import com.arsdigita.bebop.parameters.NotNullValidationListener;
import com.arsdigita.bebop.parameters.StringInRangeValidationListener;
import com.arsdigita.bebop.ColumnPanel;
import com.arsdigita.bebop.parameters.ParameterModel;
import com.arsdigita.xml.Element;

public abstract class WidgetForm extends PropertiesForm {
    private SingleSelectionModel m_form;
    private SingleSelectionModel m_action;

    protected final static String NAME = "name";
    protected final static String DESCRIPTION = "description";

    protected TextField m_name;
    protected TextArea m_description;
    private RadioGroup m_required;

    private RequestLocal m_widget = new RequestLocal();

    private Label m_script = new Label(
        "<script language=\"javascript\">\n" +
        "    var defaulting = false;\n" +
        "\n" +
        "    function urlize(title) {\n" +
        "        var result = \"\";\n" +
        "        for (var i = 0; i < title.length; i++) {\n" +
        "            result = result + substitute(title.charAt(i));\n" +
        "        }\n" +
        "        return escape(result);\n" +
        "    }\n" +
        "\n" +
        "    function substitute(c) {\n" +
        "        switch (c) {\n" +
        "            case \" \":\n" +
        "                return \"-\";\n" +
        "            case \"'\":\n" +
        "            case \",\":\n" +
        "            case \":\":\n" +
        "            case \";\":\n" +
        "            case \"?\":\n" +
        "            case \"!\":\n" +
        "                return \"\";\n" +
        "            default:\n" +
        "                return c.toLowerCase();\n" +
        "        }\n" +
        "    }\n" +
        "</script>",
        false
        );

    public WidgetForm(String name,
                      SingleSelectionModel form,
                      SingleSelectionModel action) {
        super(name);

        m_form = form;
        m_action = action;

        addInitListener(new WidgetFormInitListener());
        addProcessListener(new WidgetFormProcessListener());
    }

    
    
    public SingleSelectionModel getSelection() {
        return m_form;
    }

    protected abstract PersistentWidget getWidget();

    protected abstract PersistentWidget getWidget(BigDecimal id)
        throws DataObjectNotFoundException;

    protected PersistentWidget getWidget(PageState state) {
        if (m_widget.get(state) == null) {
            BigDecimal action = (BigDecimal)m_action.getSelectedKey(state);

            PersistentWidget widget = null;
            if (action == null) {
                widget = getWidget();
                m_action.setSelectedKey(state, widget.setID());
            } else {
                try {
                    widget = getWidget(action);
                } catch (DataObjectNotFoundException ex) {
                    throw new com.arsdigita.util.UncheckedWrapperException("cannot find persistent widget " + action, ex);
                }
            }
            m_widget.set(state, widget);
        }

        return (PersistentWidget)m_widget.get(state);
    }

    protected void addWidgets(FormSection section) {
        super.addWidgets(section);

        m_name = new TextField(new StringParameter(NAME));
 
        if (showName()) {
            m_name.addValidationListener(new URLTokenValidationListener());
            m_name.addValidationListener(new NotNullValidationListener());
            m_name.addValidationListener(new StringInRangeValidationListener(1, 40));
            m_name.setSize(30);
            section.add(new Label(GlobalizationUtil.globalize("formbuilder.ui.editors.name")), ColumnPanel.RIGHT);
            section.add(m_name);
        }

        if (showDescription()) {
            m_description = new TextArea(new StringParameter(DESCRIPTION));
            m_description.setCols(50);
            m_description.setRows(5);
            //m_description.addValidationListener(new NotNullValidationListener());
            m_description.addValidationListener(new StringInRangeValidationListener(0, 200));
            section.add(new Label(GlobalizationUtil.globalize("formbuilder.ui.editors.description")), ColumnPanel.RIGHT);
            section.add(m_description);
        }

        if (includeRequiredRadioGroup()) {
            m_required = new RadioGroup("required");
            section.add(new Label(GlobalizationUtil.globalize("formbuilder.ui.editors.answer_required")), ColumnPanel.RIGHT);
            section.add(m_required);
            
            m_required.addOption(new Option(Boolean.TRUE.toString(), "Yes"));
            m_required.addOption(new Option(Boolean.FALSE.toString(), "No"));
            m_required.setOptionSelected(Boolean.TRUE.toString());
        }
    }


    /**
     *  If this is called then the value of the widget that is used for
     *  the passed in model is used to auto-generate the value of the
     *  "name" value.  For instance, if you want the label to dictate
     *  the "name" then you would pass in the label ParameterModel
     *  and add the following to the labelWidget:
     *
     *  labelWidget.setOnFocus("if (this.form." + NAME + ".value == '') { " +
     *                         " defaulting = true; this.form." + NAME +
     *                         ".value = urlize(this.value); }");
     *  labelWidget.setOnKeyUp(
     *       "if (defaulting) { this.form." + NAME +
     *       ".value = urlize(this.value) }"
     *       );
     *
     *
     *  This can only be called after calling addWidgets()
     */
    protected void automaticallySetName(ParameterModel model) {
        Assert.assertNotNull(m_name);
        m_name.setOnFocus("defaulting = false");
        m_name.setOnBlur(
            "if (this.value == '') " +
            "{ defaulting = true; this.value = urlize(this.form." + 
            model.getName() + ".value) }"
            );
    }

    /**
     *  This determines whether or not the "required value" radio group
     *  is part of the form.  This returns true and should be overridden
     *  by fields where it does not make sense to ask.  For instance,
     *  when the widget is a hidden field then asking if it is required
     *  or not does not make any logical sense so those widgets should
     *  return false.
     *
     *  This will always return the same value for a given widget no matter
     *  what state the widget is in.
     */
    protected boolean includeRequiredRadioGroup() {
        return true;
    } 


    public void generateXML(PageState ps, Element parent) {
        m_script.generateXML(ps, parent);
        super.generateXML(ps, parent);
    }

    /**
     * Should the HTML name be on the form? Can be overridden
     * by sub classes.
     */
    protected boolean showName() {

        return true;
    }

    /**
     * Should the description be on the form? Can be overridden
     * by sub classes.
     */
    protected boolean showDescription() {

        return true;
    }

    protected void initWidgets(FormSectionEvent e,
                               PersistentWidget widget)
        throws FormProcessException {

        PageState state = e.getPageState();

        if (widget == null) {
            if (showName()) {
                m_name.setValue(state, "");
            }

            if (showDescription()) {
                m_description.setValue(state, "");
            }
        } else {
            if (showName()) {
                m_name.setValue(state, widget.getParameterName());
            }

            if (showDescription()) {
                m_description.setValue(state, widget.getDescription());
            }
        }
    }

    protected void processWidgets(FormSectionEvent e,
                                  PersistentWidget widget)
        throws FormProcessException {

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

        String name = getName(pageState, data);

        if (showDescription()) {
            String description = (String)data.get(DESCRIPTION);
            widget.setDescription(description);
        }

        widget.setParameterName(name);

        // Clear current validation listeners
        if (!widget.isNew()) {
            widget.clearValidationListeners();
        }

        if (includeRequiredRadioGroup()) {
            if (((String)m_required.getValue(pageState)).equals(Boolean.TRUE.toString())) {
                // Answer is required
                String listenerClassName =
                    "com.arsdigita.bebop.parameters.NotEmptyValidationListener";
                PersistentParameterListener listener =
                    new PersistentParameterListener(listenerClassName);
                widget.addValidationListener(listener);
                
            } else {
                // Answer is not required
                // We need not add any listener
            }
        }
    }

    protected String getName(PageState pageState, FormData formData) {

        return (String)formData.get(NAME);
    }

    protected void addToForm(FormSectionEvent e,
                             PersistentWidget widget)
        throws FormProcessException {

        BigDecimal form_id = (BigDecimal)m_form.getSelectedKey(e.getPageState());

        PersistentFormSection form = null;
        try {
            form = new PersistentFormSection(form_id);
        } catch (DataObjectNotFoundException ex) {
            throw new FormProcessException("cannot find form", ex);
        }

        form.addComponent(widget);
        form.save();
    }


    private class WidgetFormInitListener implements FormInitListener {
        public void init(FormSectionEvent e)
            throws FormProcessException {

            PageState state = e.getPageState();

            BigDecimal action = (BigDecimal)m_action.getSelectedKey(state);

            if (action == null) {
                initWidgets(e, null);
            } else {
                PersistentWidget widget = null;
                try {
                    widget = getWidget(action);
                } catch (DataObjectNotFoundException ex) {
                    throw new FormProcessException("cannot find persistent widget " + action, ex);
                }
                initWidgets(e, widget);
            }
        }
    }

    private class WidgetFormProcessListener implements FormProcessListener {
        public void process(FormSectionEvent e)
            throws FormProcessException {

            PageState state = e.getPageState();

            if (isCancelled(state))
                return;

            BigDecimal action = (BigDecimal)m_action.getSelectedKey(state);

            PersistentWidget widget = getWidget(state);

            processWidgets(e, widget);
            widget.save();

            if (action == null)
                addToForm(e, widget);
        }
    }
}
