/*
 * Copyright (c) 2006 Matthew Hall 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:
 *     Matthew Hall - initial API and implementation
 */
package net.sf.paperclips;

import net.sf.paperclips.internal.ResourcePool;
import net.sf.paperclips.internal.Util;

import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Device;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.RGB;

/**
 * A decorator that paints a background color behind it's target.
 * 
 * @author Matthew Hall
 */
public class BackgroundPrint implements Print {
	Print target;
	RGB background;

	/**
	 * Constructs a BackgroundPrint with the given target and background color.
	 * 
	 * @param target
	 *            the
	 * @param background
	 */
	public BackgroundPrint(Print target, RGB background) {
		Util.notNull(target, background);
		this.target = target;
		this.background = background;
	}

	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result
				+ ((background == null) ? 0 : background.hashCode());
		result = prime * result + ((target == null) ? 0 : target.hashCode());
		return result;
	}

	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		BackgroundPrint other = (BackgroundPrint) obj;
		if (background == null) {
			if (other.background != null)
				return false;
		} else if (!background.equals(other.background))
			return false;
		if (target == null) {
			if (other.target != null)
				return false;
		} else if (!target.equals(other.target))
			return false;
		return true;
	}

	/**
	 * Returns the wrapped print to which the background color is being applied.
	 * 
	 * @return the wrapped print to which the background color is being applied.
	 */
	public Print getTarget() {
		return target;
	}

	/**
	 * Returns the background color.
	 * 
	 * @return the background color.
	 */
	public RGB getBackground() {
		return background;
	}

	/**
	 * Sets the background color.
	 * 
	 * @param background
	 *            the new background color.
	 */
	public void setBackground(RGB background) {
		Util.notNull(background);
		this.background = background;
	}

	public PrintIterator iterator(Device device, GC gc) {
		return new BackgroundIterator(this, device, gc);
	}
}

class BackgroundIterator implements PrintIterator {
	private final PrintIterator target;
	private final RGB background;
	private final Device device;

	BackgroundIterator(BackgroundPrint print, Device device, GC gc) {
		Util.notNull(print, device, gc);
		this.device = device;
		this.target = print.target.iterator(device, gc);
		this.background = print.background;
	}

	BackgroundIterator(BackgroundIterator that) {
		this.target = that.target.copy();
		this.background = that.background;
		this.device = that.device;
	}

	public Point minimumSize() {
		return target.minimumSize();
	}

	public Point preferredSize() {
		return target.preferredSize();
	}

	public boolean hasNext() {
		return target.hasNext();
	}

	public PrintPiece next(int width, int height) {
		PrintPiece targetPiece = PaperClips.next(target, width, height);
		if (targetPiece == null)
			return null;
		return new BackgroundPiece(targetPiece, background, device);
	}

	public PrintIterator copy() {
		return new BackgroundIterator(this);
	}
}

class BackgroundPiece implements PrintPiece {
	private final PrintPiece target;
	private final Device device;
	private final RGB background;

	BackgroundPiece(PrintPiece target, RGB background, Device device) {
		Util.notNull(target, background, device);
		this.target = target;
		this.device = device;
		this.background = background;
	}

	public Point getSize() {
		return target.getSize();
	}

	public void paint(GC gc, int x, int y) {
		paintBackground(gc, x, y);
		target.paint(gc, x, y);
	}

	private void paintBackground(GC gc, int x, int y) {
		Color oldBackground = gc.getBackground();

		gc.setBackground(ResourcePool.forDevice(device).getColor(background));
		Point size = getSize();
		gc.fillRectangle(x, y, size.x, size.y);

		gc.setBackground(oldBackground);
	}

	public void dispose() {
		target.dispose();
	}
}