/*******************************************************************************
 * 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. As a
 * demonstration, cubic and quadratic curves are used to spell out "SWT".
 * The user may reposition the cubic and quadratic handles.
 */
public class CurvesSWTTab extends GraphicsTab {
	/** These rectangles represent the handles on the curves. */
	private Rectangle sRect1, sRect2, wRect1, wRect2, tTopRect1, tTopRect2,
			tBottomRect1, tBottomRect2;

	/** These values represent the positions of the curves. */
	private float sXPos, sYPos, wXPos, wYPos, topTXPos, topTYPos,
			botTXPos, botTYPos;

	/** These values represent the x and y displacement of each handle. */
	private float sDiffX1, sDiffY1, sDiffX2, sDiffY2;
	private float wDiffX1, wDiffY1, wDiffX2, wDiffY2;
	private float tTopDiffX1, tTopDiffY1, tTopDiffX2, tTopDiffY2;
	private float tBotDiffX1, tBotDiffY1, tBotDiffX2, tBotDiffY2;

	/** These are flags that indicate whether or not a handle has been moved. */
	private boolean sLeftPtMoved, sRightPtMoved, wPt1Moved, wPt2Moved,
			tTopPt1Moved, tTopPt2Moved, tBotPt1Moved, tBotPt2Moved;

	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 CurvesSWTTab(GraphicsExample example) {
	super(example);
	sRect1 = new Rectangle(-75, 50, 5, 5);
	sRect2 = new Rectangle(75, 100, 5, 5);
	sDiffX1 = sDiffY1 = 0;
	sDiffX2 = sDiffY2 = 0;
	wRect1 = new Rectangle(80, 300, 5, 5);
	wRect2 = new Rectangle(120, 300, 5, 5);
	wDiffX1 = wDiffY1 = wDiffX2 = wDiffY2 = 0;
	tTopRect1 = new Rectangle(33, -20, 5, 5);
	tTopRect2 = new Rectangle(66, 20, 5, 5);
	tTopDiffX1 = tTopDiffY1 = tTopDiffX2 = tTopDiffY2 = 0;
	tBottomRect1 = new Rectangle(-33, 50, 5, 5);
	tBottomRect2 = new Rectangle(33, 100, 5, 5);
	tBotDiffX1 = tBotDiffY1 = tBotDiffX2 = tBotDiffY2 = 0;
}

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

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

@Override
public String getDescription() {
	return GraphicsExample.getResourceString("CurvesSWTDescription"); //$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 r1 = new Rectangle(sRect1.x + (int)sXPos - 1, sRect1.y + (int)sYPos - 1, sRect1.width+2, sRect1.height+2);
	Rectangle r2 = new Rectangle(sRect2.x + (int)sXPos - 1, sRect2.y + (int)sYPos - 1, sRect2.width+2, sRect2.height+2);
	Rectangle w1 = new Rectangle(wRect1.x + (int)wXPos - 1, wRect1.y + (int)wYPos - 1, wRect1.width+2, wRect1.height+2);
	Rectangle w2 = new Rectangle(wRect2.x + (int)wXPos - 1, wRect2.y + (int)wYPos - 1, wRect2.width+2, wRect2.height+2);
	Rectangle tTop1 = new Rectangle(tTopRect1.x + (int)topTXPos - 1, tTopRect1.y + (int)topTYPos - 1, tTopRect1.width+2, tTopRect1.height+2);
	Rectangle tTop2 = new Rectangle(tTopRect2.x + (int)topTXPos - 1, tTopRect2.y + (int)topTYPos - 1, tTopRect2.width+2, tTopRect2.height+2);
	Rectangle tBot1 = new Rectangle(tBottomRect1.x + (int)botTXPos - 1, tBottomRect1.y + (int)botTYPos - 1, tBottomRect1.width+2, tBottomRect1.height+2);
	Rectangle tBot2 = new Rectangle(tBottomRect2.x + (int)botTXPos - 1, tBottomRect2.y + (int)botTYPos - 1, tBottomRect2.width+2, tBottomRect2.height+2);

	return ( r1.contains(e.x, e.y) || r2.contains(e.x, e.y)
		 || w1.contains(e.x, e.y) || w2.contains(e.x, e.y)
		 || tTop1.contains(e.x, e.y) || tTop2.contains(e.x, e.y)
		 || tBot1.contains(e.x, e.y) || tBot2.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 (sLeftPtMoved) {
			sDiffX1 = sDiffX1 + e.x - (int)sXPos - sRect1.x;
			sDiffY1 = sDiffY1 + e.y - (int)sYPos - sRect1.y;
			sRect1.x = e.x - (int)sXPos;
			sRect1.y = e.y - (int)sYPos;
		} else if (sRightPtMoved) {
			sDiffX2 = sDiffX2 + e.x - (int)sXPos - sRect2.x;
			sDiffY2 = sDiffY2 + e.y - (int)sYPos - sRect2.y;
			sRect2.x = e.x - (int)sXPos;
			sRect2.y = e.y - (int)sYPos;
		} else if (wPt1Moved) {
			wDiffX1 = wDiffX1 + e.x - (int)wXPos - wRect1.x;
			wDiffY1 = wDiffY1 + e.y - (int)wYPos - wRect1.y;
			wRect1.x = e.x - (int)wXPos;
			wRect1.y = e.y - (int)wYPos;
		} else if (wPt2Moved) {
			wDiffX2 = wDiffX2 + e.x - (int)wXPos - wRect2.x;
			wDiffY2 = wDiffY2 + e.y - (int)wYPos - wRect2.y;
			wRect2.x = e.x - (int)wXPos;
			wRect2.y = e.y - (int)wYPos;
		} else if (tTopPt1Moved) {
			tTopDiffX1 = tTopDiffX1 + e.x - (int)topTXPos - tTopRect1.x;
			tTopDiffY1 = tTopDiffY1 + e.y - (int)topTYPos - tTopRect1.y;
			tTopRect1.x = e.x - (int)topTXPos;
			tTopRect1.y = e.y - (int)topTYPos;
		} else if (tTopPt2Moved) {
			tTopDiffX2 = tTopDiffX2 + e.x - (int)topTXPos - tTopRect2.x;
			tTopDiffY2 = tTopDiffY2 + e.y - (int)topTYPos - tTopRect2.y;
			tTopRect2.x = e.x - (int)topTXPos;
			tTopRect2.y = e.y - (int)topTYPos;
		} else if (tBotPt1Moved) {
			tBotDiffX1 = tBotDiffX1 + e.x - (int)botTXPos - tBottomRect1.x;
			tBotDiffY1 = tBotDiffY1 + e.y - (int)botTYPos - tBottomRect1.y;
			tBottomRect1.x = e.x - (int)botTXPos;
			tBottomRect1.y = e.y - (int)botTYPos;
		} else if (tBotPt2Moved) {
			tBotDiffX2 = tBotDiffX2 + e.x - (int)botTXPos - tBottomRect2.x;
			tBotDiffY2 = tBotDiffY2 + e.y - (int)botTYPos - tBottomRect2.y;
			tBottomRect2.x = e.x - (int)botTXPos;
			tBottomRect2.y = e.y - (int)botTYPos;
		}
		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 r1 = new Rectangle(sRect1.x + (int)sXPos - 1, sRect1.y + (int)sYPos - 1, sRect1.width+2, sRect1.height+2);
			Rectangle r2 = new Rectangle(sRect2.x + (int)sXPos - 1, sRect2.y + (int)sYPos - 1, sRect2.width+2, sRect2.height+2);
			Rectangle w1 = new Rectangle(wRect1.x + (int)wXPos - 1, wRect1.y + (int)wYPos - 1, wRect1.width+2, wRect1.height+2);
			Rectangle w2 = new Rectangle(wRect2.x + (int)wXPos - 1, wRect2.y + (int)wYPos - 1, wRect2.width+2, wRect2.height+2);
			Rectangle tTop1 = new Rectangle(tTopRect1.x + (int)topTXPos - 1, tTopRect1.y + (int)topTYPos - 1, tTopRect1.width+2, tTopRect1.height+2);
			Rectangle tTop2 = new Rectangle(tTopRect2.x + (int)topTXPos - 1, tTopRect2.y + (int)topTYPos - 1, tTopRect2.width+2, tTopRect2.height+2);
			Rectangle tBot1 = new Rectangle(tBottomRect1.x + (int)botTXPos - 1, tBottomRect1.y + (int)botTYPos - 1, tBottomRect1.width+2, tBottomRect1.height+2);
			Rectangle tBot2 = new Rectangle(tBottomRect2.x + (int)botTXPos - 1, tBottomRect2.y + (int)botTYPos - 1, tBottomRect2.width+2, tBottomRect2.height+2);

			if (r1.contains(e.x, e.y)) {
				sLeftPtMoved = true;
				mouseDown = true;
			} else if (r2.contains(e.x, e.y)) {
				sRightPtMoved = true;
				mouseDown = true;
			} else if (w1.contains(e.x, e.y)) {
				wPt1Moved = true;
				mouseDown = true;
			} else if (w2.contains(e.x, e.y)) {
				wPt2Moved = true;
				mouseDown = true;
			} else if (tTop1.contains(e.x, e.y)) {
				tTopPt1Moved = true;
				mouseDown = true;
			} else if (tTop2.contains(e.x, e.y)) {
				tTopPt2Moved = true;
				mouseDown = true;
			} else if (tBot1.contains(e.x, e.y)) {
				tBotPt1Moved = true;
				mouseDown = true;
			} else if (tBot2.contains(e.x, e.y)) {
				tBotPt2Moved = 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 (sLeftPtMoved)
				sLeftPtMoved = false;
			if (sRightPtMoved)
				sRightPtMoved = false;
			if (wPt1Moved)
				wPt1Moved = false;
			if (wPt2Moved)
				wPt2Moved = false;
			if (tTopPt1Moved)
				tTopPt1Moved = false;
			if (tTopPt2Moved)
				tTopPt2Moved = false;
			if (tBotPt1Moved)
				tBotPt1Moved = false;
			if (tBotPt2Moved)
				tBotPt2Moved = 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(2);

	Transform transform;

	// ----- letter s -----
	sXPos = 4*width/16;
	sYPos = (height-150)/2;

	transform = new Transform(device);
	transform.translate(sXPos, sYPos);
	gc.setTransform(transform);
	transform.dispose();

	gc.setForeground(device.getSystemColor(SWT.COLOR_DARK_BLUE));
	gc.drawString(GraphicsExample.getResourceString("Cubic"), 0, 175, true);

	Path path = new Path(device);
	path.cubicTo(-200 + sDiffX1, 50 + sDiffY1, 200 + sDiffX2, 100 + sDiffY2, 0, 150);
	gc.drawPath(path);
	path.dispose();

	// draw the spline points
	gc.setTransform(null);
	gc.drawRectangle(sRect1.x + (int)sXPos, sRect1.y + (int)sYPos, sRect1.width, sRect1.height);
	gc.drawRectangle(sRect2.x + (int)sXPos, sRect2.y + (int)sYPos, sRect2.width, sRect2.height);

	// ----- letter w -----
	wXPos = 6*width/16;
	wYPos = (height-150)/2;

	transform = new Transform(device);
	transform.translate(wXPos, wYPos);
	gc.setTransform(transform);
	transform.dispose();

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

	path = new Path(device);
	path.quadTo(100 + wDiffX1, 300 + wDiffY1, 100, 0);
	path.quadTo(100+wDiffX2, 300+wDiffY2, 200, 0);
	gc.drawPath(path);
	path.dispose();

	gc.setTransform(null);
	gc.drawRectangle(wRect1.x + (int)wXPos, wRect1.y + (int)wYPos, wRect1.width, wRect1.height);
	gc.drawRectangle(wRect2.x + (int)wXPos, wRect2.y + (int)wYPos, wRect2.width, wRect2.height);


	// ----- top of letter t -----
	topTXPos = 11*width/16;
	topTYPos = (height-150)/2;

	transform = new Transform(device);
	transform.translate(topTXPos, topTYPos);
	gc.setTransform(transform);
	transform.dispose();

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

	path = new Path(device);
	path.cubicTo(33 + tTopDiffX1, -20 + tTopDiffY1, 66 + tTopDiffX2, 20 + tTopDiffY2, 100, 0);
	gc.drawPath(path);
	path.dispose();

	gc.setTransform(null);
	gc.drawRectangle(tTopRect1.x + (int)topTXPos, tTopRect1.y + (int)topTYPos, tTopRect1.width, tTopRect1.height);
	gc.drawRectangle(tTopRect2.x + (int)topTXPos, tTopRect2.y + (int)topTYPos, tTopRect2.width, tTopRect2.height);


	// ----- vertical bar of letter t -----
	botTXPos = 12*width/16;
	botTYPos = (height-150)/2;

	transform = new Transform(device);
	transform.translate(botTXPos, botTYPos);
	gc.setTransform(transform);
	transform.dispose();

	gc.setForeground(device.getSystemColor(SWT.COLOR_RED));
	gc.drawString(GraphicsExample.getResourceString("Cubic"), 0, 175, true);

	path = new Path(device);
	path.cubicTo(-33 + tBotDiffX1, 50 + tBotDiffY1, 33 + tBotDiffX2, 100 + tBotDiffY2, 0, 150);
	gc.drawPath(path);
	path.dispose();

	gc.setTransform(null);
	gc.drawRectangle(tBottomRect1.x + (int)botTXPos, tBottomRect1.y + (int)botTYPos, tBottomRect1.width, tBottomRect1.height);
	gc.drawRectangle(tBottomRect2.x + (int)botTXPos, tBottomRect2.y + (int)botTYPos, tBottomRect2.width, tBottomRect2.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";
	}
}
}
