/*
 * Copyright (c) 2007 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 org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.printing.Printer;

/**
 * An enumeration of pages for given print job on the given printer device. Each
 * element in the enumeration has already had the page orientation and page
 * margins applied. Therefore, when calling the paint(GC, int, int) method on
 * each page, the printer's trim should be provided as the x and y arguments. In
 * other words, the trim is taken as a minimum margin while applying calculating
 * margins, but the position where the page's content is drawn is determined
 * solely by the margin, and is not offset by the trim. This behavior is helpful
 * for screen display, and is already compensated for in the
 * {@link PaperClips#print(PrintJob, Printer) } method.
 * 
 * @see PaperClips#getPages(PrintJob, Printer)
 * @author Matthew Hall
 */
public class PageEnumeration {
	private PrintIterator document;
	private Rectangle marginBounds;
	private Rectangle paperBounds;

	private boolean hasNext;

	PageEnumeration(PrintJob job, Printer printer, GC gc) {
		// Rotate the document (and margins with it) depending on print job
		// orientation.
		job = applyOrientation(job, printer);
		Margins margins = job.getMargins();

		marginBounds = PaperClips.getMarginBounds(margins, printer);
		paperBounds = PaperClips.getPaperBounds(printer);

		document = job.getDocument().iterator(printer, gc);
		hasNext = document.hasNext();
	}

	/**
	 * Returns whether any pages remain.
	 * 
	 * @return whether any pages remain.
	 */
	public boolean hasNext() {
		return hasNext;
	}

	/**
	 * Returns the next page.
	 * 
	 * @return the next page.
	 */
	public PrintPiece nextPage() {
		if (!hasNext)
			return null;

		PrintPiece page = PaperClips.next(document, marginBounds.width,
				marginBounds.height);
		hasNext = notNull(page) && notDebugPiece(page) && document.hasNext();
		PrintPiece result = page == null ? null : createPagePiece(page);
		if (!hasNext) {
			document = null;
			marginBounds = null;
			paperBounds = null;
		}
		return result;
	}

	private PrintPiece createPagePiece(PrintPiece page) {
		Point offset = new Point(marginBounds.x - paperBounds.x, marginBounds.y
				- paperBounds.y);
		CompositeEntry entry = new CompositeEntry(page, offset);
		Point size = new Point(paperBounds.width, paperBounds.height);
		return new CompositePiece(new CompositeEntry[] { entry }, size);
	}

	private static boolean notNull(PrintPiece page) {
		return page != null;
	}

	private static boolean notDebugPiece(PrintPiece page) {
		return !(PaperClips.debug && page instanceof NullPrintPiece);
	}

	private static PrintJob applyOrientation(PrintJob printJob, Printer printer) {
		int orientation = printJob.getOrientation();

		Rectangle paperBounds = PaperClips.getPaperBounds(printer);
		if (((orientation == PaperClips.ORIENTATION_LANDSCAPE) && (paperBounds.width < paperBounds.height))
				|| ((orientation == PaperClips.ORIENTATION_PORTRAIT) && (paperBounds.height < paperBounds.width))) {
			String name = printJob.getName();
			Print document = new RotatePrint(printJob.getDocument());
			Margins margins = printJob.getMargins().rotate();
			printJob = new PrintJob(name, document).setMargins(margins)
					.setOrientation(PaperClips.ORIENTATION_DEFAULT);
		}

		return printJob;
	}
}