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

import com.arsdigita.bebop.event.FormSectionEvent;
import com.arsdigita.bebop.form.Submit;
import com.arsdigita.bebop.util.Traversal;
import com.arsdigita.bebop.form.Widget;


/**
 * The Wizard class can be used in conjunction with FormStep to build a series
 * of forms that gather information from the user in several stages. A simple
 * two stage confirmation step would be built like this:
 *
 * <blockquote><pre>
 * // The first step asks the user for a subject and body.
 * FormStep one = new FormStep("one");
 *
 * one.add(new Label("Subject"));
 * final TextField subject = new TextField("subject");
 * one.add(subject);
 *
 * one.add(new Label("Body"));
 * final TextArea body = new TextArea("body");
 * one.add(body);
 *
 *
 * // The second step displays the subject and body to the user as it will
 * // appear when posted.
 * GridPanel two = new GridPanel(1);
 * two.add(new Label() {
 *     public String getLabel(PageState ps) {
 *         return "Subject: " + subject.getValue(ps);
 *     }
 * });
 * two.add(new Label() {
 *     public String getLabel(PageState ps) {
 *         return (String) body.getValue(ps);
 *     }
 * });
 * two.add(new Label("Are you sure?"));
 *
 *
 * // Create the wizard and add the steps in the appropriate order.
 * Wizard form = new Wizard("post");
 * form.add(one);
 * form.add(two);
 *
 * // Add a process listener to actually save the message.
 * form.addProcessListener(new FormProcessListener() {
 *     public void process(FormSectionEvent evt) {
 *         PageState ps = evt.getPageState();
 *         System.out.println("SAVING MESSAGE");
 *         System.out.println("Subject: " + subject.getValue(ps));
 *         System.out.println("Body: " + body.getValue(ps));
 *     }
 * });
 * </pre></blockquote>
 *
 * @author <a href="mailto:rhs@mit.edu">rhs@mit.edu</a>
 * @version $Revision: #9 $ $Date: 2004/04/07 $
 **/

public class Wizard extends MultiStepForm {

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

    private ModalContainer m_steps = new ModalContainer();

    private Submit m_cancel = new Submit("Cancel");
    private Submit m_back = new Submit("<< Back");
    private Submit m_next = new Submit("Next >>");
    private Submit m_finish = new Submit("Finish");

    private Component m_first = null;
    private Component m_last = null;

    public Wizard(String name) {
        this(name, new GridPanel(1));
    }

    public Wizard(String name, Container panel) {
        super(name, panel);
        super.add(m_steps);

        BoxPanel buttons = new BoxPanel(BoxPanel.HORIZONTAL);
        buttons.add(m_cancel);
        buttons.add(m_next);
        buttons.add(m_back);
        buttons.add(m_finish);

        super.add(buttons);

        forwardSubmission();
    }

    public void register(Page p) {
        super.register(p);
        p.setVisibleDefault(m_back, false);
        p.setVisibleDefault(m_finish, false);

        Traversal trav = new Traversal () {
                protected void act(Component c) {
                    if (c instanceof Widget) {
                        ((Widget) c).setValidationGuard(
                                                        new Widget.ValidationGuard() {
                                                            public boolean shouldValidate(PageState ps) {
                                                                return m_back.isSelected(ps);
                                                            }
                                                        });
                    }
                }
            };

        trav.preorder(this);
    }

    public void add(Container step) {
        if (m_first == null) { m_first = step; }
        m_steps.add(step);
        m_last = step;
    }

    public void add(Container step, int constraints) {
        add(step);
    }

    public Submit getCancel() {
        return m_cancel;
    }

    public Submit getFinish() {
        return m_finish;
    }

    public boolean isFirst(PageState ps) {
        return m_steps.getVisibleComponent(ps) == m_first;
    }

    public boolean isLast(PageState ps) {
        return m_steps.getVisibleComponent(ps) == m_last;
    }

    public void next(PageState ps) {
        if (isFirst(ps)) {
            m_back.setVisible(ps, true);
        }
        m_steps.next(ps);
        if (isLast(ps)) {
            m_next.setVisible(ps, false);
            m_finish.setVisible(ps, true);
        }
    }

    public void previous(PageState ps) {
        if (isLast(ps)) {
            m_next.setVisible(ps, true);
            m_finish.setVisible(ps, false);
        }
        m_steps.previous(ps);
        if (isFirst(ps)) { m_back.setVisible(ps, false); }
    }

    protected void fireSubmitted(FormSectionEvent evt)
        throws FormProcessException {
        super.fireSubmitted(evt);

        PageState ps = evt.getPageState();
        if (m_cancel.isSelected(ps)) {
            fireCancel(evt);
            ps.reset(this);
            throw new FormProcessException("cancel hit");
        }
    }

    protected void fireProcess(FormSectionEvent evt)
        throws FormProcessException {
        PageState ps = evt.getPageState();
        if (m_next.isSelected(ps)) {
            next(ps);
        } else if (m_back.isSelected(ps)) {
            previous(ps);
        } else if (m_cancel.isSelected(ps)) {
            fireCancel(evt);
            ps.reset(this);
        } else if (m_finish.isSelected(evt.getPageState())) {
            super.fireProcess(evt);
            ps.reset(this);
        }
    }

}
