/*******************************************************************************
 * Copyright (c) 2005, 2015 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.ui.internal.dnd;

import org.eclipse.jface.util.Geometry;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.ui.themes.ColorUtil;

/**
 * Utility class that wraps a given control with a black 'border'. Moving the
 * border control will cause the given control to move to stay within its bounds.
 *
 * @since 3.2
 *
 */
public class DragBorder {
	// Controls
	private Composite clientControl = null;
	private Control dragControl = null;
	private Canvas border = null;

	// Colors
	private Color baseColor;
	private Color hilightColor;
	private boolean isHighlight;

	/**
	 * Construct a new DragBorder.
	 *
	 * @param client The client window that the border must stay within
	 * @param toDrag The control to be placed 'inside' the border
	 */
	public DragBorder(Composite client, Control toDrag, boolean provideFrame) {
		clientControl = client;
		dragControl = toDrag;
		Point dragSize = toDrag.getSize();

		// Create a control large enough to 'contain' the dragged control
		border = new Canvas(dragControl.getParent(), SWT.NONE);
		border.setSize(dragSize.x+2, dragSize.y+2);

		// Use the SWT 'title' colors since they should always have a proper contrast
		// and are 'related' (i.e. should look good together)
		baseColor = border.getDisplay().getSystemColor(SWT.COLOR_LIST_SELECTION);
		RGB background  = border.getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND).getRGB();
		RGB blended = ColorUtil.blend(baseColor.getRGB(), background);
		hilightColor = new Color(border.getDisplay(), blended);

		// Ensure the border is visible and the control is 'above' it...
		border.moveAbove(null);
		dragControl.moveAbove(null);

		if (provideFrame) {
			border.addPaintListener(e -> {
				if (isHighlight) {
					e.gc.setForeground(hilightColor);
				}
				else {
					e.gc.setForeground(baseColor);
				}

				// Draw a rectangle as our 'border'
				Rectangle bb = border.getBounds();
				e.gc.drawRectangle(0,0,bb.width-1, bb.height-1);
			});
		}
	}


    /**
     * Move the border (and its 'contained' control to a new position. The new
     * position will be adjusted to lie entirely within the client area of the
     * <code>clientControl</code>.
     *
     * @param newPos The new position for the border
     * @param alignment The location of the cursor relative to the border being dragged.
     * Current implementation only recognizes SWT.TOP & SWT.BOTTOM (which implies SWT.LEFT)
     * and SWT.CENTER (which centers teh dragged border on the cursor.
     */
    public void setLocation(Point newPos, int alignment) {
		// Move the border but ensure that it is still inside the Client area
    	if (alignment == SWT.CENTER) {
    		Point size = border.getSize();
    		border.setLocation(newPos.x - (size.x/2), newPos.y - (size.y/2));
    	}
    	else if (alignment == SWT.TOP) {
    		border.setLocation(newPos.x, newPos.y);
    	} else {
			border.setLocation(newPos.x, newPos.y - border.getSize().y);
		}

    	// Force the control to remain inside the shell
		Rectangle bb = border.getBounds();
		Rectangle cr = clientControl.getClientArea();
		Geometry.moveInside(bb,cr);

		// Ensure that the controls are the 'topmost' controls
		border.moveAbove(null);
		dragControl.moveAbove(null);

		// OK, now move the drag control and the border to their new locations
		dragControl.setLocation(bb.x+1, bb.y+1);
		border.setBounds(bb);
    }

	/**
	 * Sets the hilight 'mode' for the control.
	 * @param highlight true if the border should be drawn as 'hilighted'
	 */
	public void setHighlight(boolean highlight) {
		isHighlight = highlight;
		border.redraw();
	}

	/**
	 * Dispose the controls owned by the border.
	 */
	public void dispose() {
		hilightColor.dispose();
		border.dispose();
	}


	/**
	 * @return The bounds of the border's control.
	 */
	public Rectangle getBounds() {
		return border.getBounds();
	}
}
