package cn.edu.xmu.tidems.control.ui.vaccumpump;

/*******************************************************************************
 * Copyright (c) 2010 Oak Ridge National Laboratory.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 ******************************************************************************/
import org.eclipse.draw2d.AbstractLayout;
import org.eclipse.draw2d.Ellipse;
import org.eclipse.draw2d.FigureListener;
import org.eclipse.draw2d.Graphics;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.Label;
import org.eclipse.draw2d.Polygon;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Insets;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.PointList;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.nebula.visualization.widgets.figureparts.RoundScale;
import org.eclipse.nebula.visualization.widgets.figureparts.RoundScaleTickMarks;
import org.eclipse.nebula.visualization.widgets.figureparts.RoundScaledRamp;
import org.eclipse.nebula.visualization.widgets.figures.AbstractRoundRampedFigure;
import org.eclipse.nebula.visualization.widgets.util.GraphicsUtil;
import org.eclipse.nebula.visualization.widgets.util.PointsUtil;
import org.eclipse.nebula.visualization.xygraph.linearscale.AbstractScale.LabelSide;
import org.eclipse.nebula.visualization.xygraph.util.XYGraphMediaFactory;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.Pattern;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.Display;

/**
 * The figure of gauge
 *
 * @author Xihui Chen
 *
 *         Add title & unit labels
 * @author Trig Chen, Oct., 2016.
 *
 */
public class GaugeFigureExt extends AbstractRoundRampedFigure {

	class GaugeLayout extends AbstractLayout {

		private static final int GAP_BTW_NEEDLE_SCALE = -1;
		/** Used as a constraint for the scale. */
		public static final String SCALE = "scale"; //$NON-NLS-1$
		/** Used as a constraint for the Needle. */
		public static final String NEEDLE = "needle"; //$NON-NLS-1$
		/** Used as a constraint for the Ramp */
		public static final String RAMP = "ramp"; //$NON-NLS-1$
		/** Used as a constraint for the needleCenter */
		public static final String NEEDLE_CENTER = "needleCenter"; //$NON-NLS-1$
		/** Used as a constraint for the value label */
		public static final String VALUE_LABEL = "valueLabel"; //$NON-NLS-1$
		/** Used as a constraint for the title label */
		public static final String TITLE_LABEL = "titleLabel"; //$NON-NLS-1$
		/** Used as a constraint for the unit label */
		public static final String UNIT_LABEL = "unitLabel"; //$NON-NLS-1$
		private RoundScale scale;
		private RoundScaledRamp ramp;
		private Polygon needle;
		private NeedleCenter needleCenter;
		private Label valueLabel;
		private Label titleLabel;
		private Label unitLabel;
		private final PointList needlePoints = new PointList(new int[]{0, 0, 0, 0, 0, 0});

		@Override
		protected Dimension calculatePreferredSize(final IFigure container, final int w, final int h) {

			final Insets insets = container.getInsets();
			final Dimension d = new Dimension(256, 256);
			d.expand(insets.getWidth(), insets.getHeight());
			return d;
		}

		@Override
		public void layout(final IFigure container) {

			final Rectangle area = container.getClientArea();
			area.width = Math.min(area.width, area.height);
			area.height = area.width;
			area.shrink(BORDER_WIDTH, BORDER_WIDTH);
			final Point center = area.getCenter();
			if(scale != null) {
				scale.setBounds(area);
			}
			if((ramp != null) && ramp.isVisible()) {
				final Rectangle rampBounds = area.getCopy();
				ramp.setBounds(rampBounds.shrink(area.width / 4, area.height / 4));
			}
			if(valueLabel != null) {
				final Dimension labelSize = valueLabel.getPreferredSize();
				// valueLabel.setBounds(new Rectangle((area.x + (area.width / 2)) - (labelSize.width / 2), (area.y + ((area.height * 7) / 8)) - (labelSize.height / 2), labelSize.width, labelSize.height));
				valueLabel.setBounds(new Rectangle((area.x + (area.width / 2)) - (labelSize.width / 2), (area.y + ((area.height * 7) / 10)), labelSize.width, labelSize.height));
			}
			if(titleLabel != null) {
				final Dimension labelSize = titleLabel.getPreferredSize();
				titleLabel.setBounds(new Rectangle((area.x + (area.width / 2)) - (labelSize.width / 2), (area.y + ((area.height * 7) / 8)) - (labelSize.height / 2), labelSize.width, labelSize.height));
			}
			if(unitLabel != null) {
				final Dimension labelSize = unitLabel.getPreferredSize();
				unitLabel.setBounds(new Rectangle((area.x + (area.width / 2)) - (labelSize.width / 2), (area.y + ((area.height * 2) / 3)) - labelSize.height, labelSize.width, labelSize.height));
			}
			if((needle != null) && (scale != null)) {
				needlePoints.setPoint(new Point(center.x, (center.y - (NeedleCenter.DIAMETER / 2)) + 3), 0);
				scale.getScaleTickMarks();
				needlePoints.setPoint(new Point((center.x + (area.width / 2)) - RoundScaleTickMarks.MAJOR_TICK_LENGTH - GAP_BTW_NEEDLE_SCALE, center.y), 1);
				needlePoints.setPoint(new Point(center.x, (center.y + (NeedleCenter.DIAMETER / 2)) - 3), 2);
				double valuePosition = 360 - scale.getValuePosition(getCoercedValue(), false);
				if(maximum > minimum) {
					if(value > maximum) {
						valuePosition += 10;
					} else if(value < minimum) {
						valuePosition -= 10;
					}
				} else {
					if(value > minimum) {
						valuePosition -= 10;
					} else if(value < maximum) {
						valuePosition += 10;
					}
				}
				needlePoints.setPoint(PointsUtil.rotate(needlePoints.getPoint(0), valuePosition, center), 0);
				needlePoints.setPoint(PointsUtil.rotate(needlePoints.getPoint(1), valuePosition, center), 1);
				needlePoints.setPoint(PointsUtil.rotate(needlePoints.getPoint(2), valuePosition, center), 2);
				needle.setPoints(needlePoints);
			}
			if(needleCenter != null) {
				needleCenter.setBounds(new Rectangle(center.x - (NeedleCenter.DIAMETER / 2), center.y - (NeedleCenter.DIAMETER / 2), NeedleCenter.DIAMETER, NeedleCenter.DIAMETER));
			}
		}

		@Override
		public void setConstraint(final IFigure child, final Object constraint) {

			if(constraint.equals(SCALE)) {
				scale = (RoundScale)child;
			} else if(constraint.equals(RAMP)) {
				ramp = (RoundScaledRamp)child;
			} else if(constraint.equals(NEEDLE)) {
				needle = (Polygon)child;
			} else if(constraint.equals(NEEDLE_CENTER)) {
				needleCenter = (NeedleCenter)child;
			} else if(constraint.equals(VALUE_LABEL)) {
				valueLabel = (Label)child;
			} else if(constraint.equals(TITLE_LABEL)) {
				titleLabel = (Label)child;
			} else if(constraint.equals(UNIT_LABEL)) {
				unitLabel = (Label)child;
			}
		}
	}

	class Needle extends Polygon {

		public Needle() {
			setBackgroundColor(DEFAULT_NEEDLE_COLOR);
		}

		@Override
		protected void fillShape(final Graphics g) {

			g.setAntialias(SWT.ON);
			super.fillShape(g);
		}
	}

	class NeedleCenter extends Ellipse {

		public static final int DIAMETER = 16;

		@Override
		protected void fillShape(final Graphics graphics) {

			graphics.setAntialias(SWT.ON);
			Pattern pattern = null;
			graphics.setBackgroundColor(GRAY_COLOR);
			if(support3D == null) {
				support3D = GraphicsUtil.testPatternSupported(graphics);
			}
			if(effect3D && support3D) {
				// add this to eliminate the repaint bug on Mac
				// graphics.fillOval(new Rectangle());
				pattern = GraphicsUtil.createScaledPattern(graphics, Display.getCurrent(), bounds.x, bounds.y, bounds.x + bounds.width, bounds.y + bounds.height, WHITE_COLOR, BORDER_COLOR);
				graphics.setBackgroundPattern(pattern);
			}
			super.fillShape(graphics);
			if(effect3D && support3D) {
				pattern.dispose();
			}
		}
	}

	private final static int BORDER_WIDTH = 2;
	private final Color WHITE_COLOR = XYGraphMediaFactory.getInstance().getColor(XYGraphMediaFactory.COLOR_WHITE);
	private final Color BORDER_COLOR = XYGraphMediaFactory.getInstance().getColor(new RGB(100, 100, 100));
	private final Color GRAY_COLOR = XYGraphMediaFactory.getInstance().getColor(XYGraphMediaFactory.COLOR_GRAY);
	private final Color GREEN_COLOR = XYGraphMediaFactory.getInstance().getColor(XYGraphMediaFactory.COLOR_GREEN);
	private final Color DEFAULT_NEEDLE_COLOR = XYGraphMediaFactory.getInstance().getColor(XYGraphMediaFactory.COLOR_RED);
	private final Font DEFAULT_LABEL_FONT = XYGraphMediaFactory.getInstance().getFont(new FontData("Arial", 12, SWT.BOLD));
	private final Font DEFAULT_TITLE_FONT = XYGraphMediaFactory.getInstance().getFont(new FontData("Arial", 10, SWT.BOLD));
	private boolean effect3D = true;
	private final NeedleCenter needleCenter;
	private final Needle needle;
	private final Label valueLabel;
	private final Label titleLabel = new Label();
	private final Label UnitLabel = new Label();
	private Boolean support3D;

	public GaugeFigureExt() {
		super();
		transparent = true;
		scale.setScaleLineVisible(false);
		scale.setTickLableSide(LabelSide.Secondary);
		ramp.setRampWidth(10);
		valueLabel = new Label();
		valueLabel.setFont(DEFAULT_LABEL_FONT);
		valueLabel.setForegroundColor(GREEN_COLOR);
		titleLabel.setFont(DEFAULT_TITLE_FONT);
		titleLabel.setForegroundColor(GRAY_COLOR);
		UnitLabel.setFont(DEFAULT_TITLE_FONT);
		UnitLabel.setForegroundColor(GRAY_COLOR);
		needle = new Needle();
		needle.setFill(true);
		needle.setOutline(false);
		needleCenter = new NeedleCenter();
		needleCenter.setOutline(false);
		setLayoutManager(new GaugeLayout());
		add(ramp, GaugeLayout.RAMP);
		add(scale, GaugeLayout.SCALE);
		add(valueLabel, GaugeLayout.VALUE_LABEL);
		add(titleLabel, GaugeLayout.TITLE_LABEL);
		add(UnitLabel, GaugeLayout.UNIT_LABEL);
		add(needle, GaugeLayout.NEEDLE);
		add(needleCenter, GaugeLayout.NEEDLE_CENTER);
		addFigureListener(new FigureListener() {

			@Override
			public void figureMoved(final IFigure source) {

				ramp.setDirty(true);
				revalidate();
			}
		});
	}

	public Color getNeedleColor() {

		return needle.getBackgroundColor();
	}

	public String getTitle() {

		return titleLabel.getText();
	}

	public String getUnit() {

		return UnitLabel.getText();
	}

	/**
	 * @return the effect3D
	 */
	public boolean isEffect3D() {

		return effect3D;
	}

	@Override
	protected void paintClientArea(final Graphics graphics) {

		graphics.setAntialias(SWT.ON);
		final Rectangle area = getClientArea();
		area.width = Math.min(area.width, area.height);
		area.height = area.width;
		Pattern pattern = null;
		graphics.pushState();
		graphics.setBackgroundColor(GRAY_COLOR);
		if(support3D == null) {
			support3D = GraphicsUtil.testPatternSupported(graphics);
		}
		if(effect3D && support3D) {
			// add this to eliminate the repaint bug on Mac
			// Who added this? this will cause problem in zoom.
			// graphics.fillOval(new Rectangle());
			pattern = GraphicsUtil.createScaledPattern(graphics, Display.getCurrent(), area.x, area.y, area.x + area.width, area.y + area.height, BORDER_COLOR, WHITE_COLOR);
			graphics.setBackgroundPattern(pattern);
		}
		graphics.fillOval(area);
		if(effect3D && support3D) {
			pattern.dispose();
			area.shrink(BORDER_WIDTH, BORDER_WIDTH);
		} else {
			area.shrink(1, 1);
		}
		graphics.popState();
		graphics.fillOval(area);
		super.paintClientArea(graphics);
		// glossy effect
		if(effect3D && support3D) {
			graphics.pushState();
			graphics.setAntialias(SWT.ON);
			final double R = area.width / 2.0d;
			final double UD_FILL_PART = 9.5d / 10d;
			final double UP_DOWN_RATIO = 1d / 2d;
			final double LR_FILL_PART = 8.5d / 10d;
			final double UP_ANGLE = (0d * Math.PI) / 180d;
			final double DOWN_ANGLE = (35d * Math.PI) / 180d;
			// add this to eliminate the repaint bug on Mac
			// graphics.fillOval(new Rectangle());
			Pattern glossyPattern = GraphicsUtil.createScaledPattern(graphics, Display.getCurrent(), area.x + (area.width / 2), (float)((area.y + (area.height / 2)) - (R * UD_FILL_PART)), area.x + (area.width / 2), (float)(area.y + (area.height / 2) + (R * UP_DOWN_RATIO)), WHITE_COLOR, 90, WHITE_COLOR, 0);
			graphics.setBackgroundPattern(glossyPattern);
			Rectangle rectangle = new Rectangle((int)((area.x + (area.width / 2)) - (R * LR_FILL_PART * Math.cos(UP_ANGLE))), (int)((area.y + (area.height / 2)) - (R * UD_FILL_PART)), (int)(2 * R * LR_FILL_PART * Math.cos(UP_ANGLE)), (int)((R * UD_FILL_PART) + (R * UP_DOWN_RATIO)));
			graphics.fillOval(rectangle);
			glossyPattern.dispose();
			glossyPattern = GraphicsUtil.createScaledPattern(graphics, Display.getCurrent(), area.x + (area.width / 2), (float)((area.y + (area.height / 2) + (R * UP_DOWN_RATIO)) - 1), area.x + (area.width / 2), (float)(area.y + (area.height / 2) + (R * UD_FILL_PART) + 1), WHITE_COLOR, 0, WHITE_COLOR, 40);
			graphics.setBackgroundPattern(glossyPattern);
			rectangle = new Rectangle((int)((area.x + (area.width / 2)) - (R * LR_FILL_PART * Math.sin(DOWN_ANGLE))), (int)Math.ceil(area.y + (area.height / 2) + (R * UP_DOWN_RATIO)), (int)(2 * R * LR_FILL_PART * Math.sin(DOWN_ANGLE)), (int)Math.ceil((R * UD_FILL_PART) - (R * UP_DOWN_RATIO)));
			graphics.fillOval(rectangle);
			glossyPattern.dispose();
			graphics.popState();
		}
	}

	@Override
	public void setBounds(final Rectangle rect) {

		super.setBounds(rect);
	}

	/**
	 * @param effect3D
	 *            the effect3D to set
	 */
	public void setEffect3D(final boolean effect3D) {

		if(this.effect3D == effect3D) {
			return;
		}
		this.effect3D = effect3D;
		repaint();
	}

	/**
	 * @param needleColor
	 *            the needleColor to set
	 */
	public void setNeedleColor(final Color needleColor) {

		needle.setBackgroundColor(needleColor);
	}

	public void setTitle(final String title) {

		titleLabel.setText(title);
	}

	public void setUnit(final String unit) {

		UnitLabel.setText(unit);
	}

	@Override
	public void setValue(final double value) {

		super.setValue(value);
		valueLabel.setText(getValueText());
	}
}
