/*******************************************************************************
 * Copyright (c) 2008, 2014 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.e4.ui.workbench.renderers.swt;

import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Layout;

/**
 * This arranges its controls into 5 'slots' defined by its composite children
 * <ol>
 * <li>Top: spans the entire width and abuts the top of the container</li>
 * <li>Bottom: spans the entire width and abuts the bottom of the container</li>
 * <li>Left: spans the space between 'top' and 'bottom' and abuts the left of
 * the container</li>
 * <li>Right: spans the space between 'top' and 'bottom' and abuts the right of
 * the container</li>
 * <li>Center: fills the area remaining once the other controls have been
 * positioned</li>
 * </ol>
 *
 * <strong>NOTE:</strong> <i>All</i> the child controls must exist. Also,
 * computeSize is not implemented because we expect this to be used in
 * situations (i.e. shells) where the outer bounds are always 'set', not
 * computed. Also, the interior structure of the center may contain overlapping
 * controls so it may not be capable of performing the calculation.
 *
 * @author emoffatt
 *
 */
public class TrimmedPartLayout extends Layout {

	/**
	 * gutterBottom specifies the number of pixels of vertical margin that will
	 * be placed between the bottom trim component and the bottom edge of the
	 * client area. If there is no bottom trim component, the gutter serves as a
	 * margin.
	 *
	 * The default value is 0.
	 */
	public int gutterBottom = 0;

	/**
	 * gutterLeft specifies the number of pixels of horizontal margin that will
	 * be placed between the left trim component and the left edge of the client
	 * area. If there is no left trim component, the gutter serves as a margin.
	 *
	 * The default value is 0.
	 */
	public int gutterLeft = 0;

	/**
	 * gutterTop specifies the number of pixels of vertical margin that will be
	 * placed between the top trim component and the top edge of the client
	 * area. If there is no top trim component, the gutter serves as a margin.
	 *
	 * The default value is 0.
	 */
	public int gutterTop = 0;

	/**
	 * gutterRight specifies the number of pixels of horizontal margin that will
	 * be placed between the right trim component and the right edge of the
	 * client area. If there is no right trim component, the gutter serves as a
	 * margin.
	 *
	 * The default value is 0.
	 */
	public int gutterRight = 0;

	public Composite top;
	public Composite bottom;
	public Composite left;
	public Composite right;
	public Composite clientArea;

	/**
	 * This layout is used to support parts that want trim for their containing
	 * composites.
	 *
	 * @param trimOwner
	 */
	public TrimmedPartLayout(Composite parent) {
		clientArea = new Composite(parent, SWT.NONE);
		clientArea.setLayout(new FillLayout());
	}

	@Override
	protected Point computeSize(Composite composite, int wHint, int hHint,
			boolean flushCache) {
		// We can't actually compute a size so return a default
		return new Point(SWT.DEFAULT, SWT.DEFAULT);
	}

	@Override
	protected void layout(Composite composite, boolean flushCache) {
		Rectangle ca = composite.getClientArea();
		Rectangle caRect = new Rectangle(ca.x, ca.y, ca.width, ca.height);

		// 'Top' spans the entire area
		if (top != null && top.isVisible()) {
			Point topSize = top.computeSize(caRect.width, SWT.DEFAULT, true);
			caRect.y += topSize.y;
			caRect.height -= topSize.y;

			// Don't layout unless we've changed
			Rectangle newBounds = new Rectangle(ca.x, ca.y, caRect.width,
					topSize.y);
			if (!newBounds.equals(top.getBounds())) {
				top.setBounds(newBounds);
			}
		}
		// Include the gutter whether there is a top area or not.
		caRect.y += gutterTop;
		caRect.height -= gutterTop;

		// 'Bottom' spans the entire area
		if (bottom != null && bottom.isVisible()) {
			Point bottomSize = bottom.computeSize(caRect.width, SWT.DEFAULT,
					true);
			caRect.height -= bottomSize.y;

			// Don't layout unless we've changed
			Rectangle newBounds = new Rectangle(caRect.x, caRect.y
					+ caRect.height, caRect.width, bottomSize.y);
			if (!newBounds.equals(bottom.getBounds())) {
				bottom.setBounds(newBounds);
			}
		}
		caRect.height -= gutterBottom;

		// 'Left' spans between 'top' and 'bottom'
		if (left != null && left.isVisible()) {
			Point leftSize = left.computeSize(SWT.DEFAULT, caRect.height, true);
			caRect.x += leftSize.x;
			caRect.width -= leftSize.x;

			// Don't layout unless we've changed
			Rectangle newBounds = new Rectangle(caRect.x - leftSize.x,
					caRect.y, leftSize.x, caRect.height);
			if (!newBounds.equals(left.getBounds())) {
				left.setBounds(newBounds);
			}
		}
		caRect.x += gutterLeft;
		caRect.width -= gutterLeft;

		// 'Right' spans between 'top' and 'bottom'
		if (right != null && right.isVisible()) {
			Point rightSize = right.computeSize(SWT.DEFAULT, caRect.height,
					true);
			caRect.width -= rightSize.x;

			// Don't layout unless we've changed
			Rectangle newBounds = new Rectangle(caRect.x + caRect.width,
					caRect.y, rightSize.x, caRect.height);
			if (!newBounds.equals(right.getBounds())) {
				right.setBounds(newBounds);
			}
		}
		caRect.width -= gutterRight;

		// Don't layout unless we've changed
		if (!caRect.equals(clientArea.getBounds())) {
			clientArea.setBounds(caRect);
		}
	}

	/**
	 * @param top2
	 * @param b
	 * @return
	 */
	public Composite getTrimComposite(Composite parent, int side) {
		if (side == SWT.TOP) {
			if (top == null) {
				top = new Composite(parent, SWT.NONE);
				top.setLayout(new TrimBarLayout(true));
				top.addDisposeListener(e -> top = null);
			}
			return top;
		} else if (side == SWT.BOTTOM) {
			if (bottom == null) {
				bottom = new Composite(parent, SWT.NONE);
				bottom.setLayout(new TrimBarLayout(true));
				bottom.addDisposeListener(e -> bottom = null);
			}
			return bottom;
		} else if (side == SWT.LEFT) {
			if (left == null) {
				left = new Composite(parent, SWT.NONE);
				left.setLayout(new TrimBarLayout(false));
				left.addDisposeListener(e -> left = null);
			}
			return left;
		} else if (side == SWT.RIGHT) {
			if (right == null) {
				right = new Composite(parent, SWT.NONE);
				right.setLayout(new TrimBarLayout(false));
				right.addDisposeListener(e -> right = null);
			}
			return right;
		}

		// Unknown location
		return null;
	}

	public Rectangle getTrimRect(int side) {
		Rectangle caBounds = clientArea.getBounds();
		caBounds = Display.getCurrent().map(clientArea.getParent(), null,
				caBounds);

		if (side == SWT.TOP) {
			if (top != null) {
				Rectangle b = top.getBounds();
				b = top.getDisplay().map(top.getParent(), null, b);
				return b;
			}

			// Fake one
			caBounds.height = 25;
			return caBounds;
		}
		if (side == SWT.BOTTOM) {
			if (bottom != null) {
				Rectangle b = bottom.getBounds();
				b = bottom.getDisplay().map(bottom.getParent(), null, b);
				return b;
			}

			// Fake one
			caBounds.y = (caBounds.y + caBounds.height) - 25;
			caBounds.height = 25;
			return caBounds;
		}
		if (side == SWT.LEFT) {
			if (left != null && left.getChildren().length > 0) {
				Rectangle b = left.getBounds();
				b = left.getDisplay().map(left.getParent(), null, b);
				return b;
			}

			// Fake one
			caBounds.width = 25;
			return caBounds;
		}
		if (side == SWT.RIGHT) {
			if (right != null && right.getChildren().length > 0) {
				Rectangle b = right.getBounds();
				b = right.getDisplay().map(right.getParent(), null, b);
				return b;
			}

			// Fake one
			caBounds.x = (caBounds.x + caBounds.width) - 25;
			caBounds.width = 25;
			return caBounds;
		}
		return null;
	}
}
