package de.duehl.swing.ui.layout;

/*
 * Copyright 2016 Christian Dühl. All rights reserved.
 *
 * This program is free software. You can redistribute it and/or
 * modify it under the same terms as perl:
 *
 * general:  http://dev.perl.org/licenses/
 * GPL:      http://dev.perl.org/licenses/gpl1.html
 * artistic: http://dev.perl.org/licenses/artistic.html
 */

import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Insets;

/**
 * An adapted FlowLayout. It prevents elements from being hided at the right
 * end of the containing component.
 *
 * @author http://www.java-blog-buch.de/dflowlayout-mit-zeilenumbruch/
 *         Comments and changed names of variables by Christian Dühl.
 *         Blog from 20.12.2008. Changes from 04.09.2013.
 */

public class ExtendedFlowLayout extends FlowLayout {

    private static final long serialVersionUID = -4842928220870781073L;

    /**
     * Constructs a new extended flow layout manager with a centered alignment
     * and a default 5-unit horizontal and vertical gap.
     */
    public ExtendedFlowLayout() {
        super();
    }

    /**
     * Creates a new extended flow layout manager with the indicated alignment
     * and the indicated horizontal and vertical gaps.                           <br /><br />
     *
     * The value of the alignment argument must be one of
     * ExtendedFlowLayout.LEFT, ExtendedFlowLayout.RIGHT,
     * ExtendedFlowLayout.CENTER, ExtendedFlowLayout.LEADING, or
     * ExtendedFlowLayout.TRAILING.
     *
     * @param align
     *            the alignment value
     * @param hgap
     *            the horizontal gap between components and between the
     *            components and the borders of the Container
     * @param vgap
     *            the vertical gap between components and between the components
     *            and the borders of the Container
     */
    public ExtendedFlowLayout(int align, int hgap, int vgap) {
        super(align, hgap, vgap);
    }

    /**
     * Constructs a new extended flow layout manager with the specified
     * alignment and a default 5-unit horizontal and vertical gap. The value of
     * the alignment argument must be one of ExtendedFlowLayout.LEFT,
     * ExtendedFlowLayout.RIGHT, ExtendedFlowLayout.CENTER,
     * ExtendedFlowLayout.LEADING, or ExtendedFlowLayout.TRAILING.
     *
     * @param align
     *            the alignment value
     */
    public ExtendedFlowLayout(int align) {
        super(align);
    }

    /**
     * Returns the preferred dimensions for this layout given the <i>visible</i>
     * components in the specified target container.
     *
     * @param target
     *            the container that needs to be laid out
     * @return the preferred dimensions to lay out the subcomponents of the
     *         specified container
     * @see Container
     * @see #minimumLayoutSize
     * @see java.awt.Container#getPreferredSize
     */
    @Override
    public Dimension preferredLayoutSize(Container target) {

        synchronized (target.getTreeLock()) {
            int width = 0;
            int height = 0;
            Dimension dim = new Dimension(0, 0);
            Insets insets = target.getInsets();
            int nmembers = target.getComponentCount();
            boolean firstVisibleComponent = true;
            for (int i = 0; i < nmembers; i++) {
                Component m = target.getComponent(i);
                if (m.isVisible()) {
                    Dimension d = m.getPreferredSize();
                    if (firstVisibleComponent) {
                        firstVisibleComponent = false;
                        width = d.width;
                        height = d.height;
                    }
                    else {
                        if (width + d.width > target.getWidth() - insets.left
                                - insets.right - getHgap() * 2) {
                            dim.height += height + getVgap();
                            dim.width = Math.max(dim.width, width);
                            width = d.width;
                            height = d.height;
                        }
                        else {
                            width += d.width + getHgap();
                            height = Math.max(d.height, height);
                        }
                    }
                }
            }
            dim.height += height + getVgap() * 2 + insets.top + insets.bottom;
            dim.width = Math.max(dim.width, width) + getHgap() * 2
                    + insets.left + insets.right;
            return dim;
        }
    }

    /**
     * Returns the minimum dimensions needed to layout the <i>visible</i>
     * components contained in the specified target container.
     *
     * @param target
     *            the container that needs to be laid out
     * @return the minimum dimensions to lay out the subcomponents of the
     *         specified container
     * @see #preferredLayoutSize
     * @see java.awt.Container
     * @see java.awt.Container#doLayout
     */
    @Override
    public Dimension minimumLayoutSize(Container target) {

        synchronized (target.getTreeLock()) {
            int width = 0;
            int height = 0;
            Dimension dim = new Dimension(0, 0);
            Insets insets = target.getInsets();
            int nmembers = target.getComponentCount();
            boolean firstVisibleComponent = true;
            for (int i = 0; i < nmembers; i++) {
                Component m = target.getComponent(i);
                if (m.isVisible()) {
                    Dimension d = m.getMinimumSize();
                    if (firstVisibleComponent) {
                        firstVisibleComponent = false;
                        width = d.width;
                        height = d.height;
                    }
                    else {
                        if (width + d.width > target.getWidth() - insets.left
                                - insets.right - getHgap() * 2) {
                            dim.height += height + getVgap();
                            dim.width = Math.max(dim.width, width);
                            width = d.width;
                            height = d.height;
                        }
                        else {
                            width += d.width + getHgap();
                            height = Math.max(d.height, height);
                        }
                    }
                }
            }
            dim.height += height + getVgap() * 2 + insets.top + insets.bottom;
            dim.width = Math.max(dim.width, width) + getHgap() * 2
                    + insets.left + insets.right;
            return dim;
        }
    }

}