/*******************************************************************************
 * Copyright (c) 2000, 2010 IBM Corporation and others.
 * 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:
 *     IBM Corporation - initial API and implementation
 *     Tasktop Technologies - extracted FormHeading implementation for Mylyn
 *******************************************************************************/
package org.eclipse.mylyn.commons.ui;

import java.util.Hashtable;
import java.util.Map;

import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;

/**
 * Based on {@link org.eclipse.ui.internal.forms.widgets.FormHeading}.
 * 
 * @since 3.7
 */
@SuppressWarnings("restriction")
public class GradientCanvas extends Canvas {

	private static final int SEPARATOR = 1 << 1;

	private static final int BOTTOM_SEPARATOR = 1 << 2;

	private static final int BACKGROUND_IMAGE_TILED = 1 << 3;

	public static final String COLOR_BASE_BG = "baseBg"; //$NON-NLS-1$

	static String PREFIX = "org.eclipse.ui.forms."; //$NON-NLS-1$

	static String H_PREFIX = PREFIX + "H_"; //$NON-NLS-1$

	public static String H_BOTTOM_KEYLINE1 = H_PREFIX + "BOTTOM_KEYLINE1"; //$NON-NLS-1$

	/**
	 * Key for the form header bottom keyline 2 color.
	 */
	public static String H_BOTTOM_KEYLINE2 = H_PREFIX + "BOTTOM_KEYLINE2"; //$NON-NLS-1$

	private Image backgroundImage;

	private Image gradientImage;

	Map<String, Color> colors = new Hashtable<String, Color>();

	private int flags;

	private GradientInfo gradientInfo;

	private class GradientInfo {
		Color[] gradientColors;

		int[] percents;

		boolean vertical;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.swt.widgets.Control#forceFocus()
	 */
	@Override
	public boolean forceFocus() {
		return false;
	}

	/**
	 * Creates the form content control as a child of the provided parent.
	 * 
	 * @param parent
	 *            the parent widget
	 */
	public GradientCanvas(Composite parent, int style) {
		super(parent, style);
		setBackgroundMode(SWT.INHERIT_DEFAULT);
		setSeparatorAlignment(SWT.BOTTOM);
		addListener(SWT.Paint, new Listener() {
			public void handleEvent(Event e) {
				onPaint(e.gc);
			}
		});
		addListener(SWT.Dispose, new Listener() {
			public void handleEvent(Event e) {
				if (gradientImage != null) {
					// XXX FormImages.getInstance().markFinished(gradientImage);
					gradientImage.dispose();
					gradientImage = null;
				}
			}
		});
		addListener(SWT.Resize, new Listener() {
			public void handleEvent(Event e) {
				if (gradientInfo != null || (backgroundImage != null && !isBackgroundImageTiled())) {
					updateGradientImage();
				}
			}
		});
	}

	/**
	 * Sets the background color of the header.
	 */
	@Override
	public void setBackground(Color bg) {
		super.setBackground(bg);
		internalSetBackground(bg);
	}

	private void internalSetBackground(Color bg) {
		putColor(COLOR_BASE_BG, bg);
	}

	public void setBackgroundGradient(Color[] gradientColors, int[] percents, boolean vertical) {
		if (gradientColors != null) {
			gradientInfo = new GradientInfo();
			gradientInfo.gradientColors = gradientColors;
			gradientInfo.percents = percents;
			gradientInfo.vertical = vertical;
			setBackground(null);
			updateGradientImage();
		} else {
			// reset
			gradientInfo = null;
			if (gradientImage != null) {
				// XXX FormImages.getInstance().markFinished(gradientImage);
				gradientImage.dispose();
				gradientImage = null;
				setBackgroundImage(null);
			}
		}
	}

	public void setHeadingBackgroundImage(Image image) {
		this.backgroundImage = image;
		if (image != null) {
			setBackground(null);
		}
		if (isBackgroundImageTiled()) {
			setBackgroundImage(image);
		} else {
			updateGradientImage();
		}
	}

	public Image getHeadingBackgroundImage() {
		return backgroundImage;
	}

	public void setBackgroundImageTiled(boolean tiled) {
		if (tiled) {
			flags |= BACKGROUND_IMAGE_TILED;
		} else {
			flags &= ~BACKGROUND_IMAGE_TILED;
		}
		setHeadingBackgroundImage(this.backgroundImage);
	}

	public boolean isBackgroundImageTiled() {
		return (flags & BACKGROUND_IMAGE_TILED) != 0;
	}

	@Override
	public void setBackgroundImage(Image image) {
		super.setBackgroundImage(image);
		if (image != null) {
			internalSetBackground(null);
		}
	}

	private void onPaint(GC gc) {
		if (!isSeparatorVisible() && getBackgroundImage() == null) {
			return;
		}
		Rectangle carea = getClientArea();
		Image buffer = new Image(getDisplay(), carea.width, carea.height);
		buffer.setBackground(getBackground());
		GC igc = new GC(buffer);
		igc.setBackground(getBackground());
		igc.fillRectangle(0, 0, carea.width, carea.height);
		if (getBackgroundImage() != null) {
			if (gradientInfo != null) {
				drawBackground(igc, carea.x, carea.y, carea.width, carea.height);
			} else {
				Image bgImage = getBackgroundImage();
				Rectangle ibounds = bgImage.getBounds();
				drawBackground(igc, carea.x, carea.y, ibounds.width, ibounds.height);
			}
		}

		if (isSeparatorVisible()) {
			drawSeparator(carea, igc);
		}
		igc.dispose();
		gc.drawImage(buffer, carea.x, carea.y);
		buffer.dispose();
	}

	private void drawSeparator(Rectangle carea, GC igc) {
		// bg separator
		if (hasColor(H_BOTTOM_KEYLINE1)) {
			igc.setForeground(getColor(H_BOTTOM_KEYLINE1));
		} else {
			igc.setForeground(getBackground());
		}
		if (getSeparatorAlignment() == SWT.BOTTOM) {
			igc.drawLine(carea.x, carea.height - 2, carea.x + carea.width - 1, carea.height - 2);
		} else {
			igc.drawLine(carea.x, 1, carea.x + carea.width - 1, 1);
		}
		if (hasColor(H_BOTTOM_KEYLINE2)) {
			igc.setForeground(getColor(H_BOTTOM_KEYLINE2));
		} else {
			igc.setForeground(getForeground());
		}
		if (getSeparatorAlignment() == SWT.BOTTOM) {
			igc.drawLine(carea.x, carea.height - 1, carea.x + carea.width - 1, carea.height - 1);
		} else {
			igc.drawLine(carea.x, 0, carea.x + carea.width - 1, 0);
		}
	}

	private void updateGradientImage() {
		Rectangle rect = getBounds();
		if (gradientImage != null) {
			// XXX FormImages.getInstance().markFinished(gradientImage);
			gradientImage.dispose();
			gradientImage = null;
		}
		if (gradientInfo != null) {
			// XXX use FormImages
//			gradientImage = FormImages.getInstance().getGradient(gradientInfo.gradientColors, gradientInfo.percents,
//					gradientInfo.vertical ? rect.height : rect.width, gradientInfo.vertical, getColor(COLOR_BASE_BG));
			boolean vertical = gradientInfo.vertical;
			int width = vertical ? 1 : rect.width;
			int height = vertical ? rect.height : 1;
			gradientImage = new Image(getDisplay(), Math.max(width, 1), Math.max(height, 1));
			GC gc = new GC(gradientImage);
			drawTextGradient(gc, width, height);
			gc.dispose();
		} else if (backgroundImage != null && !isBackgroundImageTiled()) {
			gradientImage = new Image(getDisplay(), Math.max(rect.width, 1), Math.max(rect.height, 1));
			gradientImage.setBackground(getBackground());
			GC gc = new GC(gradientImage);
			gc.drawImage(backgroundImage, 0, 0);
			gc.dispose();
		}
		setBackgroundImage(gradientImage);
	}

	// XXX remove, use FormImages
	private void drawTextGradient(GC gc, int width, int height) {
		final Color oldBackground = gc.getBackground();
		if (gradientInfo.gradientColors.length == 1) {
			if (gradientInfo.gradientColors[0] != null) {
				gc.setBackground(gradientInfo.gradientColors[0]);
			}
			gc.fillRectangle(0, 0, width, height);
		} else {
			final Color oldForeground = gc.getForeground();
			Color lastColor = gradientInfo.gradientColors[0];
			if (lastColor == null) {
				lastColor = oldBackground;
			}
			int pos = 0;
			for (int i = 0; i < gradientInfo.percents.length; ++i) {
				gc.setForeground(lastColor);
				lastColor = gradientInfo.gradientColors[i + 1];
				if (lastColor == null) {
					lastColor = oldBackground;
				}
				gc.setBackground(lastColor);
				if (gradientInfo.vertical) {
					final int gradientHeight = (gradientInfo.percents[i] * height / 100) - pos;
					gc.fillGradientRectangle(0, pos, width, gradientHeight, true);
					pos += gradientHeight;
				} else {
					final int gradientWidth = (gradientInfo.percents[i] * width / 100) - pos;
					gc.fillGradientRectangle(pos, 0, gradientWidth, height, false);
					pos += gradientWidth;
				}
			}
			if (gradientInfo.vertical && pos < height) {
				gc.setBackground(getColor(COLOR_BASE_BG));
				gc.fillRectangle(0, pos, width, height - pos);
			}
			if (!gradientInfo.vertical && pos < width) {
				gc.setBackground(getColor(COLOR_BASE_BG));
				gc.fillRectangle(pos, 0, width - pos, height);
			}

			if (isSeparatorVisible()) {
				drawSeparator(getClientArea(), gc);
			}
			gc.setForeground(oldForeground);
		}
	}

	public boolean isSeparatorVisible() {
		return (flags & SEPARATOR) != 0;
	}

	public void setSeparatorVisible(boolean addSeparator) {
		if (addSeparator) {
			flags |= SEPARATOR;
		} else {
			flags &= ~SEPARATOR;
		}
	}

	public void setSeparatorAlignment(int alignment) {
		if (alignment == SWT.BOTTOM) {
			flags |= BOTTOM_SEPARATOR;
		} else {
			flags &= ~BOTTOM_SEPARATOR;
		}
	}

	public int getSeparatorAlignment() {
		return (flags & BOTTOM_SEPARATOR) != 0 ? SWT.BOTTOM : SWT.TOP;
	}

	public void putColor(String key, Color color) {
		if (color == null) {
			colors.remove(key);
		} else {
			colors.put(key, color);
		}
	}

	public Color getColor(String key) {
		return colors.get(key);
	}

	public boolean hasColor(String key) {
		return colors.containsKey(key);
	}

}
