/*******************************************************************************
 * Copyright (c) 2012 Pivotal Software, Inc.
 * 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:
 *    Pivotal Software, Inc. - initial API and implementation
 *******************************************************************************/
package org.springsource.ide.eclipse.commons.livexp.ui;

import java.util.List;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.operation.IRunnableContext;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.forms.widgets.SharedScrolledComposite;
import org.eclipse.ui.progress.UIJob;
import org.springsource.ide.eclipse.commons.livexp.core.CompositeValidator;
import org.springsource.ide.eclipse.commons.livexp.core.LiveExpression;
import org.springsource.ide.eclipse.commons.livexp.core.ValidationResult;
import org.springsource.ide.eclipse.commons.livexp.core.ValueListener;

/**
 * @author Kris De Volder
 */
public abstract class WizardPageWithSections extends WizardPage implements IPageWithSections, Reflowable, ValueListener<ValidationResult> {

	/**
	 * A delay used for posting status messages to the dialog area after a status update happens.
	 * This is to get rid of spurious message that only appear for a fraction of a second as
	 * internal some auto updating states in models are inconsistent. E.g. in new boot project wizard
	 * when project name is entered it is temporarily inconsistent with default project location until
	 * that project location itself is update in response to the change event from the project name.
	 * If the project location validator runs before the location update, a spurious validation error
	 * temporarily results.
	 *
	 * Note: this is a hacky solution. It would be better if the LiveExp framework solved this by
	 * tracking and scheduling refreshes based on the depedency graph. Thus it might guarantee
	 * that the validator never sees the inconsistent state because it is refreshed last.
	 */
	private static final long MESSAGE_DELAY = 250;

	protected WizardPageWithSections(String pageName, String title, ImageDescriptor titleImage) {
		super(pageName, title, titleImage);
	}

	private List<WizardPageSection> sections = null;
	private CompositeValidator validator;
	private UIJob updateJob;
	private SharedScrolledComposite scroller;
	private Composite page;
	private UIJob reflowJob;

	/* (non-Javadoc)
	 * @see org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets.Composite)
	 */
	public void createControl(Composite parent) {
		GridDataFactory.fillDefaults().grab(true,true).applyTo(parent);

		scroller = new SharedScrolledComposite(parent, SWT.V_SCROLL | SWT.H_SCROLL) {};
//		scroller.setWidthHint(500); // Avoid excessively wide dialogs
//		Display display = Display.getCurrent();
//		Color blue = display.getSystemColor(SWT.COLOR_BLUE);
//		scroller.setBackground(blue);
		scroller.setExpandHorizontal(true);
		scroller.setExpandVertical(true);
		page = new Composite(scroller, SWT.NONE);
        GridLayout layout = new GridLayout(1, false);
        layout.marginHeight = 12;
        layout.marginWidth = 12;
        page.setLayout(layout);
        validator = new CompositeValidator();
        for (PageSection section : getSections()) {
			section.createContents(page);
			validator.addChild(section.getValidator());
		}
        validator.addListener(this);
        Dialog.applyDialogFont(page);
        page.pack(true);
//        scroller.setMinSize(page.getSize());
//        scroller.setSize(page.getSize());
		scroller.setContent(page);
        setControl(scroller);
    	page.addPaintListener(new PaintListener() {
            //This is a bit yuck. But don't know a better way to 'reflow' the page the first time
            // it gets shown. And if we don't do that, then there are often layout issues.
            //See for example: https://www.pivotaltracker.com/story/show/130209913
			@Override
			public void paintControl(PaintEvent e) {
				page.removePaintListener(this);
				reflow();
			}
		});
        if (getContainer().getCurrentPage()!=null) { // Otherwise an NPE will ensue when updating buttons. Buttons depend on current page so that is logical.
	        getContainer().updateButtons();
	        getContainer().updateMessage();
        }
	}

	@Override
	public boolean reflow() {
		if (reflowJob==null) {
			reflowJob = new UIJob(Display.getDefault(), "Reflow Wizard Contents") {
				@Override
				public IStatus runInUIThread(IProgressMonitor monitor) {
					if (scroller!=null && !scroller.isDisposed()) {
						scroller.reflow(true);
					}
					return Status.OK_STATUS;
				}
			};
			reflowJob.setSystem(true);
		}
		reflowJob.schedule();
		return true;
	}

	protected synchronized List<WizardPageSection> getSections() {
		if (sections==null) {
			sections = createSections();
		}
		return sections;
	}

	/**
	 * This method should be implemented to generate the contents of the page.
	 */
	protected abstract List<WizardPageSection> createSections();

	public void gotValue(LiveExpression<ValidationResult> exp, final ValidationResult status) {
//		setPageComplete(status.isOk()); //Don't delay this, never allow clicking finish button if state not consistent.
		scheduleUpdateJob();
	}

	private synchronized void scheduleUpdateJob() {
		Shell shell = getShell();
		if (shell!=null) {
			if (this.updateJob==null) {
				this.updateJob = new UIJob("Update Wizard message") {
					@Override
					public IStatus runInUIThread(IProgressMonitor monitor) {
						ValidationResult status = validator.getValue();
						setErrorMessage(null);
						setMessage(null);
						if (status.isOk()) {
						} else if (status.status == IStatus.ERROR) {
							setErrorMessage(status.msg);
						} else if (status.status == IStatus.WARNING) {
							setMessage(status.msg, IMessageProvider.WARNING);
						} else if (status.status == IStatus.INFO) {
							setMessage(status.msg, IMessageProvider.INFORMATION);
						} else {
							setMessage(status.msg, IMessageProvider.NONE);
						}
						setPageComplete(status.isOk());
						return Status.OK_STATUS;
					}
				};
				updateJob.setSystem(true);
			}
			updateJob.schedule(MESSAGE_DELAY);
		}
	}

	public void dispose() {
		for (WizardPageSection s : sections) {
			s.dispose();
		}
	}

	@Override
	public IRunnableContext getRunnableContext() {
		return getContainer();
	}
}

@@ -693,32 +693,74 @@
 untime.IStatus;%0A
+import org.eclipse.core.runtime.Platform;%0A
 import org.eclip
@@ -5381,24 +5381,59 @@
 sposed()) %7B%0A
+%09%09%09%09%09%09scroller.layout(true, true);%0A
 %09%09%09%09%09%09scroll

Fix a layout issue in New spring Starter wizard