/*
 * ProportionalLayout.java 18 sept 08
 *
 * Sweet Home 3D, Copyright (c) 2024 Space Mushrooms <info@sweethome3d.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
package com.eteks.sweethome3d.swing;

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

/**
 * A layout manager that displays two components at the top of each other. 
 * The component at top is sized at container width and at its preferred height.
 * The component at bottom is centered in the rest of the space and sized proportionally 
 * to its preferred size.
 * @author Emmanuel Puybaret
 */
public class ProportionalLayout implements LayoutManager2 {
  /**
   * The two locations where components managed by a {@link ProportionalLayout} instance can be placed.
   */
  public enum Constraints {TOP, BOTTOM}

  private Component topComponent;
  private Component bottomComponent;
  private int       gap;

  /**
   * Creates a layout manager which layouts its component 
   * with a default gap of 5 pixels between them.
   */
  public ProportionalLayout() {
    this(5);
  }

  /**
   * Creates a layout manager which layouts its component 
   * with a given <code>gap</code> between them.
   */
  public ProportionalLayout(int gap) {
    this.gap = gap;
  }
  
  /**
   * Records a given <code>component</code> in this layout manager as the component at 
   * <code>Constraints.TOP</code> or at <code>Constraints.BOTTOM</code> of its container.
   */
  public void addLayoutComponent(Component component, Object constraints) {
    if (constraints == Constraints.TOP) {
      this.topComponent = component; 
    } else if (constraints == Constraints.BOTTOM) {
      this.bottomComponent = component;
    }
  }

  /**
   * Do not use.
   */
  public void addLayoutComponent(String name, Component comp) {
    throw new IllegalArgumentException("Use addLayoutComponent with a Constraints object");
  }

  /**
   * Removes the given <code>component</code> from the ones managed by this layout manager.
   */
  public void removeLayoutComponent(Component component) {
  }
  
  /**
   * Returns 0.5.
   */
  public float getLayoutAlignmentX(Container target) {
    return 0.5f;
  }

  /**
   * Return 0.
   */
  public float getLayoutAlignmentY(Container target) {
    return 0f;
  }

  /**
   * Invalidates layout.
   */
  public void invalidateLayout(Container target) {
    // Sizes are computed on the fly each time
  }

  /**
   * Layouts the container.
   */
  public void layoutContainer(Container parent) {
    Insets parentInsets = parent.getInsets();
    int parentAvailableWidth = parent.getWidth() - parentInsets.left - parentInsets.right;
    int parentAvailableHeight = parent.getHeight() - parentInsets.top - parentInsets.bottom;
    
    // Component at top is sized at container width and at its preferred height
    boolean topComponentUsed = this.topComponent != null && this.topComponent.getParent() != null;
    if (topComponentUsed) {
      this.topComponent.setBounds(parentInsets.left, parentInsets.top, 
          parentAvailableWidth, 
          Math.min(this.topComponent.getPreferredSize().height, parentAvailableHeight));
    }
    // Component is centered in the rest of the space and sized proportionally to its preferred size
    if (this.bottomComponent != null && this.bottomComponent.getParent() != null) {
      Dimension bottomComponentPreferredSize = this.bottomComponent.getPreferredSize();
      int bottomComponentHeight = parentAvailableHeight;
      int bottomComponentY = parentInsets.top;
      if (topComponentUsed) {
        int occupiedHeight = this.topComponent.getHeight() + this.gap;
        bottomComponentHeight -= occupiedHeight;
        bottomComponentY += occupiedHeight;
      }
      int bottomComponentWidth = bottomComponentHeight * bottomComponentPreferredSize.width 
                                 / bottomComponentPreferredSize.height;
      int bottomComponentX = parentInsets.left;
      // Adjust component width and height if it's larger than parent
      if (bottomComponentWidth > parentAvailableWidth) {
        bottomComponentWidth = parentAvailableWidth;
        int previousHeight = bottomComponentHeight;
        bottomComponentHeight = bottomComponentWidth * bottomComponentPreferredSize.height 
                                / bottomComponentPreferredSize.width;
        bottomComponentY += (previousHeight - bottomComponentHeight)  / 2;
      } else {
        // Center component in width
        bottomComponentX += (parentAvailableWidth - bottomComponentWidth)  / 2; 
      }
        
      this.bottomComponent.setBounds(bottomComponentX, bottomComponentY, 
          bottomComponentWidth, bottomComponentHeight);
    }
  }

  /**
   * Returns the largest minimum width of the components managed by this layout manager,
   * and the sum of their minimum heights.
   */
  public Dimension minimumLayoutSize(Container parent) {
    Insets parentInsets = parent.getInsets();
    int minWidth = 0;
    int minHeight = 0;
    boolean topComponentUsed = this.topComponent != null && this.topComponent.getParent() != null;
    if (topComponentUsed) {
      Dimension topComponentMinSize = this.topComponent.getMinimumSize();
      minWidth = Math.max(minWidth, topComponentMinSize.width);
      minHeight = topComponentMinSize.height;
    }
    if (this.bottomComponent != null && this.bottomComponent.getParent() != null) {
      Dimension bottomComponentMinSize = this.bottomComponent.getMinimumSize();
      minWidth = Math.max(minWidth, bottomComponentMinSize.width);
      minHeight += bottomComponentMinSize.height;
      if (topComponentUsed) {
        minHeight += this.gap;
      }
    }
    
    return new Dimension(minWidth + parentInsets.left + parentInsets.right, 
        minHeight + parentInsets.top + parentInsets.bottom);
  }

  /**
   * Returns the largest maximum width of the components managed by this layout manager,
   * and the sum of their maximum heights.
   */
  public Dimension maximumLayoutSize(Container parent) {
    Insets parentInsets = parent.getInsets();
    int maxWidth = 0;
    int maxHeight = 0;
    boolean topComponentUsed = this.topComponent != null && this.topComponent.getParent() != null;
    if (topComponentUsed) {
      Dimension topComponentMaxSize = this.topComponent.getMaximumSize();
      maxWidth = Math.max(maxWidth, topComponentMaxSize.width);
      maxHeight = topComponentMaxSize.height;
    }
    if (this.bottomComponent != null && this.bottomComponent.getParent() != null) {
      Dimension bottomComponentMaxSize = this.bottomComponent.getMaximumSize();
      maxWidth = Math.max(maxWidth, bottomComponentMaxSize.width);
      maxHeight += bottomComponentMaxSize.height;
      if (topComponentUsed) {
        maxHeight += this.gap;
      }
    }
    
    return new Dimension(maxWidth + parentInsets.left + parentInsets.right, 
        maxHeight + parentInsets.top + parentInsets.bottom);
  }

  /**
   * Returns the largest preferred width of the components managed by this layout manager,
   * and the sum of their preferred heights.
   */
  public Dimension preferredLayoutSize(Container parent) {
    Insets parentInsets = parent.getInsets();
    int preferredWidth = 0;
    int preferredHeight = 0;
    boolean topComponentUsed = this.topComponent != null && this.topComponent.getParent() != null;
    if (topComponentUsed) {
      Dimension topComponentPreferredSize = this.topComponent.getPreferredSize();
      preferredWidth = Math.max(preferredWidth, topComponentPreferredSize.width);
      preferredHeight = topComponentPreferredSize.height;
    }
    if (this.bottomComponent != null && this.bottomComponent.getParent() != null) {
      Dimension bottomComponentPreferredSize = this.bottomComponent.getPreferredSize();
      preferredWidth = Math.max(preferredWidth, bottomComponentPreferredSize.width);
      preferredHeight += bottomComponentPreferredSize.height;
      if (topComponentUsed) {
        preferredHeight += this.gap;
      }
    }
    
    return new Dimension(preferredWidth + parentInsets.left + parentInsets.right, 
        preferredHeight + parentInsets.top + parentInsets.bottom);
  }
}