/*******************************************************************************
 * Copyright (c) 2013, 2018 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.addons.dndaddon;

import java.util.ArrayList;
import java.util.List;
import org.eclipse.e4.ui.internal.workbench.swt.AbstractPartRenderer;
import org.eclipse.e4.ui.model.application.ui.basic.MWindow;
import org.eclipse.e4.ui.services.IStylingEngine;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.graphics.Region;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

public class SplitFeedbackOverlay {
	final Display display = Display.getCurrent();

	private Shell feedbackShell;
	private int curSide = 0;
	private float ratio;

	private List<Rectangle> rects = new ArrayList<>();
	private Rectangle outerRect;

	Boolean isModified = null;
	private IStylingEngine stylingEngine;

	public SplitFeedbackOverlay(Shell dragShell, Rectangle rect, int side, float pct,
			boolean enclosed, boolean modified) {
		outerRect = rect;
		curSide = side;
		ratio = pct;

		feedbackShell = new Shell(dragShell, SWT.NO_TRIM | SWT.ON_TOP);
		feedbackShell.setBounds(dragShell.getBounds());
		feedbackShell.setData(DragAndDropUtil.IGNORE_AS_DROP_TARGET, DragAndDropUtil.IGNORE_AS_DROP_TARGET);

		MWindow winModel = (MWindow) dragShell.getData(AbstractPartRenderer.OWNING_ME);
		stylingEngine = winModel.getContext().get(IStylingEngine.class);

		// Show the appropriate feedback rectangles
		setFeedback(enclosed, modified);

		defineRegion();
	}

	public void dispose() {
		if (feedbackShell != null && !feedbackShell.isDisposed()) {
			Region region = feedbackShell.getRegion();
			if (region != null && !region.isDisposed()) {
				region.dispose();
			}
			feedbackShell.dispose();
		}
		feedbackShell = null;
	}

	private void showRects(boolean enclosed) {
		if (curSide == 0) {
			return;
		}

		Rectangle ca = new Rectangle(outerRect.x, outerRect.y, outerRect.width, outerRect.height);
		rects.clear();

		if (enclosed) {
			addRect(ca);
			ca.x += 4;
			ca.y += 4;
			ca.width -= 8;
			ca.height -= 8;
		}

		int pctWidth = (int) (ca.width * ratio);
		int pctHeight = (int) (ca.height * ratio);

		Rectangle r1 = null, r2 = null;
		if (curSide == SWT.LEFT) {
			r1 = new Rectangle(ca.x, ca.y, pctWidth, ca.height);
			r2 = new Rectangle(ca.x + r1.width + 2, ca.y, ca.width - (pctWidth + 2), ca.height);
		} else if (curSide == SWT.RIGHT) {
			r1 = new Rectangle(ca.x, ca.y, ca.width - pctWidth, ca.height);
			r2 = new Rectangle(ca.x + r1.width + 2, ca.y, pctWidth - 2, ca.height);
		} else if (curSide == SWT.TOP) {
			r1 = new Rectangle(ca.x, ca.y, ca.width, pctHeight);
			r2 = new Rectangle(ca.x, ca.y + pctHeight + 2, ca.width, ca.height - (pctHeight + 2));
		} else if (curSide == SWT.BOTTOM) {
			r1 = new Rectangle(ca.x, ca.y, ca.width, ca.height - pctHeight);
			r2 = new Rectangle(ca.x, ca.y + r1.height + 2, ca.width, pctHeight - 2);
		}

		addRect(r1);
		addRect(r2);
	}

	private void defineRegion() {
		Region rgn = new Region();
		for (Rectangle r : rects) {
			rgn.add(r);
			rgn.subtract(r.x + 2, r.y + 2, r.width - 4, r.height - 4);
		}

		// Workaround: Some window managers draw a drop shadow even if the shell
		// is set to NO_TRIM. By making the shell contain a component in the
		// bottom-right of its parent shell, SWT won't resize it and any extra
		// shadows will end up being drawn on top of the shadows for the parent
		// shell rather than in the middle of the workbench window.
		Composite parent = feedbackShell.getParent();
		if (parent instanceof Shell) {
			Shell parentShell = (Shell) parent;

			Rectangle bounds = parentShell.getBounds();
			rgn.add(bounds.width - 1, bounds.height - 1, 1, 1);
		}

		if (feedbackShell.getRegion() != null && !feedbackShell.getRegion().isDisposed()) {
			feedbackShell.getRegion().dispose();
		}
		feedbackShell.setRegion(rgn);

		feedbackShell.redraw();
		display.update();
	}

	private void addRect(Rectangle rect) {
		// Map the rect to the feedback shell
		rect = display.map(null, feedbackShell, rect);
		rects.add(rect);
	}

	public void setFeedback(boolean enclosed, boolean modified) {
		if (isModified == null) {
			isModified = !modified;
		}

		// Update the feedback color if the drag is 'modified'
		if (modified != isModified) {
			if (!modified) {
				stylingEngine.setClassname(feedbackShell, "DragFeedback"); //$NON-NLS-1$
			} else {
				stylingEngine.setClassname(feedbackShell, "ModifiedDragFeedback"); //$NON-NLS-1$
			}
			stylingEngine.style(feedbackShell);
			isModified = modified;
		}

		showRects(enclosed);
		defineRegion();
		feedbackShell.update();
	}

	/**
	 * Control this instance's visibility.
	 *
	 * @param visible
	 *            make visible if {@code true} or invisible if {@code false}
	 * @since 0.11
	 */
	public void setVisible(boolean visible) {
		if (feedbackShell != null) {
			feedbackShell.setVisible(visible);
		}
	}
}
