/*
 * Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 */
package com.arsdigita.bebop;

import static com.arsdigita.bebop.Component.*;
import static com.arsdigita.bebop.util.BebopConstants.*;

import com.arsdigita.xml.Element;
import com.arsdigita.util.Assert;

import com.arsdigita.bebop.util.BebopConstants;
import java.util.Iterator;

/**
 * Generates a list of segments. Each segment consists of a header (which could
 * be any Bebop component) and a body (which, likewise, could be any component).
 * The entire <code>SegmentedPanel</code> look roughly like this:
 *
 * <blockquote><pre><code>
 * ----------------------
 * Header 1
 * ----------------------
 * Body 1
 * More Body 1
 * Even more Body 1
 * ----------------------
 * Header 2
 * ----------------------
 * Body 2
 * More Body 2
 * Even more Body 2
 * </code></pre></blockquote>
 *
 * Typically, the body of each segment will be a {@link SimpleContainer} which
 * contains many other components
 * <p>
 * The XML generated by this component looks something like this:
 * <blockquote><pre><code>
 * &lt;bebop:segmentedPanel&gt;
 *   &lt;bebop:segment id="foo"&gt;
 *     &lt;bebop:segmentHeader&gt;
 *       &lt;aRandomHeaderComponent/&gt;
 *       &lt;anotherRandomHeaderComponent/&gt;
 *       ...
 *     &lt;/bebop:segmentHeader&gt;
 *     &lt;bebop:segmentBody&gt;
 *       &lt;aRandomBodyComponent&gt;
 *       &lt;anotherRandomBodyComponent&gt;
 *       ...
 *     &lt;/bebop:segmentBody&gt;
 *   &lt;/bebop:segment&gt;
 *   ...
 * &lt;/bebop:segmentedPanel&gt;
 * </code></pre></blockquote>
 *
 * @see #generateXML(PageState, Element)
 *
 * @author Michael Pih
 * @version $Id$
 */
public class SegmentedPanel extends SimpleContainer
        implements BebopConstants {

    public static final String HEADER_CLASS = "seg-header";

    /**
     * Construct a new <code>SegmentedPanel</code>
     */
    public SegmentedPanel() {
        super();
    }

    /**
     * Construct a new <code>SegmentedPanel</code>
     *
     * @param idAttr the XSL ID attribute for this container
     * @see SimpleComponent#setIdAttr(String)
     */
    public SegmentedPanel(String idAttr) {
        this();
        setIdAttr(idAttr);
    }

    /**
     * Add a segment to this container
     *
     * @param header the component that will act as the header
     * @param body the component that will act as the body
     * @return the new segment
     */
    public Segment addSegment(Component header, Component body) {
        Segment s = new Segment(header, body);
        add(s);
        return s;
    }

    /**
     * Add a segment to this container.
     *
     * @param segmentID the XSL ID attribute for the new segment.
     * @param header the component that will act as the header.
     * @param body the component that will act as the body
     * @return the new segment
     */
    public Segment addSegment(String segmentID, Component header, Component body) {
        Segment s = addSegment(header, body);
        s.setIdAttr(segmentID);
        return s;
    }

    /**
     * Add a segment to this container.
     *
     * @param segmentID the XSL ID attribute for the new segment. The XSL
     * template for this component will render the correct header based on the
     * ID attribute
     * @param body the component that will act as the body
     * @return the new segment
     */
    public Segment addSegment(String segmentID, Component body) {
        return addSegment(segmentID, null, body);
    }

    /**
     * Add and return a new empty segment.
     *
     * @return a new empty segment that is part of the panel.
     */
    public Segment addSegment() {
        Segment result = new Segment();
        add(result);
        return result;
    }

    /**
     * Generate the XML for this component, as described above
     *
     * @param state represents the page state for the current request
     * @param parent the parent XML element
     */
    @Override
    public void generateXML(PageState state, Element parent) {
        if (isVisible(state)) {
            Element panel = parent.
                    newChildElement(BEBOP_SEG_PANEL, BEBOP_XML_NS);
            exportAttributes(panel);
            super.generateXML(state, panel);
        }
    }

    /**
     * A single Segment within this container
     */
    public static class Segment extends SimpleContainer {

        private SimpleContainer m_header;
        private SimpleContainer m_body;

        /**
         * Construct an empty <code>Segment</code>
         */
        public Segment() {
            this(null, null);
        }

        /**
         * Construct a new <code>Segment</code>
         *
         * @param header the component which will act as the header; the XSL
         * class attribute for the component will be set to
         * {@link #HEADER_CLASS}. Typically, this component will be a
         * {@link Label}
         * @param body the component which represents the body of the segment,
         * Typically, this component will be a {@link SimpleContainer} or a
         * panel of some sort
         */
        public Segment(Component header, Component body) {
            super();
            if (header != null) {
                addHeader(header);
            }
            if (body != null) {
                add(body);
            }
        }

        /**
         * Construct a new <code>Segment</code> with no header
         *
         * @param body the component which represents the body of the segment,
         * Typically, this component will be a {@link SimpleContainer} or a
         * panel of some sort
         */
        public Segment(Component body) {
            this(null, body);
        }

        /**
         * Add a header component.
         *
         * @param c an additional header component
         */
        public void addHeader(Component c) {
            Assert.isUnlocked(this);
            if (m_header == null) {
                m_header = new SimpleContainer(BEBOP_SEG_HEADER, BEBOP_XML_NS);
                super.add(m_header);
            }
            m_header.add(c);
        }

        /**
         * Add a component to the body of this segment
         *
         * @param c
         */
        @Override
        public void add(Component c) {
            Assert.isUnlocked(this);
            if (m_body == null) {
                m_body = new SimpleContainer(BEBOP_SEG_BODY, BEBOP_XML_NS);
                super.add(m_body);
            }
            m_body.add(c);
        }

        /**
         * Add a component to the body of this segment
         *
         * @param c
         */
        @Override
        public void add(Component c, int constraints) {
            add(c);
        }

        @Override
        public boolean isVisible(final PageState state) {
            boolean result = super.isVisible(state);

            if (m_header != null) {
                result = result && m_header.isVisible(state);

                final Iterator children = m_header.children();
                while (children.hasNext()) {
                    final Component component = (Component) children.next();
                    result = result && component.isVisible(state);
                }
            }

            if (m_body != null) {
                result = result && m_body.isVisible(state);

                final Iterator children = m_body.children();
                while (children.hasNext()) {
                    final Component component = (Component) children.next();
                    result = result && component.isVisible(state);
                }
            }

            return result;
        }

        /**
         * Generate the XML for this segment
         *
         * @param state the current page state
         * @param parent the parent XML element
         */
        @Override
        public void generateXML(PageState state, Element parent) {
            if (isVisible(state)) {
                Element seg = parent.
                        newChildElement(BEBOP_SEGMENT, BEBOP_XML_NS);
                exportAttributes(seg);
                super.generateXML(state, seg);
            }
        }
    }

}
