/*
 * Copyright (c) 2002-2007 JGoodies Karsten Lentzsch. All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions are met:
 * 
 *  o Redistributions of source code must retain the above copyright notice, 
 *    this list of conditions and the following disclaimer. 
 *     
 *  o Redistributions in binary form must reproduce the above copyright notice, 
 *    this list of conditions and the following disclaimer in the documentation 
 *    and/or other materials provided with the distribution. 
 *     
 *  o Neither the name of JGoodies Karsten Lentzsch nor the names of 
 *    its contributors may be used to endorse or promote products derived 
 *    from this software without specific prior written permission. 
 *     
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 */

package com.jgoodies.forms.factories;

import com.jgoodies.forms.layout.ColumnSpec;
import com.jgoodies.forms.layout.ConstantSize;
import com.jgoodies.forms.layout.FormLayout;
import com.jgoodies.forms.layout.RowSpec;
import com.jgoodies.forms.layout.Sizes;
import com.jgoodies.forms.util.LayoutStyle;

/**
 * A factory that creates instances of FormLayout for frequently used
 * form layouts. It makes form creation easier and more consistent.<p>
 * 
 * <strong>I consider removing the <code>FormLayout</code> factory methods.
 * These seem to be less usefull and may lead to poor layout readability.
 * If you want to use these methods, you may consider copying them 
 * to your codebase.</strong><p>
 *  
 * The forms are described by major and minor columns. Major columns
 * consist of a leading label and a set of related components, for example: a
 * label plus textfield, or label plus textfield plus button. The component
 * part of a major column is divided into minor columns as shown in this
 * layout:
 * <pre>
 * <-    major column 1        ->  <-     major column 2       -> 
 * label1 textfield1a textfield1b  label2 textfield2a textfield2b
 * label3 textfield3a textfield3b  label4 textfield4
 * label5 textfield5               label6 textfield6
 * </pre><p>
 * 
 * Many forms use 1, 2, 3 or 4 major columns, which in turn are often split
 * into 1, 2, 3 or 4 minor columns.
 * 
 * @author	Karsten Lentzsch
 * @version $Revision: 1.3 $
 * 
 * @see	com.jgoodies.forms.layout.FormLayout
 * @see	ColumnSpec
 */
public final class FormFactory {

    private FormFactory() {
        // Suppresses default constructor, ensuring non-instantiability.
    }
    

    // Frequently used Column Specifications ********************************

    /**
     * An unmodifyable <code>ColumnSpec</code> that determines its width by
     * computing the maximum of all column component minimum widths.
     * 
     * @see #PREF_COLSPEC
     * @see #DEFAULT_COLSPEC
     */
    public static final ColumnSpec MIN_COLSPEC = 
        new ColumnSpec(Sizes.MINIMUM);   
              
    
    /**
     * An unmodifyable <code>ColumnSpec</code> that determines its width by
     * computing the maximum of all column component preferred widths.
     * 
     * @see #MIN_COLSPEC
     * @see #DEFAULT_COLSPEC
     */
    public static final ColumnSpec PREF_COLSPEC = 
        new ColumnSpec(Sizes.PREFERRED);   
    
    
    /**
     * An unmodifyable <code>ColumnSpec</code> that determines its preferred 
     * width by computing the maximum of all column component preferred widths
     * and its minimum width by computing all column component minimum widths.<p>
     * 
     * Useful to let a column shrink from preferred width to minimum width 
     * if the container space gets scarce.
     * 
     * @see #MIN_COLSPEC
     * @see #PREF_COLSPEC
     */
    public static final ColumnSpec DEFAULT_COLSPEC = 
        new ColumnSpec(Sizes.DEFAULT);   
                    
    
    /**
     * An unmodifyable <code>ColumnSpec</code> that has an initial width
     * of 0 pixels and that grows. Useful to describe <em>glue</em> columns
     * that fill the space between other columns.
     * 
     * @see #GLUE_ROWSPEC
     */
    public static final ColumnSpec GLUE_COLSPEC = 
        new ColumnSpec(ColumnSpec.DEFAULT, Sizes.ZERO, ColumnSpec.DEFAULT_GROW);                               

    
    // Layout Style Dependent Column Specs ***********************************
    
    /**
     * Describes a logical horizontal gap between a label and an associated 
     * component. Useful for builders that automatically fill a grid with labels
     * and components.<p>
     * 
     * <strong>Note:</strong> In a future version this constant will likely 
     * be moved to a class <code>LogicalSize</code> or <code>StyledSize</code>.
     * 
     * @since 1.0.3
     */
    public static final ColumnSpec LABEL_COMPONENT_GAP_COLSPEC =
        createGapColumnSpec(LayoutStyle.getCurrent().getLabelComponentPadX());
    

    /**
     * Describes a logical horizontal gap between two related components. 
     * For example the <em>OK</em> and <em>Cancel</em> buttons are considered
     * related.<p>
     * 
     * <strong>Note:</strong> In a future version this constant will likely 
     * be moved to a class <code>LogicalSize</code> or <code>StyledSize</code>.
     * 
     * @see #UNRELATED_GAP_COLSPEC 
     */
    public static final ColumnSpec RELATED_GAP_COLSPEC =
        createGapColumnSpec(LayoutStyle.getCurrent().getRelatedComponentsPadX());
        

    /**
     * Describes a logical horizontal gap between two unrelated components.<p>
     * 
     * <strong>Note:</strong> In a future version this constant will likely 
     * be moved to a class <code>LogicalSize</code> or <code>StyledSize</code>.
     * 
     * @see #RELATED_GAP_COLSPEC 
     */
    public static final ColumnSpec UNRELATED_GAP_COLSPEC =
        createGapColumnSpec(LayoutStyle.getCurrent().getUnrelatedComponentsPadX()); 
        

    /**
     * Describes a logical horizontal column for a fixed size button. This spec 
     * honors the current layout style's default button minimum width.<p>
     * 
     * <strong>Note:</strong> In a future version this constant will likely 
     * be moved to a class <code>LogicalSize</code> or <code>StyledSize</code>.
     * 
     * @see #GROWING_BUTTON_COLSPEC 
     */
    public static final ColumnSpec BUTTON_COLSPEC = 
        new ColumnSpec(Sizes.bounded(Sizes.PREFERRED,
                                     LayoutStyle.getCurrent().getDefaultButtonWidth(),
                                     null));
        

    /**
     * Describes a logical horizontal column for a growing button. This spec 
     * does <em>not</em> use the layout style's default button minimum width.<p>
     * 
     * <strong>Note:</strong> In a future version this constant will likely 
     * be moved to a class <code>LogicalSize</code> or <code>StyledSize</code>.
     * 
     * @see #BUTTON_COLSPEC 
     */
    public static final ColumnSpec GROWING_BUTTON_COLSPEC = 
        new ColumnSpec(ColumnSpec.DEFAULT,
                       BUTTON_COLSPEC.getSize(),
                       ColumnSpec.DEFAULT_GROW);
    
    
    // Frequently used Row Specifications ***********************************
    
    /**
     * An unmodifyable <code>RowSpec</code> that determines its height by
     * computing the maximum of all column component minimum heights.
     * 
     * @see #PREF_ROWSPEC
     * @see #DEFAULT_ROWSPEC
     */
    public static final RowSpec MIN_ROWSPEC = 
        new RowSpec(Sizes.MINIMUM);  
    
                                    
    /**
     * An unmodifyable <code>RowSpec</code> that determines its height by
     * computing the maximum of all column component preferred heights.
     * 
     * @see #MIN_ROWSPEC
     * @see #DEFAULT_ROWSPEC
     */
    public static final RowSpec PREF_ROWSPEC = 
        new RowSpec(Sizes.PREFERRED);   
    
                                    
    /**
     * An unmodifyable <code>RowSpec</code> that determines its preferred 
     * height by computing the maximum of all column component preferred heights
     * and its minimum height by computing all column component minimum heights.<p>
     * 
     * Useful to let a column shrink from preferred height to minimum height 
     * if the container space gets scarce.
     * 
     * @see #MIN_COLSPEC
     * @see #PREF_COLSPEC
     */
    public static final RowSpec DEFAULT_ROWSPEC = 
        new RowSpec(Sizes.DEFAULT);  
    
                                    
    /**
     * An unmodifyable <code>RowSpec</code> that has an initial height
     * of 0 pixels and that grows. Useful to describe <em>glue</em> rows
     * that fill the space between other rows.
     * 
     * @see #GLUE_COLSPEC
     */
    public static final RowSpec GLUE_ROWSPEC = 
        new RowSpec(RowSpec.DEFAULT, Sizes.ZERO, RowSpec.DEFAULT_GROW);                               

    
    // Layout Style Dependent Row Specs *************************************

    /**
     * Describes a logical vertzical gap between two related components. 
     * For example the <em>OK</em> and <em>Cancel</em> buttons are considered
     * related.<p>
     * 
     * <strong>Note:</strong> In a future version this constant will likely 
     * be moved to a class <code>LogicalSize</code> or <code>StyledSize</code>.
     * 
     * @see #UNRELATED_GAP_ROWSPEC 
     */
    public static final RowSpec RELATED_GAP_ROWSPEC =
        createGapRowSpec(LayoutStyle.getCurrent().getRelatedComponentsPadY());
        
    
    /**
     * Describes a logical vertical gap between two unrelated components.<p>
     * 
     * <strong>Note:</strong> In a future version this constant will likely 
     * be moved to a class <code>LogicalSize</code> or <code>StyledSize</code>.
     * 
     * @see #RELATED_GAP_ROWSPEC 
     */
    public static final RowSpec UNRELATED_GAP_ROWSPEC =
        createGapRowSpec(LayoutStyle.getCurrent().getUnrelatedComponentsPadY());
        
    
    /**
     * Describes a logical vertical narrow gap between two rows in the grid.
     * Useful if the vertical space is scarce or if an individual vertical gap
     * shall be small than the default line gap.<p>
     * 
     * <strong>Note:</strong> In a future version this constant will likely 
     * be moved to a class <code>LogicalSize</code> or <code>StyledSize</code>.
     * 
     * @see #LINE_GAP_ROWSPEC 
     * @see #PARAGRAPH_GAP_ROWSPEC
     */
    public static final RowSpec NARROW_LINE_GAP_ROWSPEC =
        createGapRowSpec(LayoutStyle.getCurrent().getNarrowLinePad());

    
    /**
     * Describes the logical vertical default gap between two rows in the grid.
     * A little bit larger than the narrow line gap.<p>
     * 
     * <strong>Note:</strong> In a future version this constant will likely 
     * be moved to a class <code>LogicalSize</code> or <code>StyledSize</code>.
     * 
     * @see #NARROW_LINE_GAP_ROWSPEC 
     * @see #PARAGRAPH_GAP_ROWSPEC
     */
    public static final RowSpec LINE_GAP_ROWSPEC =
        createGapRowSpec(LayoutStyle.getCurrent().getLinePad());

    
    /**
     * Describes the logical vertical default gap between two paragraphs in 
     * the layout grid. This gap is larger than the default line gap.<p>
     * 
     * <strong>Note:</strong> In a future version this constant will likely 
     * be moved to a class <code>LogicalSize</code> or <code>StyledSize</code>.
     * 
     * @see #NARROW_LINE_GAP_ROWSPEC 
     * @see #LINE_GAP_ROWSPEC 
     */
    public static final RowSpec PARAGRAPH_GAP_ROWSPEC =
        createGapRowSpec(LayoutStyle.getCurrent().getParagraphPad());
        
    
    
    // Factory Methods ******************************************************

    /**
     * Creates and returns an instance of <code>FormLayout</code>
     * to build forms with the specified number of major and minor columns.<p>
     * 
     * The layout will use default values for all gaps.<p>
     * 
     * <strong>This method will be removed from version 1.2.</strong>
     *
     * @param majorColumns     the number of used major columns
     * @param minorColumns     the number of used minor columns
     * @param labelColumnSpec  specifies the label columns
     * @return a prepared <code>FormLayout</code>
     * 
     * @deprecated This method will be removed from the Forms 1.2
     */
    public static FormLayout createColumnLayout(
            int majorColumns,
            int minorColumns,
            ColumnSpec labelColumnSpec) {
        return createColumnLayout(
            majorColumns,
            minorColumns,
            labelColumnSpec,
            Sizes.DLUX14,
            Sizes.DLUX2);
    }

    /**
     * Creates and returns an instance of <code>FormLayout</code>
     * to build forms with the given number of major columns. 
     * Major columns consists of a label and a component section, where each 
     * component section is divided into the given number of minor columns.<p>
     * 
     * The layout will use the specified gaps to separate major columns, 
     * and the label and component section.<p>
     * 
     * <strong>This method will be removed from version 1.2.</strong>
     * 
     * @param majorColumns         the number of major columns
     * @param minorColumns         the number of minor columns
     * @param labelColumnSpec      specifies the label columns
     * @param indent               an optional <code>ConstantSize</code> 
     * that describes the width of the leading indent column
     * @param minorColumnGap       a <code>ConstantSize</code> that describes
     * the gap between minor columns
     * @return a prepared <code>FormLayout</code>
     * 
     * @deprecated This method will be removed from the Forms 1.2
     */
    public static FormLayout createColumnLayout(
            int majorColumns,
            int minorColumns,
            ColumnSpec labelColumnSpec,
            ConstantSize indent,
            ConstantSize minorColumnGap) {
        return createColumnLayout(
            majorColumns, 
            minorColumns, 
            labelColumnSpec, 
            PREF_COLSPEC, 
            indent, 
            Sizes.DLUX14, 
            minorColumnGap);
    }
                
    /**
     * Creates and returns an instance of <code>FormLayout</code>
     * to build forms with the given number of major columns. 
     * Major columns consists of a label and a component section, where each 
     * component section is divided into the given number of minor columns.<p>
     * 
     * The layout will use the specified gaps to separate major columns, 
     * minor columns, and the label and component section.<p>
     * 
     * <strong>This method will be removed from version 1.2.</strong>
     *
     * @param majorColumns         the number of major columns
     * @param minorColumns         the number of minor columns
     * @param labelColumnSpec      specifies the label columns
     * @param componentColumnSpec  specifies the label columns
     * @param indent               an optional <code>ConstantSize</code> 
     * that describes the width of the leading indent column
     * @param majorColumnGap       a <code>ConstantSize</code> that describes
     * the gap between major columns
     * @param minorColumnGap       a <code>ConstantSize</code> that describes
     * the gap between minor columns
     * @return a prepared <code>FormLayout</code>
     * 
     * @deprecated This method will be removed from the Forms 1.2
     */
    public static FormLayout createColumnLayout(
            int majorColumns,
            int minorColumns,
            ColumnSpec labelColumnSpec,
            ColumnSpec componentColumnSpec,
            ConstantSize indent,
            ConstantSize majorColumnGap,
            ConstantSize minorColumnGap) {
                
        ColumnSpec majorGapColSpec = createGapColumnSpec(majorColumnGap);
        ColumnSpec minorGapColSpec = createGapColumnSpec(minorColumnGap);
        FormLayout layout = new FormLayout(new ColumnSpec[]{}, new RowSpec[]{});

        // Add the optional leading indent.
        if (indent != null) { 
            layout.appendColumn(createGapColumnSpec(indent));
        }
        for (int i = 0; i < majorColumns; i++) {
            // Add the optional label column with gap.
            if (labelColumnSpec != null) {
                layout.appendColumn(labelColumnSpec);
                layout.appendColumn(RELATED_GAP_COLSPEC);
            }
            // Add the minor columns with separating gaps.
            for (int j = 0; j < minorColumns; j++) {
                layout.appendColumn(componentColumnSpec);
                layout.addGroupedColumn(layout.getColumnCount());
                if (j < minorColumns - 1) {
                    layout.appendColumn(minorGapColSpec);
                }
            }
            // Add a gap between major columns.
            if (i < majorColumns - 1) { 
                layout.appendColumn(majorGapColSpec); 
            }
        }
        return layout;
    }

    
    // Helper Code **********************************************************
    
    /**
     * Creates and returns a {@link ColumnSpec} that represents a gap with the
     * specified {@link ConstantSize}.
     * 
     * @param gapSize	a <code>ConstantSize</code> that specifies the gap
     * @return a <code>ColumnSpec</code> that describes a horizontal gap
     */
    public static ColumnSpec createGapColumnSpec(ConstantSize gapSize) {
        return new ColumnSpec(ColumnSpec.LEFT, gapSize, ColumnSpec.NO_GROW);
    }

    /**
     * Creates and returns a {@link RowSpec} that represents a gap with the
     * specified {@link ConstantSize}.
     * 
     * @param gapSize   a <code>ConstantSize</code> that specifies the gap
     * @return a <code>RowSpec</code> that describes a vertical gap
     */
    public static RowSpec createGapRowSpec(ConstantSize gapSize) {
        return new RowSpec(RowSpec.TOP, gapSize, RowSpec.NO_GROW);
    }
    
    
}
