/*******************************************************************************
 * Copyright 2005-2006, CHISEL Group, University of Victoria, Victoria, BC,
 * Canada. 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
 * 
 * Contributors: The Chisel Group, University of Victoria
 ******************************************************************************/
package org.eclipse.zest.core.widgets.internal;

import org.eclipse.draw2d.Animation;
import org.eclipse.draw2d.ColorConstants;
import org.eclipse.draw2d.Graphics;
import org.eclipse.draw2d.Label;
import org.eclipse.draw2d.SWTGraphics;
import org.eclipse.draw2d.ScaledGraphics;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Display;

/**
 * A cached label to improve performance of text drawing under linux
 * 
 * @author Ian Bull
 * 
 */
public abstract class CachedLabel extends Label {

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.draw2d.Label#paintFigure(org.eclipse.draw2d.Graphics)
	 */
	Image cachedImage = null;
	boolean cacheLabel = false;
	boolean invalidationRequired = false;

	/**
	 * CachedLabel constructor.
	 * 
	 * @param cacheLabel
	 *            Should the label be cached, or should the text be re-layedout
	 *            each time
	 */
	public CachedLabel(boolean cacheLabel) {
		this.cacheLabel = cacheLabel;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.draw2d.Label#setIcon(org.eclipse.swt.graphics.Image)
	 */
	public void setIcon(Image image) {
		updateInvalidation();
		super.setIcon(image);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.draw2d.Figure#setForegroundColor(org.eclipse.swt.graphics.Color)
	 */
	public void setForegroundColor(Color fg) {
		updateInvalidation();
		super.setForegroundColor(fg);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.draw2d.Figure#setBackgroundColor(org.eclipse.swt.graphics.Color)
	 */
	public void setBackgroundColor(Color bg) {
		updateInvalidation();
		super.setBackgroundColor(bg);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.draw2d.Figure#setFont(org.eclipse.swt.graphics.Font)
	 */
	public void setFont(Font f) {
		updateInvalidation();
		super.setFont(f);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.draw2d.Label#setText(java.lang.String)
	 */
	public void setText(String s) {
		updateInvalidation();
		super.setText(s);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.draw2d.Figure#setSize(int, int)
	 */
	public void setSize(int w, int h) {
		updateInvalidation();

		if (cachedImage != null && shouldInvalidateCache()) {
			cleanImage();
		}
		super.setSize(w, h);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.draw2d.Figure#setBounds(org.eclipse.draw2d.geometry.Rectangle)
	 */
	public void setBounds(Rectangle rect) {
		boolean resize = (rect.width != bounds.width) || (rect.height != bounds.height);

		if (resize && Animation.isAnimating()) {
			updateInvalidation();
		}
		if (resize && shouldInvalidateCache() && cachedImage != null) {
			cleanImage();
		}

		super.setBounds(rect);
	}

	/**
	 * Override this method to return the background colour for the text Note:
	 * Text must have a background color since it is being stored in an image
	 * (You can set it to white if you want)
	 * 
	 * @return
	 */
	protected abstract Color getBackgroundTextColor();

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.draw2d.Label#paintFigure(org.eclipse.draw2d.Graphics)
	 */
	static Rectangle tempRect = new Rectangle();

	protected void paintFigure(Graphics graphics) {
		if (graphics.getClass() == ScaledGraphics.class) {
			if (((ScaledGraphics) graphics).getAbsoluteScale() < 0.30) {
				return;
			}
		}
		if (!cacheLabel) {
			if (isOpaque()) {
				super.paintFigure(graphics);
			}
			Rectangle bounds = getBounds();
			graphics.translate(bounds.x, bounds.y);
			if (getIcon() != null) {
				graphics.drawImage(getIcon(), getIconLocation());
			}
			if (!isEnabled()) {
				graphics.translate(1, 1);
				graphics.setForegroundColor(ColorConstants.buttonLightest);
				graphics.drawText(getSubStringText(), getTextLocation());
				graphics.translate(-1, -1);
				graphics.setForegroundColor(ColorConstants.buttonDarker);
			}
			graphics.drawText(getText(), getTextLocation());
			graphics.translate(-bounds.x, -bounds.y);
			return;
		}

		if (isOpaque()) {
			graphics.fillRectangle(getBounds());
		}
		Rectangle bounds = getBounds();
		graphics.translate(bounds.x, bounds.y);

		Image icon = getIcon();

		if (icon != null) {
			graphics.drawImage(icon, getIconLocation());
		}

		int width = getSubStringTextSize().width;
		int height = getSubStringTextSize().height;

		if (cachedImage == null || shouldInvalidateCache()) {
			invalidationRequired = false;
			cleanImage();
			cachedImage = new Image(Display.getCurrent(), width, height);

			// @tag TODO : Dispose of the image properly
			//ZestPlugin.getDefault().addImage(cachedImage.toString(), cachedImage);
//			   UNSUPPORTED - image constructor not implemented in RAP
//			GC gc = new GC(cachedImage);
			GC gc = new GC(cachedImage.getDevice());

			Graphics graphics2 = new SWTGraphics(gc);
			graphics2.setBackgroundColor(getBackgroundTextColor());
			graphics2.fillRectangle(0, 0, width, height);
			graphics2.setForegroundColor(getForegroundColor());
			//graphics2.drawText(getSubStringText(), new Point(0, 0));
			graphics2.drawText(getText(), new Point(0, 0));
			gc.dispose();

		}
		graphics.drawImage(cachedImage, getTextLocation());
		graphics.translate(-bounds.x, -bounds.y);
		this.paintBorder(graphics);

	}

	/**
	 * Determine if the image should be remade or not
	 * 
	 * @return
	 */
	private boolean shouldInvalidateCache() {
		if (invalidationRequired && !Animation.isAnimating()) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * Notifies the cache that the image will need updating.
	 */
	private void updateInvalidation() {
		invalidationRequired = true;
	}

	protected void cleanImage() {
		if (cachedImage != null) {

			//ZestPlugin.getDefault().removeImage(cachedImage.toString());
			cachedImage.dispose();
			cachedImage = null;
		}
	}
}
