/*
 * License (BSD):
 * ==============
 *
 * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (miglayout (at) miginfocom (dot) com)
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 * Redistributions of source code must retain the above copyright notice, this list
 * of conditions and the following disclaimer.
 * 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.
 * Neither the name of the MiG InfoCom AB 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.
 *
 * @version 1.0
 * @author Mikael Grev, MiG InfoCom AB
 *         Date: 2006-sep-08
 */

package net.miginfocom.layout;

/** This class contains static methods to be used by IDE vendors to convert to and from String/API constraints.
 * <p>
 * <b>Note that {@link LayoutUtil#setDesignTime(boolean)} should be set to <code>true</code> for this class'
 * methods to work.</b>
 */
public class IDEUtil {

    /** A direct reference to the corresponding value for predefined UnitValues in {@link UnitValue}. */
    public static final UnitValue ZERO = UnitValue.ZERO;
    /** A direct reference to the corresponding value for predefined UnitValues in {@link UnitValue}. */
    public static final UnitValue TOP = UnitValue.TOP;
    /** A direct reference to the corresponding value for predefined UnitValues in {@link UnitValue}. */
    public static final UnitValue LEADING =
            UnitValue.LEADING;
    /** A direct reference to the corresponding value for predefined UnitValues in {@link UnitValue}. */
    public static final UnitValue LEFT = UnitValue.LEFT;
    /** A direct reference to the corresponding value for predefined UnitValues in {@link UnitValue}. */
    public static final UnitValue CENTER = UnitValue.CENTER;
    /** A direct reference to the corresponding value for predefined UnitValues in {@link UnitValue}. */
    public static final UnitValue TRAILING =
            UnitValue.TRAILING;
    /** A direct reference to the corresponding value for predefined UnitValues in {@link UnitValue}. */
    public static final UnitValue RIGHT = UnitValue.RIGHT;
    /** A direct reference to the corresponding value for predefined UnitValues in {@link UnitValue}. */
    public static final UnitValue BOTTOM = UnitValue.BOTTOM;
    /** A direct reference to the corresponding value for predefined UnitValues in {@link UnitValue}. */
    public static final UnitValue LABEL = UnitValue.LABEL;
    /** A direct reference to the corresponding value for predefined UnitValues in {@link UnitValue}. */
    public static final UnitValue INF = UnitValue.INF;
    /** A direct reference to the corresponding value for predefined UnitValues in {@link UnitValue}. */
    public static final UnitValue BASELINE_IDENTITY =
            UnitValue.BASELINE_IDENTITY;
    private static final String[] X_Y_STRINGS =
            new String[]{"x", "y", "x2", "y2"};

    /** Returns the sizes of the rows and gaps for a container.	 * There will be two arrays returned [0] and [1].
     * <p>
     * The first array will be the indexes of the rows where indexes that
     * are less than 30000 or larger than 30000 is docking rows. There might be extra docking rows that aren't
     * visible but they always have size 0. Non docking indexes will probably always be 0, 1, 2, 3, etc..
     * <p>
     * The second array is the sizes of the form:<br>
     * <code>[left inset][row size 1][gap 1][row size 2][gap 2][row size n][right inset]</code>.
     * <p>
     * The returned sizes will be the ones calculated in the last layout cycle.
     * @param parentContainer The container to retuern the row sizes and gaps for. In Swing it will be a {@link java.awt.Container} and
     * in SWT it will be a {@link org.eclipse.swt.widgets.Composite}.
     * @return The sizes or <code>null</code> if {@link LayoutUtil#isDesignTime()} is <code>false</code> or
     * <code>parentContainer</code> does not have a MigLayout layout manager.
     * The returned sizes will be the ones calculated in the last layout cycle.
     * @see LayoutUtil#isDesignTime()
     */
    public static int[][] getRowSizes(Object parentContainer) {
        return Grid.getSizesAndIndexes(parentContainer, true);
    }

    /** Returns the sizes of the columns and gaps for a container.
     * There will be two arrays returned [0] and [1].
     * <p>
     * The first array will be the indexes of the columns where indexes that
     * are less than 30000 or larger than 30000 is docking columns. There might be extra docking columns that aren't
     * visible but they always have size 0. Non docking indexes will probably always be 0, 1, 2, 3, etc..
     * <p>
     * The second array is the sizes of the form:<br>
     * <code>[top inset][column size 1][gap 1][column size 2][gap 2][column size n][bottom inset]</code>.
     * <p>
     * The returned sizes will be the ones calculated in the last layout cycle.
     * @param parentContainer The container to retuern the column sizes and gaps for. In Swing it will be a {@link java.awt.Container} and
     * in SWT it will be a {@link org.eclipse.swt.widgets.Composite}.
     * @return The sizes and indexes or <code>null</code> if {@link LayoutUtil#isDesignTime()} is <code>false</code> or
     * <code>parentContainer</code> does not have a MigLayout layout manager.
     * The returned sizes will be the ones calculated in the last layout cycle.
     * @see LayoutUtil#isDesignTime()
     */
    public static int[][] getColumnSizes(Object parentContainer) {
        return Grid.getSizesAndIndexes(parentContainer, false);
    }

    /** Returns the a constraint string that can be re-parsed to be the exact same AxisConstraint.
     * @param ac The axis constraint to return as a constraint string.
     * @param asAPI If the returned string should be of API type (e.g. .flowX().gap("rel").align("right")) or
     * as a String type (e.g. "flowx, gap rel, right").
     * @param isCols The the constraint should be returned for columns rather than rows.
     * @return A String. Never <code>null</code>.
     */
    public static final String getConstraintString(AC ac, boolean asAPI,
            boolean isCols) {
        StringBuffer sb = new StringBuffer(32);

        DimConstraint[] dims = ac.getConstaints();
        BoundSize defGap =
                isCols ? PlatformDefaults.getGridGapX()
                : PlatformDefaults.getGridGapY();

        for (int i = 0; i < dims.length;
                i++) {
            DimConstraint dc = dims[i];

            addRowDimConstraintString(dc, sb, asAPI);

            if (i < dims.length - 1) {
                BoundSize gapAft = dc.getGapAfter();
                BoundSize gapBefNext = dims[i + 1].getGapBefore();

                if (gapAft != null && gapAft == gapBefNext && gapAft != defGap) {
                    if (asAPI) {
                        sb.append(".gap(\"").append(getBS(gapAft)).append("\")");
                    } else {
                        sb.append(getBS(gapAft));
                    }
                } else {
                    if (asAPI) {
                        sb.append(".gap()");
                    }
                }
            }
        }

        return sb.toString();
    }

    /** Adds the a constraint string that can be re-parsed to be the exact same DimConstraint.
     * @param dc The layout constraint to return as a constraint string.
     * @param asAPI If the returned string should be of API type (e.g. .flowX().gap("rel").align("right")) or
     * as a String type (e.g. "flowx, gap rel, right").
     */
    private static final void addRowDimConstraintString(DimConstraint dc,
            StringBuffer sb, boolean asAPI) {
        int gp = dc.getGrowPriority();

        int firstComma = sb.length();

        BoundSize size = dc.getSize();
        if (size != null) {
            if (asAPI) {
                sb.append(".size(\"").append(getBS(size)).append("\")");
            } else {
                sb.append(',').append(getBS(size));
            }
        }

        if (gp != 100) {
            if (asAPI) {
                sb.append(".growPrio(").append(gp).append("\")");
            } else {
                sb.append(",growprio ").append(gp);
            }
        }

        Float gw = dc.getGrow();
        if (gw != null) {
            String g =
                    gw.floatValue() != 100f
                    ? LayoutUtil.floatToString(gw.floatValue()) : "";
            if (asAPI) {
                if (g.length() == 0) {
                    sb.append(".grow()");
                } else {
                    sb.append(".grow(\"").append(g).append("\")");
                }
            } else {
                sb.append(",grow").append(g.length() > 0 ? (" " + g) : "");
            }
        }

        int sp = dc.getShrinkPriority();
        if (sp != 100) {
            if (asAPI) {
                sb.append(".shrinkPrio(").append(sp).append("\")");
            } else {
                sb.append(",shrinkprio ").append(sp);
            }
        }

        Float sw = dc.getShrink();
        if (sw != null && sw.intValue() != 100) {
            String s =
                    LayoutUtil.floatToString(sw.floatValue());
            if (asAPI) {
                sb.append(".shrink(\"").append(s).append("\")");
            } else {
                sb.append(",shrink ").append(s);
            }
        }

        String eg = dc.getEndGroup();
        if (eg != null) {
            if (asAPI) {
                sb.append(".endGroup(\"").append(eg).append("\")");
            } else {
                sb.append(",endgroup ").append(eg);
            }
        }

        String sg = dc.getSizeGroup();
        if (sg != null) {
            if (asAPI) {
                sb.append(".sizeGroup(\"").append(sg).append("\")");
            } else {
                sb.append(",sizegroup ").append(sg);
            }
        }

        UnitValue al = dc.getAlign();
        if (al != null) {
            if (asAPI) {
                sb.append(".align(\"").append(getUV(al)).append("\")");
            } else {
                String s = getUV(al);
                String alKw =
                        (s.equals("top") || s.equals("bottom") ||
                        s.equals("left") || s.equals("label") ||
                        s.equals("leading") || s.equals("center") ||
                        s.equals("trailing") || s.equals("right") ||
                        s.equals("baseline")) ? "" : "align ";
                sb.append(',').append(alKw).append(s);
            }
        }

        if (dc.isNoGrid()) {
            if (asAPI) {
                sb.append(".noGrid()");
            } else {
                sb.append(",nogrid");
            }
        }

        if (dc.isFill()) {
            if (asAPI) {
                sb.append(".fill()");
            } else {
                sb.append(",fill");
            }
        }

        if (asAPI == false) {
            if (sb.length() > firstComma) {
                sb.setCharAt(firstComma, '[');
                sb.append(']');
            } else {
                sb.append("[]");
            }
        }
    }

    /** Returns the a constraint string that can be re-parsed to be the exact same DimConstraint.
     * @param dc The layout constraint to return as a constraint string.
     * @param asAPI If the returned string should be of API type (e.g. .flowX().gap("rel").align("right")) or
     * as a String type (e.g. "flowx, gap rel, right").
     * @param isHor The the DimConstraint is decoration something horizontal (column or x).
     * @param noGrowAdd If <code>true</code> no grow constraints will be added.
     * @return A constraint string. Never <code>null</code>.
     */
    private static final void addComponentDimConstraintString(DimConstraint dc,
            StringBuffer sb, boolean asAPI, boolean isHor, boolean noGrowAdd) {
        int gp = dc.getGrowPriority();
        if (gp != 100) {
            if (asAPI) {
                sb.append(isHor ? ".growPrioX(" : ".growPrioY(").append(gp).
                        append(')');
            } else {
                sb.append(isHor ? ",growpriox " : ",growprioy ").append(gp);
            }
        }

        if (noGrowAdd == false) {
            Float gw = dc.getGrow();
            if (gw != null) {
                String g =
                        gw.floatValue() != 100f
                        ? LayoutUtil.floatToString(gw.floatValue()) : "";
                if (asAPI) {
                    sb.append(isHor ? ".growX(" : ".growY(").append(g).
                            append(')');
                } else {
                    sb.append(isHor ? ",growx" : ",growy").
                            append(g.length() > 0 ? (" " + g) : "");
                }
            }
        }

        int sp = dc.getShrinkPriority();
        if (sp != 100) {
            if (asAPI) {
                sb.append(isHor ? ".shrinkPrioX(" : ".shrinkPrioY(").append(sp).
                        append(')');
            } else {
                sb.append(isHor ? ",shrinkpriox " : ",shrinkprioy ").append(sp);
            }
        }

        Float sw = dc.getShrink();
        if (sw != null && sw.intValue() != 100) {
            String s =
                    LayoutUtil.floatToString(sw.floatValue());
            if (asAPI) {
                sb.append(isHor ? ".shrinkX(" : ".shrinkY(").append(s).
                        append(')');
            } else {
                sb.append(isHor ? ",shrinkx " : ",shrinky ").append(s);
            }
        }

        String eg = dc.getEndGroup();
        if (eg != null) {
            if (asAPI) {
                sb.append(isHor ? ".endGroupX(\"" : ".endGroupY(\"").append(eg).
                        append("\")");
            } else {
                sb.append(isHor ? ",endgroupx " : ",endgroupy ").append(eg);
            }
        }

        String sg = dc.getSizeGroup();
        if (sg != null) {
            if (asAPI) {
                sb.append(isHor ? ".sizeGroupX(\"" : ".sizeGroupY(\"").
                        append(sg).append("\")");
            } else {
                sb.append(isHor ? ",sizegroupx " : ",sizegroupy ").append(sg);
            }
        }

        BoundSize size = dc.getSize();
        if (size != null) {
            if (size.getPreferred() == null) {
                if (size.getMin() == null) {
                    if (asAPI) {
                        sb.append(isHor ? ".maxWidth(\"" : ".maxHeight(\"").
                                append(getBS(size)).append("\")");
                    } else {
                        sb.append(isHor ? ",wmax " : ",hmax ").
                                append(getBS(size));
                    }
                } else if (size.getMax() == null) {
                    if (asAPI) {
                        sb.append(isHor ? ".minWidth(\"" : ".minHeight(\"").
                                append(getBS(size)).append("\")");
                    } else {
                        sb.append(isHor ? ",wmin " : ",hmin ").
                                append(getBS(size));
                    }
                }
            } else {
                if (asAPI) {
                    sb.append(isHor ? ".width(\"" : ".height(\"").
                            append(getBS(size)).append("\")");
                } else {
                    sb.append(isHor ? ",width " : ",height ").
                            append(getBS(size));
                }
            }
        }

        UnitValue al = dc.getAlign();
        if (al != null) {
            if (asAPI) {
                sb.append(isHor ? ".alignX(\"" : ".alignY(\"").append(getUV(al)).
                        append("\")");
            } else {
                sb.append(isHor ? ",alignx " : ",aligny ").append(getUV(al));
            }
        }

        BoundSize gapBef = dc.getGapBefore();
        BoundSize gapAft = dc.getGapAfter();
        if (gapBef != null || gapAft != null) {
            if (asAPI) {
                sb.append(isHor ? ".gapX(\"" : ".gapY(\"").append(getBS(gapBef)).
                        append("\", \"").append(getBS(gapAft)).append("\")");
            } else {
                sb.append(isHor ? ",gapx " : ",gapy ").append(getBS(gapBef)).
                        append(' ').append(getBS(gapAft));
            }
        }
    }

    /** Returns the a constraint string that can be re-parsed to be the exact same LayoutConstraint.
     * @param cc The component constraint to return as a constraint string.
     * @param asAPI If the returned string should be of API type (e.g. .flowX().gap("rel").align("right")) or
     * as a String type (e.g. "flowx, gap rel, right").
     * @return A String. Never <code>null</code>.
     */
    public static final String getConstraintString(CC cc, boolean asAPI) {
        StringBuffer sb = new StringBuffer(16);

        if (cc.isNewline()) {
            sb.append(asAPI ? ".newline()" : ",newline");
        }
        if (cc.isExternal()) {
            sb.append(asAPI ? ".external()" : ",external");
        }
        Boolean flowX = cc.getFlowX();
        if (flowX != null) {
            if (asAPI) {
                sb.append(flowX.booleanValue() ? ".flowX()" : ".flowY()");
            } else {
                sb.append(flowX.booleanValue() ? ",flowx" : ",flowy");
            }
        }

        UnitValue[] pad = cc.getPadding();
        if (pad != null) {
            sb.append(asAPI ? ".pad(\"" : ",pad ");
            for (int i = 0; i < pad.length;
                    i++) {
                sb.append(getUV(pad[i])).append(i < pad.length - 1 ? " " : "");
            }
            if (asAPI) {
                sb.append("\")");
            }
        }

        UnitValue[] pos = cc.getPos();
        if (pos != null) {
            if (cc.isBoundsInGrid()) {
                for (int i = 0; i < 4;
                        i++) {
                    if (pos[i] != null) {
                        if (asAPI) {
                            sb.append('.').append(X_Y_STRINGS[i]).append("(\"").
                                    append(getUV(pos[i])).append("\")");
                        } else {
                            sb.append(',').append(X_Y_STRINGS[i]).
                                    append(getUV(pos[i]));
                        }
                    }
                }
            } else {
                sb.append(asAPI ? ".pos(\"" : ",pos ");
                int iSz = (pos[2] != null || pos[3] != null) ? 4 : 2; // "pos x y" vs "pos x1 y1 x2 y2".
                for (int i = 0; i < iSz;
                        i++) {
                    sb.append(getUV(pos[i])).append(i < iSz - 1 ? " " : "");
                }
                if (asAPI) {
                    sb.append("\")");
                }
            }
        }

        String id = cc.getId();
        if (id != null) {
            if (asAPI) {
                sb.append(".id(\"").append(id).append("\")");
            } else {
                sb.append(",id ").append(id);
            }
        }

        String tag = cc.getTag();
        if (tag != null) {
            if (asAPI) {
                sb.append(".tag(\"").append(tag).append("\")");
            } else {
                sb.append(",tag ").append(tag);
            }
        }

        int hideMode = cc.getHideMode();
        if (hideMode >= 0) {
            if (asAPI) {
                sb.append(".hideMode(").append(hideMode).append(')');
            } else {
                sb.append(",hideMode ").append(hideMode);
            }
        }

        int skip = cc.getSkip();
        if (skip > 0) {
            if (asAPI) {
                sb.append(".skip(").append(skip).append(')');
            } else {
                sb.append(",skip ").append(skip);
            }
        }

        int split = cc.getSplit();
        if (split > 1) {
            String s =
                    split == LayoutUtil.INF ? "" : String.valueOf(split);
            if (asAPI) {
                sb.append(".split(").append(s).append(')');
            } else {
                sb.append(",split ").append(s);
            }
        }

        int cx = cc.getCellX();
        int cy = cc.getCellY();
        int spanX = cc.getSpanX();
        int spanY = cc.getSpanY();
        if (cx >= 0 && cy >= 0) {
            if (asAPI) {
                sb.append(".cell(").append(cx).append(", ").append(cy);
                if (spanX > 1 || spanY > 1) {
                    sb.append(", ").append(spanX).append(", ").append(spanY);
                }
                sb.append(')');
            } else {
                sb.append(",cell ").append(cx).append(' ').append(cy);
                if (spanX > 1 || spanY > 1) {
                    sb.append(' ').append(spanX).append(' ').append(spanY);
                }
            }
        } else if (spanX > 1 || spanY > 1) {
            if (spanX > 1 && spanY > 1) {
                sb.append(asAPI ? ".span(" : ",span ").append(spanX).
                        append(asAPI ? ", " : " ").append(spanY);
            } else if (spanX > 1) {
                sb.append(asAPI ? ".spanX(" : ",spanx ").
                        append(spanX == LayoutUtil.INF ? ""
                        : (String.valueOf(spanX)));
            } else if (spanY > 1) {
                sb.append(asAPI ? ".spanY(" : ",spany ").
                        append(spanY == LayoutUtil.INF ? ""
                        : (String.valueOf(spanY)));
            }
            if (asAPI) {
                sb.append(')');
            }
        }

        Float pushX = cc.getPushX();
        Float pushY = cc.getPushY();
        if (pushX != null || pushY != null) {
            if (pushX != null && pushY != null) {
                sb.append(asAPI ? ".push(" : ",push ");
                if (pushX != 100.0 || pushY != 100.0) {
                    sb.append(pushX).append(asAPI ? ", " : " ").append(pushY);
                }
            } else if (pushX != null) {
                sb.append(asAPI ? ".pushX(" : ",pushx ").
                        append(pushX == 100 ? ""
                        : (String.valueOf(pushX)));
            } else if (pushY != null) {
                sb.append(asAPI ? ".pushY(" : ",pushy ").
                        append(pushY == 100 ? ""
                        : (String.valueOf(pushY)));
            }
            if (asAPI) {
                sb.append(')');
            }
        }

        int dock = cc.getDockSide();
        if (dock >= 0) {
            String ds = CC.DOCK_SIDES[dock];
            if (asAPI) {
                sb.append(".dock").
                        append(Character.toUpperCase(ds.charAt(0))).
                        append(ds.substring(1)).append("()");
            } else {
                sb.append(",").append(ds);
            }
        }

        boolean noGrowAdd =
                cc.getHorizontal().getGrow() != null &&
                cc.getHorizontal().getGrow().intValue() == 100 &&
                cc.getVertical().getGrow() != null &&
                cc.getVertical().getGrow().intValue() == 100;

        addComponentDimConstraintString(cc.getHorizontal(), sb, asAPI, true,
                noGrowAdd);
        addComponentDimConstraintString(cc.getVertical(), sb, asAPI, false,
                noGrowAdd);
        if (noGrowAdd) {
            sb.append(asAPI ? ".grow()" : ",grow"); // Combine ".growX().growY()" into ".grow()".
        }
        if (cc.isWrap()) {
            sb.append(asAPI ? ".wrap()" : ",wrap");
        }
        String s = sb.toString();
        return s.length() == 0 || s.charAt(0) != ',' ? s : s.substring(1);
    }

    /** Returns the a constraint string that can be re-parsed to be the exact same LayoutConstraint.
     * @param lc The layout constraint to return as a constraint string.
     * @param asAPI If the returned string should be of API type (e.g. .flowX().gap("rel").align("right")) or
     * as a String type (e.g. "flowx, gap rel, right").
     * @return A String. Never <code>null</code>.
     */
    public static final String getConstraintString(LC lc, boolean asAPI) {
        StringBuffer sb = new StringBuffer(16);

        if (lc.isFlowX() == false) {
            sb.append(asAPI ? ".flowY()" : ",flowy");
        }
        boolean fillX = lc.isFillX();
        boolean fillY = lc.isFillY();
        if (fillX || fillY) {
            if (fillX == fillY) {
                sb.append(asAPI ? ".fill()" : ",fill");
            } else {
                sb.append(asAPI ? (fillX ? ".fillX()" : ".fillY()")
                        : (fillX ? ",fillx" : ",filly"));
            }
        }

        Boolean leftToRight = lc.getLeftToRight();
        if (leftToRight != null) {
            if (asAPI) {
                sb.append(".leftToRight(").append(leftToRight).append(')');
            } else {
                sb.append(leftToRight.booleanValue() ? ",ltr" : ",rtl");
            }
        }

        if (lc.isTopToBottom() == false) {
            sb.append(asAPI ? ".bottomToTop()" : ",btt");
        }
        UnitValue[] insets = lc.getInsets();
        if (insets != null) {
            String cs = LayoutUtil.getCCString(insets);
            if (cs != null) {
                if (asAPI) {
                    sb.append(".insets(\"").append(cs).append("\")");
                } else {
                    sb.append(",insets ").append(cs);
                }
            } else {
                sb.append(asAPI ? ".insets(\"" : ",insets ");
                for (int i = 0; i < insets.length;
                        i++) {
                    sb.append(getUV(insets[i])).
                            append(i < insets.length - 1 ? " " : "");
                }
                if (asAPI) {
                    sb.append("\")");
                }
            }
        }

        if (lc.isNoGrid()) {
            sb.append(asAPI ? ".noGrid()" : ",nogrid");
        }
        if (lc.isVisualPadding() == false) {
            sb.append(asAPI ? ".noVisualPadding()" : ",novisualpadding");
        }
        int hideMode = lc.getHideMode();
        if (hideMode > 0) {
            if (asAPI) {
                sb.append(".hideMode(").append(hideMode).append(')');
            } else {
                sb.append(",hideMode ").append(hideMode);
            }
        }

        UnitValue alignX = lc.getAlignX();
        UnitValue alignY = lc.getAlignY();
        if (alignX != null || alignY != null) {
            if (alignX != null && alignY != null) {
                sb.append(asAPI ? ".align(\"" : ",align ").append(getUV(alignX)).
                        append(' ').append(getUV(alignY));
            } else if (alignX != null) {
                sb.append(asAPI ? ".alignX(\"" : ",alignx ").
                        append(getUV(alignX));
            } else if (alignY != null) {
                sb.append(asAPI ? ".alignY(\"" : ",aligny ").
                        append(getUV(alignY));
            }
            if (asAPI) {
                sb.append("\")");
            }
        }

        BoundSize gridGapX = lc.getGridGapX();
        BoundSize gridGapY = lc.getGridGapY();
        if (gridGapX != null || gridGapY != null) {
            if (gridGapX != null && gridGapY != null) {
                sb.append(asAPI ? ".gridGap(\"" : ",gap ").
                        append(getBS(gridGapX)).append(' ').
                        append(getBS(gridGapY));
            } else if (gridGapX != null) {
                sb.append(asAPI ? ".gridGapX(\"" : ",gapx ").
                        append(getBS(gridGapX));
            } else if (gridGapY != null) {
                sb.append(asAPI ? ".gridGapY(\"" : ",gapy ").
                        append(getBS(gridGapY));
            }
            if (asAPI) {
                sb.append("\")");
            }
        }

        int wrapAfter = lc.getWrapAfter();
        if (wrapAfter != LayoutUtil.INF) {
            String ws = wrapAfter > 0 ? String.valueOf(wrapAfter) : "";
            if (asAPI) {
                sb.append(".wrap(").append(ws).append(')');
            } else {
                sb.append(",wrap ").append(ws);
            }
        }

        int debugMillis = lc.getDebugMillis();
        if (debugMillis > 0) {
            if (asAPI) {
                sb.append(".debug(").append(debugMillis).append(')');
            } else {
                sb.append(",debug ").append(debugMillis);
            }
        }

        String s = sb.toString();
        return s.length() == 0 || s.charAt(0) != ',' ? s : s.substring(1);
    }

    private static String getUV(UnitValue uv) {
        return uv != null ? uv.getConstraintString() : "null";
    }

    private static String getBS(BoundSize bs) {
        return bs != null ? bs.getConstraintString() : "null";
    }
}