/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.netbeans.core.multitabs;

import java.awt.*;
import java.lang.ref.WeakReference;
import javax.swing.JComponent;

/**
 * Special simple layout used in TabbedContainer. Shows component in the
 * "stack", it means that only one component is visible at any time, others are
 * always hidden "below" the visible one. Use method showComponent to select
 * visible component.
 *
 * Copied from o.n.swing.tabcontrol
 *
 * @author Dafe Simonek
 */
final class StackLayout implements LayoutManager {
    
    // #100486 - hold visibleComp weakly, because removeLayoutComponent may not
    // be called and then visibleComp is not freed. See StackLayoutTest for details.
    /**
     * Holds currently visible component or null if no comp is visible
     */
    private WeakReference<Component> visibleComp = null;

    /**
     * Set the currently displayed component.  If passed null for the component,
     * all contained components will be made invisible (sliding windows do this)
     * @param c Component to show
     * @param parent Parent container
     */
    public void showComponent(Component c, Container parent) {
        Component comp = getVisibleComponent();
        if (comp != c) {
            if (!parent.isAncestorOf(c) && c != null) {
                parent.add(c);
            }
            synchronized (parent.getTreeLock()) {
                if (comp != null) {
                    comp.setVisible(false);
                }
                visibleComp = new WeakReference<Component>(c);
                if (c != null) {
                    c.setVisible(true);
                }
		// trigger re-layout
		if (c instanceof JComponent) {
		    ((JComponent)c).revalidate();
		}
		else {
		    parent.validate(); //XXX revalidate should work!
		}
            }
        }
    }
    
    /** Allows support for content policies 
     * @return Currently visible component or null
     */
    public Component getVisibleComponent() {
        return visibleComp == null ? null : visibleComp.get();
    }

    /**
     * ********** Implementation of LayoutManager interface *********
     */

    @Override
    public void addLayoutComponent(String name, Component comp) {
        synchronized (comp.getTreeLock()) {
            comp.setVisible(false);
            // keep consistency if showComponent was already called on this
            // component before
            if (comp == getVisibleComponent()) {
                visibleComp = null;
            }
        }
    }
    
    @Override
    public void removeLayoutComponent(Component comp) {
        synchronized (comp.getTreeLock()) {
            if (comp == getVisibleComponent()) {
                visibleComp = null;
            }
            // kick out removed component as visible, so that others
            // don't have problems with hidden components
            comp.setVisible(true);
        }
    }

    @Override
    public void layoutContainer(Container parent) {
        Component visibleComp = getVisibleComponent();
        if (visibleComp != null) {
            synchronized (parent.getTreeLock()) {
                Insets insets = parent.getInsets();
                visibleComp.setBounds(insets.left, insets.top, parent.getWidth()
                   - (insets.left + insets.right), parent.getHeight()
                   - (insets.top + insets.bottom));
            }
        }
    }

    @Override
    public Dimension minimumLayoutSize(Container parent) {
        Component c = getVisibleComponent();
        return c != null ? c.getMinimumSize() : getEmptySize();
    }

    @Override
    public Dimension preferredLayoutSize(Container parent) {
        Component c = getVisibleComponent();
        return c != null ? c.getPreferredSize() : getEmptySize();
    }

    /**
     * Specifies default size of empty container
     */
    private static Dimension getEmptySize() {
        return new Dimension(50, 50);
    }

}
