/*******************************************************************************
 * Copyright (c) 2004, 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.progress.internal;

import java.util.Collections;
import java.util.List;

import org.eclipse.core.runtime.Assert;
import org.eclipse.e4.ui.progress.internal.legacy.TrimUtil;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.viewers.IBaseLabelProvider;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.FontMetrics;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.graphics.Transform;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Widget;

/**
 * The ProgressCanvasViewer is the viewer used by progress windows. It displays text
 * on the canvas.
 */
public class ProgressCanvasViewer extends AbstractProgressViewer {
	Canvas canvas;

	Object[] displayedItems = new Object[0];

	/**
	 * Font metrics to use for determining pixel sizes.
	 */
	private FontMetrics fontMetrics;

	private int numShowItems = 1;

	private int maxCharacterWidth;

	private int orientation = SWT.HORIZONTAL;

	/**
	 * Create a new instance of the receiver with the supplied
	 * parent and style bits.
	 * @param parent The composite the Canvas is created in
	 * @param style style bits for the canvas
	 * @param itemsToShow the number of items this will show
	 * @param numChars The number of characters for the width hint.
	 * @param side the side to display text, this helps determine horizontal vs vertical
	 */
	ProgressCanvasViewer(Composite parent, int style, int itemsToShow, int numChars, int orientation) {
		super();
		this.orientation = orientation;
		numShowItems = itemsToShow;
		maxCharacterWidth = numChars;
		canvas = new Canvas(parent, style);
		hookControl(canvas);
		// Compute and store a font metric
		GC gc = new GC(canvas);
		gc.setFont(JFaceResources.getDefaultFont());
		fontMetrics = gc.getFontMetrics();
		gc.dispose();
		initializeListeners();
	}

	/**
	 * NE: Copied from ContentViewer.  We don't want the OpenStrategy hooked
	 * in StructuredViewer.hookControl otherwise the canvas will take focus
	 * since it has a key listener.  We don't want this included in the window's
	 * tab traversal order.  Defeating it here is more self-contained then
	 * setting the tab list on the shell or other parent composite.
	 */
	@Override
	protected void hookControl(Control control) {
		control.addDisposeListener(this::handleDispose);
	}

	@Override
	protected Widget doFindInputItem(Object element) {
		return null; // No widgets associated with items
	}

	@Override
	protected Widget doFindItem(Object element) {
		return null; // No widgets associated with items
	}

	@Override
	protected void doUpdateItem(Widget item, Object element, boolean fullMap) {
		canvas.redraw();
	}

	@Override
	protected List<Object> getSelectionFromWidget() {
		//No selection on a Canvas
		return Collections.emptyList();
	}

	@Override
	protected void internalRefresh(Object element) {
		displayedItems = getSortedChildren(getRoot());
		canvas.redraw();
	}

	@Override
	public void reveal(Object element) {
		//Nothing to do here as we do not scroll
	}

	@Override
	protected void setSelectionToWidget(@SuppressWarnings("rawtypes") List l, boolean reveal) {
		//Do nothing as there is no selection
	}

	@Override
	public Control getControl() {
		return canvas;
	}

	private void initializeListeners() {
		canvas.addPaintListener(event -> {

			GC gc = event.gc;
			Transform transform = null;
			if (orientation == SWT.VERTICAL) {
				transform = new Transform(event.display);
				transform.translate(TrimUtil.TRIM_DEFAULT_HEIGHT, 0);
				transform.rotate(90);
			}
			ILabelProvider labelProvider = (ILabelProvider) getLabelProvider();

			int itemCount = Math.min(displayedItems.length, numShowItems);

			int yOffset = 0;
			int xOffset = 0;
			if (numShowItems == 1) {// If there is a single item try to center it
				Rectangle clientArea = canvas.getParent().getClientArea();
				if (orientation == SWT.HORIZONTAL) {
					int size1 = clientArea.height;
					yOffset = size1 - (fontMetrics.getHeight());
					yOffset = yOffset / 2;
				} else {
					int size2 = clientArea.width;
					xOffset = size2 - (fontMetrics.getHeight());
					xOffset = xOffset / 2;
				}
			}

			for (int i = 0; i < itemCount; i++) {
				String string = labelProvider.getText(displayedItems[i]);
				if (string == null) {
					string = "";//$NON-NLS-1$
				}
				if (orientation == SWT.HORIZONTAL) {
					gc.drawString(string, 2, yOffset + (i * fontMetrics.getHeight()), true);
				} else {
					gc.setTransform(transform);
					gc.drawString(string, xOffset + (i * fontMetrics.getHeight()), 2, true);
				}
			}
			if (transform != null)
				transform.dispose();
		});
	}

	@Override
	public void setLabelProvider(IBaseLabelProvider labelProvider) {
		Assert.isTrue(labelProvider instanceof ILabelProvider);
		super.setLabelProvider(labelProvider);
	}

	/**
	 * Get the size hints for the receiver. These are used for
	 * layout data.
	 * @return Point - the preferred x and y coordinates
	 */
	public Point getSizeHints() {

		Display display = canvas.getDisplay();

		GC gc = new GC(canvas);
		FontMetrics fm = gc.getFontMetrics();
		int charWidth = (int) fm.getAverageCharacterWidth();
		int charHeight = fm.getHeight();
		int maxWidth = display.getBounds().width / 2;
		int maxHeight = display.getBounds().height / 6;
		int fontWidth = charWidth * maxCharacterWidth;
		int fontHeight = charHeight * numShowItems;
		if (maxWidth < fontWidth) {
			fontWidth = maxWidth;
		}
		if (maxHeight < fontHeight) {
			fontHeight = maxHeight;
		}
		gc.dispose();
		return new Point(fontWidth, fontHeight);
	}

	@Override
	public void add(Object[] elements) {
		refresh(true);

	}

	@Override
	public void remove(Object[] elements) {
		refresh(true);

	}


}
