/**
 * This software is released as part of the Pumpernickel project.
 * 
 * All com.pump resources in the Pumpernickel project are distributed under the
 * MIT License:
 * https://github.com/mickleness/pumpernickel/raw/master/License.txt
 * 
 * More information about the Pumpernickel project is available here:
 * https://mickleness.github.io/pumpernickel/
 */
package com.pump.print.swing;

import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.print.PageFormat;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.swing.ButtonGroup;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JSpinner;
import javax.swing.SpinnerNumberModel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import com.pump.blog.ResourceSample;
import com.pump.math.Length;
import com.pump.print.CustomPaper;
import com.pump.print.PrintLayout;
import com.pump.swing.LengthSpinner;
import com.pump.swing.LengthSpinnerGroup;

/**
 * This is the GUI-rich panel that manipulates the properties of a
 * <code>PrintLayout</code>.
 * 
 * 
 * <!-- ======== START OF AUTOGENERATED SAMPLES ======== -->
 * <p>
 * <img src=
 * "https://github.com/mickleness/pumpernickel/raw/master/resources/samples/PrintLayoutPropertiesPanel/sample.png"
 * alt=
 * "new&#160;com.pump.print.swing.PrintLayoutPropertiesPanel(null,&#160;true,&#160;true)"
 * > <!-- ======== END OF AUTOGENERATED SAMPLES ======== -->
 */
@ResourceSample(sample = { "new com.pump.print.swing.PrintLayoutPropertiesPanel(null, true, true)" })
public class PrintLayoutPropertiesPanel extends JPanel {

	private static final long serialVersionUID = 1L;

	JComboBox paperComboBox = new JComboBox();
	JLabel orientationLabel = new JLabel("Orientation:");
	JRadioButton portraitButton = new JRadioButton("Portrait");
	JRadioButton landscapeButton = new JRadioButton("Landscape");
	JLabel rowsLabel = new JLabel("Rows:");
	JLabel columnsLabel = new JLabel("Columns:");
	JLabel leftLabel = new JLabel("Left:");
	JLabel marginsLabel = new JLabel("Margins:");
	JLabel topLabel = new JLabel("Top:");
	JLabel bottomLabel = new JLabel("Bottom:");
	JLabel rightLabel = new JLabel("Right:");
	JLabel paddingLabel = new JLabel("Padding:");
	JLabel widthLabel = new JLabel("Paper Width:");
	JLabel paperLabel = new JLabel("Paper:");
	JLabel paperText = new JLabel();
	JLabel heightLabel = new JLabel("Paper Height:");

	private JPanel orientationRow = new JPanel(new GridBagLayout());
	private JPanel paperRow = new JPanel(new GridBagLayout());
	private JPanel sizeRow = new JPanel(new GridBagLayout());
	private JPanel marginsRow = new JPanel(new GridBagLayout());

	JSpinner rowsSpinner = new JSpinner(new SpinnerNumberModel(1, 1, 4, 1));
	JSpinner columnsSpinner = new JSpinner(new SpinnerNumberModel(1, 1, 4, 1));

	LengthSpinner widthSpinner = new LengthSpinner(8.5, .5, 2, 20, 50,
			Length.INCH, "printDialog");
	LengthSpinner heightSpinner = new LengthSpinner(11.0, .5, 2, 20, 50,
			Length.INCH, "printDialog");
	// JSpinner widthSpinner = new JSpinner(new
	// SpinnerNumberModel(8.5,.5,20,.5));
	// JSpinner heightSpinner = new JSpinner(new
	// SpinnerNumberModel(11.0,.5,20,.5));
	LengthSpinner leftSpinner = new LengthSpinner(1, .25, .5, 2, 5,
			Length.INCH, "printDialog");
	LengthSpinner rightSpinner = new LengthSpinner(1, .25, .5, 2, 5,
			Length.INCH, "printDialog");
	LengthSpinner bottomSpinner = new LengthSpinner(1, .25, .5, 2, 5,
			Length.INCH, "printDialog");
	LengthSpinner topSpinner = new LengthSpinner(1, .25, .5, 2, 5, Length.INCH,
			"printDialog");
	LengthSpinner paddingSpinner = new LengthSpinner(.5, 0, 0, 2, 5,
			Length.INCH, "printDialog");
	// JSpinner leftSpinner = new JSpinner(new SpinnerNumberModel(1,.25,2,.25));
	// JSpinner topSpinner = new JSpinner(new SpinnerNumberModel(1,.25,2,.25));
	// JSpinner rightSpinner = new JSpinner(new
	// SpinnerNumberModel(1,.25,2,.25));
	// JSpinner bottomSpinner = new JSpinner(new
	// SpinnerNumberModel(1,.25,2,.25));
	// JSpinner paddingSpinner = new JSpinner(new
	// SpinnerNumberModel(.25,0,2,.25));
	JCheckBox headerCheckbox = new JCheckBox("Show Header");
	JCheckBox footerCheckbox = new JCheckBox("Show Footer");

	private int adjustingPaperSize = 0;
	final PrintLayout printLayout;
	ActionListener actionListener = new ActionListener() {
		public void actionPerformed(ActionEvent e) {
			if (e.getSource() == landscapeButton) {
				printLayout.setOrientation(PageFormat.LANDSCAPE);
			} else if (e.getSource() == portraitButton) {
				printLayout.setOrientation(PageFormat.PORTRAIT);
			} else if (e.getSource() == headerCheckbox) {
				printLayout.setHeaderActive(headerCheckbox.isSelected());
				repopulate();
			} else if (e.getSource() == footerCheckbox) {
				printLayout.setFooterActive(footerCheckbox.isSelected());
			} else if (e.getSource() == paperComboBox) {
				if (adjustingPaperSize > 0)
					return;

				Object selected = paperComboBox.getSelectedItem();
				adjustingPaperSize++;
				try {
					if (selected instanceof CustomPaper) {
						CustomPaper paper = (CustomPaper) selected;
						printLayout.setPaperWidth((int) (paper.getWidth()));
						printLayout.setPaperHeight((int) (paper.getHeight()));
					}
				} finally {
					adjustingPaperSize--;
				}
			}
			repopulate();

		}
	};

	ChangeListener spinnerListener = new ChangeListener() {
		public void stateChanged(ChangeEvent e) {
			JSpinner spinner = (JSpinner) e.getSource();
			Object v = spinner.getValue();
			Number n;
			if (v instanceof Length) {
				Length l = (Length) v;
				n = new Double(l.getValue(Length.INCH));
			} else {
				n = (Number) v;
			}
			if (spinner == rowsSpinner) {
				printLayout.setRows(n.intValue());
			} else if (spinner == columnsSpinner) {
				printLayout.setColumns(n.intValue());
			} else if (spinner == leftSpinner) {
				Insets insets = printLayout.getInsets();
				insets.left = (int) (n.doubleValue() * 72);
				if (rightSpinner.getParent() == null) {
					// this panel isn't showing the other margin spinners,
					// meaning what we did for "left" should apply to all:
					insets.right = insets.left;
					insets.bottom = insets.left;
					insets.top = insets.left;
				}
				printLayout.setInsets(insets);
			} else if (spinner == rightSpinner) {
				Insets insets = printLayout.getInsets();
				insets.right = (int) (n.doubleValue() * 72);
				printLayout.setInsets(insets);
			} else if (spinner == topSpinner) {
				Insets insets = printLayout.getInsets();
				insets.top = (int) (n.doubleValue() * 72);
				printLayout.setInsets(insets);
			} else if (spinner == bottomSpinner) {
				Insets insets = printLayout.getInsets();
				insets.bottom = (int) (n.doubleValue() * 72);
				printLayout.setInsets(insets);
			} else if (spinner == paddingSpinner) {
				printLayout.setInnerPadding((int) (n.doubleValue() * 72));
			} else if (spinner == widthSpinner) {
				if (adjustingPaperSize > 0)
					return;
				printLayout.setPaperWidth((int) (n.doubleValue() * 72));
			} else if (spinner == heightSpinner) {
				if (adjustingPaperSize > 0)
					return;
				printLayout.setPaperHeight((int) (n.doubleValue() * 72));
			}
			repopulate();
		}
	};

	private PropertyChangeListener layoutListener = new PropertyChangeListener() {
		public void propertyChange(PropertyChangeEvent e) {
			String n = e.getPropertyName();
			if (n.equals(PrintLayout.PROPERTY_HEADER)) {
				String header = printLayout.getHeader();
				boolean headerExists = header != null
						&& header.trim().length() > 0
						&& header.toLowerCase().trim().equals("<html></html>") == false;
				headerCheckbox.setVisible(headerExists);
			} else if (n.equals(PrintLayout.PROPERTY_FOOTER)) {
				String footer = printLayout.getFooter();
				boolean footerExists = footer != null
						&& footer.trim().length() > 0
						&& footer.toLowerCase().trim().equals("<html></html>") == false;
				footerCheckbox.setVisible(footerExists);
			}
		}
	};

	/**
	 * Create a new <code>PrintLayoutPropertiesPanel</code>.
	 * 
	 * @param layout
	 *            the <code>PrintLayout</code> to modify.
	 * @param vertical
	 *            whether these controls should be arranged vertically or
	 *            horizontally.
	 * @param separateMargins
	 *            whether there should be 1 control for margins, or separate
	 *            controls for each of the 4 margins (left, top, right, bottom).
	 */
	public PrintLayoutPropertiesPanel(PrintLayout layout, boolean vertical,
			boolean separateMargins) {
		super(new GridBagLayout());

		if (layout == null)
			layout = new PrintLayout();

		printLayout = layout;

		CustomPaper[] paperArray = CustomPaper.common;
		for (int a = 0; a < paperArray.length; a++) {
			paperComboBox.addItem(paperArray[a]);
		}

		layoutControls(vertical, separateMargins);

		paddingSpinner.setPreferredSize(bottomSpinner.getPreferredSize());

		repopulate();

		rowsSpinner.setValue(new Integer(printLayout.getRows()));
		columnsSpinner.setValue(new Integer(printLayout.getColumns()));

		widthSpinner.addChangeListener(spinnerListener);
		heightSpinner.addChangeListener(spinnerListener);
		leftSpinner.addChangeListener(spinnerListener);
		topSpinner.addChangeListener(spinnerListener);
		rightSpinner.addChangeListener(spinnerListener);
		bottomSpinner.addChangeListener(spinnerListener);
		paddingSpinner.addChangeListener(spinnerListener);
		rowsSpinner.addChangeListener(spinnerListener);
		columnsSpinner.addChangeListener(spinnerListener);

		LengthSpinnerGroup g = new LengthSpinnerGroup();
		g.add(widthSpinner);
		g.add(heightSpinner);
		g.add(leftSpinner);
		g.add(rightSpinner);
		g.add(topSpinner);
		g.add(bottomSpinner);
		g.add(paddingSpinner);

		ButtonGroup buttonGroup = new ButtonGroup();
		buttonGroup.add(portraitButton);
		buttonGroup.add(landscapeButton);

		portraitButton.addActionListener(actionListener);
		landscapeButton.addActionListener(actionListener);
		footerCheckbox.addActionListener(actionListener);
		headerCheckbox.addActionListener(actionListener);
		paperComboBox.addActionListener(actionListener);

		printLayout.addPropertyChangeListener(layoutListener);
		printLayout.addPropertyChangeListener(layoutListener);
		layoutListener.propertyChange(new PropertyChangeEvent(this,
				PrintLayout.PROPERTY_HEADER, null, layout.getHeader()));
		layoutListener.propertyChange(new PropertyChangeEvent(this,
				PrintLayout.PROPERTY_FOOTER, null, layout.getFooter()));
	}

	/**
	 * Arranges the components in this panel.
	 * 
	 * @param vertical
	 *            whether these controls should be arranged vertically or
	 *            horizontally.
	 * @param separateMargins
	 *            whether there should be 1 control for margins, or separate
	 *            controls for each of the 4 margins (left, top, right, bottom).
	 */
	public void layoutControls(boolean vertical, boolean separateMargins) {
		removeAll();
		orientationRow.removeAll();
		paperRow.removeAll();
		sizeRow.removeAll();
		marginsRow.removeAll();

		GridBagConstraints c = new GridBagConstraints();
		c.gridx = 0;
		c.gridy = 0;
		c.weightx = 0;
		c.weighty = 1;
		c.insets = new Insets(0, 0, 0, 3);
		orientationRow.add(landscapeButton, c);
		c.gridx++;
		orientationRow.add(portraitButton, c);

		c.gridx = 0;
		c.gridy = 0;
		c.weightx = 0;
		c.weighty = 1;
		c.insets = new Insets(0, 0, 0, 3);
		paperRow.add(widthSpinner, c);
		c.gridx++;
		paperRow.add(new JLabel("x"), c);
		c.gridx++;
		paperRow.add(heightSpinner, c);

		c.gridx = 0;
		c.gridy = 0;
		c.weightx = 0;
		c.weighty = 1;
		c.insets = new Insets(0, 0, 0, 3);
		sizeRow.add(rowsSpinner, c);
		c.gridx++;
		c.insets = new Insets(0, 13, 0, 3);
		sizeRow.add(columnsLabel, c);
		c.gridx++;
		c.insets = new Insets(0, 0, 0, 3);
		sizeRow.add(columnsSpinner, c);

		c.gridx = 0;
		c.gridy = 0;
		c.weightx = 0;
		c.weighty = 1;
		c.insets = new Insets(0, 0, 0, 6);
		marginsRow.add(leftSpinner, c);
		c.gridx++;
		marginsRow.add(rightLabel, c);
		c.gridx++;
		marginsRow.add(rightSpinner, c);
		c.gridx++;
		marginsRow.add(topLabel, c);
		c.gridx++;
		marginsRow.add(topSpinner, c);
		c.gridx++;
		marginsRow.add(bottomLabel, c);
		c.gridx++;
		marginsRow.add(bottomSpinner, c);

		Insets major = new Insets(18, 3, 3, 3);
		Insets minor = new Insets(3, 3, 3, 3);

		c = new GridBagConstraints();

		if (vertical) {
			if (separateMargins) {
				c.gridx = 0;
				c.gridy = 0;
				c.insets = minor;
				c.anchor = GridBagConstraints.EAST;
				add(paperLabel, c);
				c.gridy += 2;
				c.insets = major;
				add(orientationLabel, c);
				c.gridy++;
				add(rowsLabel, c);
				c.gridy++;
				add(marginsLabel, c);
				c.gridy++;
				c.insets = minor;
				add(leftLabel, c);
				c.gridy++;
				add(topLabel, c);
				c.gridy++;
				add(paddingLabel, c);
				c.gridy++;
				c.insets = major;
				c.gridwidth = 2;
				c.gridx = 0;
				c.anchor = GridBagConstraints.WEST;
				add(headerCheckbox, c);
				c.gridx = 2;
				add(footerCheckbox, c);
				c.gridx = 0;

				c.gridy = 0;
				c.gridx++;
				c.anchor = GridBagConstraints.WEST;
				c.gridwidth = GridBagConstraints.REMAINDER;
				c.insets = minor;
				add(paperComboBox, c);
				c.gridy++;
				c.insets = minor;
				add(paperRow, c);
				c.gridy++;
				c.insets = major;
				add(orientationRow, c);
				c.gridy++;
				add(sizeRow, c);
				c.gridy += 2;
				c.gridwidth = 1;
				c.insets = minor;
				add(leftSpinner, c);
				c.gridy++;
				add(topSpinner, c);
				c.gridy++;
				add(paddingSpinner, c);
				c.gridy -= 2;
				c.gridx++;
				c.anchor = GridBagConstraints.EAST;
				c.insets = new Insets(3, 13, 3, 3);
				add(rightLabel, c);
				c.gridy++;
				add(bottomLabel, c);
				c.gridy -= 1;
				c.gridx++;
				c.anchor = GridBagConstraints.WEST;
				c.insets = new Insets(3, 3, 3, 13);
				add(rightSpinner, c);
				c.gridy++;
				add(bottomSpinner, c);
			} else {
				c.gridx = 0;
				c.gridy = 0;
				c.insets = minor;
				c.anchor = GridBagConstraints.EAST;
				add(paperLabel, c);
				c.gridy += 2;
				c.insets = major;
				add(orientationLabel, c);
				c.gridy++;
				add(rowsLabel, c);
				c.gridy++;
				add(marginsLabel, c);
				c.gridy++;
				c.insets = minor;
				add(paddingLabel, c);
				c.gridy++;
				c.insets = major;
				c.gridwidth = 1;
				c.gridx = 1;
				c.anchor = GridBagConstraints.WEST;
				add(headerCheckbox, c);
				c.gridy++;
				c.insets = minor;
				add(footerCheckbox, c);
				c.gridx = 0;

				c.gridy = 0;
				c.gridx++;
				c.anchor = GridBagConstraints.WEST;
				c.gridwidth = GridBagConstraints.REMAINDER;
				c.insets = minor;
				add(paperComboBox, c);
				c.gridy++;
				c.insets = minor;
				add(paperRow, c);
				c.gridy++;
				c.insets = major;
				add(orientationRow, c);
				c.gridy++;
				add(sizeRow, c);
				c.gridy++;
				c.gridwidth = 1;
				c.insets = major;
				add(leftSpinner, c);
				c.gridy++;
				c.insets = minor;
				add(paddingSpinner, c);
			}
		} else {
			// horizontal layout
			if (separateMargins) {
				c.gridx = 0;
				c.gridy = 0;
				c.weightx = 0;
				c.weighty = 0;
				c.anchor = GridBagConstraints.EAST;
				c.insets = minor;
				add(paperLabel, c);
				c.gridy++;
				c.insets = major;
				add(orientationLabel, c);
				c.gridy += 2;
				add(rowsLabel, c);
				c.gridy++;
				add(leftLabel, c);
				c.gridx++;
				c.insets = major;
				c.gridy = 0;
				c.anchor = GridBagConstraints.WEST;
				c.insets = minor;
				add(paperComboBox, c);
				c.gridy++;
				c.insets = major;
				add(landscapeButton, c);
				c.insets = minor;
				c.gridy++;
				add(portraitButton, c);
				c.gridy++;
				c.insets = major;
				add(sizeRow, c);
				c.gridy++;
				c.gridwidth = GridBagConstraints.REMAINDER;
				add(marginsRow, c);
				c.gridx++;
				c.gridy = 0;
				c.gridwidth = 2;
				c.insets = minor;
				add(paperRow, c);
				c.gridy++;
				c.insets = major;
				add(headerCheckbox, c);
				c.gridy++;
				c.insets = minor;
				add(footerCheckbox, c);
				c.gridy++;
				c.insets = major;
				c.gridwidth = 1;
				add(paddingLabel, c);
				c.gridx++;
				add(paddingSpinner, c);

				leftSpinner.setColumns(leftSpinner.getColumns() - 1);
				rightSpinner.setColumns(leftSpinner.getColumns() - 1);
				topSpinner.setColumns(leftSpinner.getColumns() - 1);
				bottomSpinner.setColumns(leftSpinner.getColumns() - 1);
			} else {
				c.gridx = 0;
				c.gridy = 0;
				c.weightx = 0;
				c.weighty = 0;
				c.anchor = GridBagConstraints.EAST;
				c.insets = minor;
				add(paperLabel, c);
				c.gridy++;
				c.insets = major;
				add(orientationLabel, c);
				c.gridy += 2;
				add(rowsLabel, c);
				c.gridy++;
				c.insets = minor;
				add(columnsLabel, c);
				c.gridx++;
				c.insets = major;
				c.gridy = 0;
				c.anchor = GridBagConstraints.WEST;
				c.insets = minor;
				add(paperComboBox, c);
				c.gridy++;
				c.insets = major;
				add(landscapeButton, c);
				c.insets = minor;
				c.gridy++;
				add(portraitButton, c);
				c.gridy++;
				c.insets = major;
				add(rowsSpinner, c);
				c.gridy++;
				c.insets = minor;
				add(columnsSpinner, c);

				c.gridx++;
				c.gridy = 0;
				c.gridwidth = 2;
				c.insets = minor;
				add(paperRow, c);
				c.gridy++;
				c.insets = major;
				add(headerCheckbox, c);
				c.gridy++;
				c.insets = minor;
				add(footerCheckbox, c);
				c.gridy++;
				c.insets = major;
				c.gridwidth = 1;
				c.anchor = GridBagConstraints.EAST;
				add(paddingLabel, c);
				c.gridy++;
				c.insets = minor;
				add(marginsLabel, c);
				c.anchor = GridBagConstraints.WEST;
				c.gridx++;
				c.gridy--;
				c.insets = major;
				add(paddingSpinner, c);
				c.gridy++;
				c.insets = minor;
				add(leftSpinner, c);
			}
		}
	}

	private void repopulate() {
		CustomPaper paper = null;
		adjustingPaperSize++;
		try {
			double width, height;
			if (printLayout.getOrientation() == PageFormat.PORTRAIT) {
				width = printLayout.getPaperWidth();
				height = printLayout.getPaperHeight();
			} else {
				width = printLayout.getPaperHeight();
				height = printLayout.getPaperWidth();
			}
			widthSpinner.setValue(new Length(width / 72.0, Length.INCH), false);
			heightSpinner.setValue(new Length(height / 72.0, Length.INCH),
					false);

			paperComboBox.removeAllItems();
			for (int a = 0; a < CustomPaper.common.length; a++) {
				if (Math.abs(width - CustomPaper.common[a].getWidth()) < 1
						&& Math.abs(height - CustomPaper.common[a].getHeight()) < 1) {
					paper = CustomPaper.common[a];
				}
				paperComboBox.addItem(CustomPaper.common[a]);
			}
			if (paper != null) {
				paperComboBox.setSelectedItem(paper);
			} else {
				String s = "Custom...";
				paperComboBox.addItem(s);
				paperComboBox.setSelectedItem(s);
			}
		} finally {
			adjustingPaperSize--;
		}

		Insets insets = printLayout.getInsets();
		leftSpinner
				.setValue(new Length(insets.left / 72.0, Length.INCH), false);
		topSpinner.setValue(new Length(insets.top / 72.0, Length.INCH), false);
		rightSpinner.setValue(new Length(insets.right / 72.0, Length.INCH),
				false);
		bottomSpinner.setValue(new Length(insets.bottom / 72.0, Length.INCH),
				false);
		paddingSpinner.setValue(new Length(
				printLayout.getInnerPadding() / 72.0, Length.INCH), false);

		landscapeButton
				.setSelected(printLayout.getOrientation() == PageFormat.LANDSCAPE);
		portraitButton
				.setSelected(printLayout.getOrientation() == PageFormat.PORTRAIT);

		headerCheckbox.setSelected(printLayout.isHeaderActive());
		footerCheckbox.setSelected(printLayout.isFooterActive());

		boolean paddingUsed = !(printLayout.getRows() == 1 && printLayout
				.getColumns() == 1);
		if (printLayout.isHeaderActive()
				&& printLayout.getHeader().length() > 0
				&& printLayout.getHeader().trim().equals("<html></html>") == false) {
			paddingUsed = true;
		}
		if (printLayout.isFooterActive()
				&& printLayout.getFooter().length() > 0
				&& printLayout.getFooter().trim().equals("<html></html>") == false) {
			paddingUsed = true;
		}
		paddingLabel.setEnabled(paddingUsed);
		paddingSpinner.setEnabled(paddingUsed);
	}
}