/*
 * Copyright (c) 1998-2016 by Richard A. Wilkes. All rights reserved.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public License,
 * version 2.0. If a copy of the MPL was not distributed with this file, You
 * can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This Source Code Form is "Incompatible With Secondary Licenses", as defined
 * by the Mozilla Public License, version 2.0.
 */

package com.trollworks.gcs.character;

import com.trollworks.toolkit.ui.layout.ColumnLayout;
import com.trollworks.toolkit.ui.layout.RowDistribution;
import com.trollworks.toolkit.ui.widget.Wrapper;

import java.awt.Container;
import java.awt.Dimension;
import java.awt.Insets;

/** Assembles pages in a sheet. */
public class PageAssembler {
	private CharacterSheet	mSheet;
	private Page			mPage;
	private Wrapper			mContent;
	private int				mRemaining;
	private int				mContentHeight;
	private int				mContentWidth;

	/**
	 * Create a new page assembler.
	 * 
	 * @param sheet The sheet to assemble pages within.
	 */
	PageAssembler(CharacterSheet sheet) {
		mSheet = sheet;
		addPageInternal();
	}

	/** Adds the notes field to the sheet. */
	public void addNotes() {
		NotesPanel notes = new NotesPanel(mSheet.getCharacter().getDescription().getNotes(), false);
		Insets insets = notes.getInsets();
		int width = mContentWidth - (insets.left + insets.right);
		boolean addPage = mRemaining < notes.getMinimumSize().height;
		notes.addActionListener(mSheet);
		notes.setWrapWidth(width);
		while (true) {
			if (addPage) {
				addPageInternal();
			}
			String text = notes.setMaxHeight(mRemaining);
			addPage = !addToContent(notes, null, null);
			if (text == null) {
				break;
			}
			notes = new NotesPanel(text, true);
			notes.addActionListener(mSheet);
			notes.setWrapWidth(width);
		}
	}

	/** @return The content width. */
	public int getContentWidth() {
		return mContentWidth;
	}

	private void addPageInternal() {
		mPage = new Page(mSheet);
		mSheet.add(mPage);
		if (mContentHeight < 1) {
			Insets insets = mPage.getInsets();
			Dimension size = mPage.getSize();
			mContentWidth = size.width - (insets.left + insets.right);
			mContentHeight = size.height - (insets.top + insets.bottom);
		}
		mContent = new Wrapper(new ColumnLayout(1, 2, 2, RowDistribution.GIVE_EXCESS_TO_LAST));
		mContent.setAlignmentY(-1f);
		mRemaining = mContentHeight;
		mPage.add(mContent);
	}

	/**
	 * Add a panel to the content of the page.
	 * 
	 * @param panel The panel to add.
	 * @param leftInfo Outline info for the left outline.
	 * @param rightInfo Outline info for the right outline.
	 * @return <code>true</code> if the panel was too big to fit on a single page.
	 */
	public boolean addToContent(Container panel, OutlineInfo leftInfo, OutlineInfo rightInfo) {
		boolean isOutline = panel instanceof SingleOutlinePanel || panel instanceof DoubleOutlinePanel;
		int height = 0;
		int minLeft = 0;
		int minRight = 0;

		if (isOutline) {
			minLeft = leftInfo.getMinimumHeight();
			if (panel instanceof SingleOutlinePanel) {
				height += minLeft;
			} else {
				minRight = rightInfo.getMinimumHeight();
				height += minLeft < minRight ? minRight : minLeft;
			}
		} else {
			height += panel.getPreferredSize().height;
		}
		if (mRemaining < height) {
			addPageInternal();
		}
		mContent.add(panel);

		if (isOutline) {
			int savedRemaining = mRemaining;
			boolean hasMore;

			if (panel instanceof SingleOutlinePanel) {
				int startIndex = leftInfo.getRowIndex() + 1;
				int amt = leftInfo.determineHeightForOutline(mRemaining);
				((SingleOutlinePanel) panel).setOutlineRowRange(startIndex, leftInfo.getRowIndex());
				if (amt < minLeft) {
					amt = minLeft;
				}
				mRemaining = savedRemaining - amt;
				hasMore = leftInfo.hasMore();
			} else {
				DoubleOutlinePanel panel2 = (DoubleOutlinePanel) panel;
				int leftStart = leftInfo.getRowIndex() + 1;
				int leftHeight = leftInfo.determineHeightForOutline(mRemaining);
				int rightStart = rightInfo.getRowIndex() + 1;
				int rightHeight = rightInfo.determineHeightForOutline(mRemaining);

				panel2.setOutlineRowRange(false, leftStart, leftInfo.getRowIndex());
				panel2.setOutlineRowRange(true, rightStart, rightInfo.getRowIndex());
				if (leftHeight < minLeft) {
					leftHeight = minLeft;
				}
				if (rightHeight < minRight) {
					rightHeight = minRight;
				}
				if (leftHeight < rightHeight) {
					mRemaining = savedRemaining - rightHeight;
				} else {
					mRemaining = savedRemaining - leftHeight;
				}
				hasMore = leftInfo.hasMore() || rightInfo.hasMore();
			}
			if (hasMore) {
				addPageInternal();
				return true;
			}
			return false;
		}

		if (mRemaining >= height) {
			mRemaining -= height;
			return false;
		}
		addPageInternal();
		return true;
	}
}

@@ -753,16 +753,127 @@
 mbler %7B%0A
+%09private static final int%09VERTICAL_SLOP%09= 2;%09// Shouldn't be necessary, but prevents%0A%09%09%09%09%09%09%09%09%09%09%09%09%09%09// cut-offs%0A
 %09private
@@ -888,16 +888,17 @@
 erSheet%09
+%09
 mSheet;%0A
@@ -913,16 +913,17 @@
  Page%09%09%09
+%09
 mPage;%0A%09
@@ -940,16 +940,17 @@
 apper%09%09%09
+%09
 mContent
@@ -963,24 +963,25 @@
 vate int%09%09%09%09
+%09
 mRemaining;%0A
@@ -988,32 +988,33 @@
 %09private int%09%09%09%09
+%09
 mContentHeight;%0A
@@ -1025,24 +1025,25 @@
 vate int%09%09%09%09
+%09
 mContentWidt
@@ -1084,20 +1084,19 @@
 embler.%0A
+
 %09 *
- 
 %0A%09 * @pa
@@ -2332,16 +2332,32 @@
 s.bottom
+ + VERTICAL_SLOP
 );%0A%09%09%7D%0A%09
@@ -2586,17 +2586,16 @@
 age.%0A%09 *
- 
 %0A%09 * @pa

Add some vertical slop to compensate for some inaccuracies in layout.