/*******************************************************************************
 * Copyright (c) 2006, 2016 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.swt.examples.graphics;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.graphics.Device;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Path;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.graphics.Transform;
import org.eclipse.swt.widgets.Composite;

/**
 * This tab presents cubic and quadratic curves that can be drawn.
 * The user may reposition the cubic and quadratic handles.
 */
public class CurvesTab extends GraphicsTab {
	/** These rectangles represent the handles on the curves. */
	private Rectangle quadHndl, quadEndHndl, cubHndl1, cubHndl2, cubEndHndl;

	/** These values represent the positions of the curves. */
	private float quadXPos, quadYPos, cubXPos, cubYPos;

	/** These values represent the x and y displacement of each handle. */
	private float quadDiffX, quadDiffY, quadEndDiffX, quadEndDiffY;
	private float cubDiffX1, cubDiffY1, cubDiffX2, cubDiffY2, cubEndDiffX, cubEndDiffY;

	/** These are flags that indicate whether or not a handle has been moved. */
	private boolean quadPtMoved, quadEndPtMoved, cubPt1Moved, cubPt2Moved, cubEndPtMoved;

	private MouseMoveListener mouseMoveListener;
	private MouseListener mouseListener;
	private Cursor cursor;

	/** true if hovering over a handle, false otherwise */
	private boolean hovering = false;

	/** true if left mouse button is held down, false otherwise */
	private boolean mouseDown = false;


public CurvesTab(GraphicsExample example) {
	super(example);
	quadHndl = new Rectangle(200, 150, 5, 5);
	quadEndHndl = new Rectangle(400, 0, 5, 5);
	quadDiffX = quadDiffY = quadEndDiffX = quadEndDiffY = 0;
	cubHndl1 = new Rectangle(133, -60, 5, 5);
	cubHndl2 = new Rectangle(266, 60, 5, 5);
	cubDiffX1 = cubDiffY1 = cubDiffX2 = cubDiffY2 = 0;
	cubEndHndl = new Rectangle(400, 0, 5, 5);
	cubEndDiffX = cubEndDiffY = 0;
}

@Override
public String getCategory() {
	return GraphicsExample.getResourceString("Curves"); //$NON-NLS-1$
}

@Override
public String getText() {
	return GraphicsExample.getResourceString("Curves"); //$NON-NLS-1$
}

@Override
public String getDescription() {
	return GraphicsExample.getResourceString("CurvesDescription"); //$NON-NLS-1$
}

@Override
public boolean getDoubleBuffered() {
	return true;
}

@Override
public void dispose() {
	if (mouseListener != null)
		example.canvas.removeMouseListener(mouseListener);

	if (mouseMoveListener != null)
		example.canvas.removeMouseMoveListener(mouseMoveListener);

	cursor = null;
}

/**
 * This helper method determines whether or not the cursor is positioned
 * over a handle.
 *
 * @param e
 *            A MouseEvent
 * @return true if cursor is positioned over a handle; false otherwise
 */
private boolean isHovering(MouseEvent e) {
	Rectangle quad = new Rectangle(quadHndl.x + (int)quadXPos - 1, quadHndl.y + (int)quadYPos - 1, quadHndl.width+2, quadHndl.height+2);
	Rectangle quadEnd = new Rectangle(quadEndHndl.x + (int)quadXPos - 1, quadEndHndl.y + (int)quadYPos - 1, quadEndHndl.width+2, quadEndHndl.height+2);
	Rectangle cub1 = new Rectangle(cubHndl1.x + (int)cubXPos - 1, cubHndl1.y + (int)cubYPos - 1, cubHndl1.width+2, cubHndl1.height+2);
	Rectangle cub2 = new Rectangle(cubHndl2.x + (int)cubXPos - 1, cubHndl2.y + (int)cubYPos - 1, cubHndl2.width+2, cubHndl2.height+2);
	Rectangle cubEnd = new Rectangle(cubEndHndl.x + (int)cubXPos - 1, cubEndHndl.y + (int)cubYPos - 1, cubEndHndl.width+2, cubEndHndl.height+2);

	return ( quad.contains(e.x, e.y) || quadEnd.contains(e.x, e.y)
		 || cub1.contains(e.x, e.y) || cub2.contains(e.x, e.y)
		 || cubEnd.contains(e.x, e.y));
}
/**
 * Creates the widgets used to control the drawing.
 */
@Override
public void createControlPanel(Composite parent) {
	if (cursor == null) {
		cursor = parent.getDisplay().getSystemCursor(SWT.CURSOR_HAND);
	}

	mouseMoveListener = e -> {
		if (hovering && mouseDown) {
			example.canvas.setCursor(cursor);
		} else if (isHovering(e)) {
			example.canvas.setCursor(cursor);
			hovering = true;
		} else {
			example.canvas.setCursor(null);
			hovering = false;
		}

		if (quadPtMoved) {
			quadDiffX = quadDiffX + e.x - (int)quadXPos - quadHndl.x;
			quadDiffY = quadDiffY + e.y - (int)quadYPos - quadHndl.y;
			quadHndl.x = e.x - (int)quadXPos;
			quadHndl.y = e.y - (int)quadYPos;
		} else if (quadEndPtMoved) {
			quadEndDiffX = quadEndDiffX + e.x - (int)quadXPos - quadEndHndl.x;
			quadEndDiffY = quadEndDiffY + e.y - (int)quadYPos - quadEndHndl.y;
			quadEndHndl.x = e.x - (int)quadXPos;
			quadEndHndl.y = e.y - (int)quadYPos;
		} else if (cubPt1Moved) {
			cubDiffX1 = cubDiffX1 + e.x - (int)cubXPos - cubHndl1.x;
			cubDiffY1 = cubDiffY1 + e.y - (int)cubYPos - cubHndl1.y;
			cubHndl1.x = e.x - (int)cubXPos;
			cubHndl1.y = e.y - (int)cubYPos;
		} else if (cubPt2Moved) {
			cubDiffX2 = cubDiffX2 + e.x - (int)cubXPos - cubHndl2.x;
			cubDiffY2 = cubDiffY2 + e.y - (int)cubYPos - cubHndl2.y;
			cubHndl2.x = e.x - (int)cubXPos;
			cubHndl2.y = e.y - (int)cubYPos;
		} else if (cubEndPtMoved) {
			cubEndDiffX = cubEndDiffX + e.x - (int)cubXPos - cubEndHndl.x;
			cubEndDiffY = cubEndDiffY + e.y - (int)cubYPos - cubEndHndl.y;
			cubEndHndl.x = e.x - (int)cubXPos;
			cubEndHndl.y = e.y - (int)cubYPos;
		}
		example.redraw();
	};

	mouseListener = new MouseListener() {

		@Override
		public void mouseDoubleClick(MouseEvent e) {}

		/**
		 * Sent when a mouse button is pressed.
		 *
		 * @param e an event containing information about the mouse button press
		 */
		@Override
		public void mouseDown(MouseEvent e) {
			Rectangle quad = new Rectangle(quadHndl.x + (int)quadXPos - 1, quadHndl.y + (int)quadYPos - 1, quadHndl.width+2, quadHndl.height+2);
			Rectangle quadEnd = new Rectangle(quadEndHndl.x + (int)quadXPos - 1, quadEndHndl.y + (int)quadYPos - 1, quadEndHndl.width+2, quadEndHndl.height+2);
			Rectangle cub1 = new Rectangle(cubHndl1.x + (int)cubXPos - 1, cubHndl1.y + (int)cubYPos - 1, cubHndl1.width+2, cubHndl1.height+2);
			Rectangle cub2 = new Rectangle(cubHndl2.x + (int)cubXPos - 1, cubHndl2.y + (int)cubYPos - 1, cubHndl2.width+2, cubHndl2.height+2);
			Rectangle cubEnd = new Rectangle(cubEndHndl.x + (int)cubXPos - 1, cubEndHndl.y + (int)cubYPos - 1, cubEndHndl.width+2, cubEndHndl.height+2);

			if (quad.contains(e.x, e.y)) {
				quadPtMoved = true;
				mouseDown = true;
			} else if (quadEnd.contains(e.x, e.y)) {
				quadEndPtMoved = true;
				mouseDown = true;
			} else if (cub1.contains(e.x, e.y)) {
				cubPt1Moved = true;
				mouseDown = true;
			} else if (cub2.contains(e.x, e.y)) {
				cubPt2Moved = true;
				mouseDown = true;
			} else if (cubEnd.contains(e.x, e.y)) {
				cubEndPtMoved = true;
				mouseDown = true;
			}
		}

		/**
		 * Sent when a mouse button is released.
		 *
		 * @param e an event containing information about the mouse button release
		 */
		@Override
		public void mouseUp(MouseEvent e) {
			mouseDown = false;
			if (isHovering(e)) {
				example.canvas.setCursor(cursor);
			} else {
				example.canvas.setCursor(null);
			}

			if (quadPtMoved)
				quadPtMoved = false;
			if (quadEndPtMoved)
				quadEndPtMoved = false;
			if (cubPt1Moved)
				cubPt1Moved = false;
			if (cubPt2Moved)
				cubPt2Moved = false;
			if (cubEndPtMoved)
				cubEndPtMoved = false;

			example.redraw();
		}
	};
	example.canvas.addMouseMoveListener(mouseMoveListener);
	example.canvas.addMouseListener(mouseListener);
}

@Override
public void paint(GC gc, int width, int height) {
	if (!example.checkAdvancedGraphics()) return;
	Device device = gc.getDevice();

	Font font = new Font(device, getPlatformFont(), 16, SWT.ITALIC);
	gc.setFont(font);
	gc.setLineWidth(5);

	Transform transform;

	// ----- cubic curve -----
	cubXPos = width/5;
	cubYPos = height/3;

	transform = new Transform(device);
	transform.translate(cubXPos, cubYPos);
	gc.setTransform(transform);
	transform.dispose();

	gc.setForeground(device.getSystemColor(SWT.COLOR_RED));
	gc.drawString(GraphicsExample.getResourceString("Cubic"), 25, -70, true);

	Path path = new Path(device);
	path.cubicTo(133 + cubDiffX1, -60 + cubDiffY1, 266 + cubDiffX2, 60 + cubDiffY2, 400 + cubEndDiffX, 0 + cubEndDiffY);
	gc.drawPath(path);
	path.dispose();

	gc.setTransform(null);
	gc.setForeground(device.getSystemColor(SWT.COLOR_DARK_BLUE));
	gc.drawRectangle(cubHndl1.x + (int)cubXPos, cubHndl1.y + (int)cubYPos, cubHndl1.width, cubHndl1.height);
	gc.drawRectangle(cubHndl2.x + (int)cubXPos, cubHndl2.y + (int)cubYPos, cubHndl2.width, cubHndl2.height);
	gc.drawRectangle(cubEndHndl.x + (int)cubXPos, cubEndHndl.y + (int)cubYPos, cubEndHndl.width, cubEndHndl.height);

	// ----- quadratic curve -----
	quadXPos = width/5;
	quadYPos = 2*height/3;

	transform = new Transform(device);
	transform.translate(quadXPos, quadYPos);
	gc.setTransform(transform);
	transform.dispose();

	gc.setForeground(device.getSystemColor(SWT.COLOR_GREEN));
	gc.drawString(GraphicsExample.getResourceString("Quadratic"), 0, -50, true);

	path = new Path(device);
	path.quadTo(200 + quadDiffX, 150 + quadDiffY, 400 + quadEndDiffX, 0 + quadEndDiffY);
	gc.drawPath(path);
	path.dispose();

	gc.setTransform(null);
	gc.setForeground(device.getSystemColor(SWT.COLOR_GRAY));
	gc.drawRectangle(quadHndl.x + (int)quadXPos, quadHndl.y + (int)quadYPos, quadHndl.width, quadHndl.height);
	gc.drawRectangle(quadEndHndl.x + (int)quadXPos, quadEndHndl.y + (int)quadYPos, quadEndHndl.width, quadEndHndl.height);

	font.dispose();
}

/**
 * Returns the name of a valid font for the resident platform.
 */
static String getPlatformFont() {
	if(SWT.getPlatform() == "win32") {
		return "Arial";
	} else if (SWT.getPlatform() == "gtk") {
		return "Baekmuk Batang";
	} else {
		return "Verdana";
	}
}

}

