/*******************************************************************************
 * Copyright (c) 2007, 2008 IBM Corporation 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:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.equinox.internal.p2.ui.sdk.updates;

import org.eclipse.core.runtime.*;
import org.eclipse.core.runtime.Preferences.IPropertyChangeListener;
import org.eclipse.core.runtime.Preferences.PropertyChangeEvent;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.equinox.internal.p2.ui.sdk.ProvSDKMessages;
import org.eclipse.equinox.internal.p2.ui.sdk.ProvSDKUIActivator;
import org.eclipse.equinox.internal.p2.ui.sdk.prefs.PreferenceConstants;
import org.eclipse.equinox.internal.provisional.p2.ui.ProvUIImages;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.dialogs.PopupDialog;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.preference.PreferenceDialog;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.*;
import org.eclipse.ui.dialogs.PreferencesUtil;
import org.eclipse.ui.progress.WorkbenchJob;

/**
 * AutomaticUpdatesPopup is an async popup dialog for notifying
 * the user of updates.
 * 
 * @since 3.4
 */
public class AutomaticUpdatesPopup extends PopupDialog {
	public static final String[] ELAPSED = {ProvSDKMessages.AutomaticUpdateScheduler_30Minutes, ProvSDKMessages.AutomaticUpdateScheduler_60Minutes, ProvSDKMessages.AutomaticUpdateScheduler_240Minutes};
	private static final long MINUTE = 60 * 1000L;
	private static final String PREFS_HREF = "PREFS"; //$NON-NLS-1$
	private static final String DIALOG_SETTINGS_SECTION = "AutomaticUpdatesPopup"; //$NON-NLS-1$
	private static final int POPUP_OFFSET = 20;

	Preferences prefs;
	long remindDelay = -1L;
	IPropertyChangeListener prefListener;
	WorkbenchJob remindJob;
	boolean downloaded;
	Composite dialogArea;
	Link remindLink;
	MouseListener clickListener;

	public AutomaticUpdatesPopup(Shell parentShell, boolean alreadyDownloaded, Preferences prefs) {
		super(parentShell, PopupDialog.INFOPOPUPRESIZE_SHELLSTYLE | SWT.MODELESS, false, true, false, false, ProvSDKMessages.AutomaticUpdatesPopup_UpdatesAvailableTitle, null);
		downloaded = alreadyDownloaded;
		this.prefs = prefs;
		remindDelay = computeRemindDelay();
		clickListener = new MouseAdapter() {
			public void mouseDown(MouseEvent e) {
				ProvSDKUIActivator.getDefault().getAutomaticUpdater().launchUpdate();
			}
		};
	}

	protected Control createDialogArea(Composite parent) {
		dialogArea = new Composite(parent, SWT.NONE);
		dialogArea.setLayoutData(new GridData(GridData.FILL_BOTH));
		GridLayout layout = new GridLayout();
		layout.numColumns = 1;
		dialogArea.setLayout(layout);
		dialogArea.addMouseListener(clickListener);

		// The "click to update" label
		Label infoLabel = new Label(dialogArea, SWT.NONE);
		if (downloaded)
			infoLabel.setText(ProvSDKMessages.AutomaticUpdatesPopup_ClickToReviewDownloaded);
		else
			infoLabel.setText(ProvSDKMessages.AutomaticUpdatesPopup_ClickToReviewNotDownloaded);
		infoLabel.setLayoutData(new GridData(GridData.FILL_BOTH));
		infoLabel.addMouseListener(clickListener);

		createRemindSection(dialogArea);

		return dialogArea;

	}

	private void createRemindSection(Composite parent) {
		remindLink = new Link(parent, SWT.MULTI | SWT.WRAP | SWT.RIGHT);
		updateRemindText();
		remindLink.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				PreferenceDialog dialog = PreferencesUtil.createPreferenceDialogOn(getShell(), PreferenceConstants.PREF_PAGE_AUTO_UPDATES, null, null);
				dialog.open();

			}
		});
		remindLink.setLayoutData(new GridData(GridData.FILL_BOTH));
	}

	private void updateRemindText() {
		if (prefs.getBoolean(PreferenceConstants.PREF_REMIND_SCHEDULE))
			remindLink.setText(NLS.bind(ProvSDKMessages.AutomaticUpdatesPopup_RemindAndPrefLink, new String[] {prefs.getString(PreferenceConstants.PREF_REMIND_ELAPSED), PREFS_HREF}));
		else
			remindLink.setText(ProvSDKMessages.AutomaticUpdatesPopup_PrefLinkOnly);
		remindLink.getParent().layout(true);
	}

	protected IDialogSettings getDialogBoundsSettings() {
		IDialogSettings settings = ProvSDKUIActivator.getDefault().getDialogSettings();
		IDialogSettings section = settings.getSection(DIALOG_SETTINGS_SECTION);
		if (section == null) {
			section = settings.addNewSection(DIALOG_SETTINGS_SECTION);
		}
		return section;
	}

	public int open() {
		prefListener = new IPropertyChangeListener() {
			public void propertyChange(PropertyChangeEvent event) {
				handlePreferenceChange(event);
			}
		};
		prefs.addPropertyChangeListener(prefListener);
		return super.open();
	}

	public boolean close() {
		return close(true);
	}

	public boolean close(boolean remind) {
		if (remind && prefs.getBoolean(PreferenceConstants.PREF_REMIND_SCHEDULE))
			scheduleRemindJob();
		else
			cancelRemindJob();
		if (prefListener != null) {
			prefs.removePropertyChangeListener(prefListener);
			prefListener = null;
		}
		return super.close();

	}

	void scheduleRemindJob() {
		// Cancel any pending remind job if there is one
		if (remindJob != null)
			remindJob.cancel();
		// If no updates have been found, there is nothing to remind
		if (remindDelay < 0)
			return;
		remindJob = new WorkbenchJob(ProvSDKMessages.AutomaticUpdatesPopup_ReminderJobTitle) {
			public IStatus runInUIThread(IProgressMonitor monitor) {
				if (monitor.isCanceled())
					return Status.CANCEL_STATUS;
				open();
				return Status.OK_STATUS;
			}
		};
		remindJob.setSystem(true);
		remindJob.setPriority(Job.INTERACTIVE);
		remindJob.schedule(remindDelay);

	}

	/*
	 * Computes the number of milliseconds for the delay
	 * in reminding the user of updates
	 */
	long computeRemindDelay() {
		if (prefs.getBoolean(PreferenceConstants.PREF_REMIND_SCHEDULE)) {
			String elapsed = prefs.getString(PreferenceConstants.PREF_REMIND_ELAPSED);
			for (int d = 0; d < ELAPSED.length; d++)
				if (ELAPSED[d].equals(elapsed))
					switch (d) {
						case 0 :
							// 30 minutes
							return 30 * MINUTE;
						case 1 :
							// 60 minutes
							return 60 * MINUTE;
						case 2 :
							// 240 minutes
							return 240 * MINUTE;
					}
		}
		return -1L;
	}

	void cancelRemindJob() {
		if (remindJob != null) {
			remindJob.cancel();
			remindJob = null;
		}
	}

	protected void configureShell(Shell newShell) {
		super.configureShell(newShell);
		newShell.setText(ProvSDKMessages.AutomaticUpdatesPopup_UpdatesAvailableTitle);
	}

	/**
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.jface.window.Window#getInitialLocation(org.eclipse.swt.graphics.Point)
	 */
	protected Point getInitialLocation(Point initialSize) {
		Shell parent = getParentShell();
		Point parentSize, parentLocation;

		if (parent != null) {
			parentSize = parent.getSize();
			parentLocation = parent.getLocation();
		} else {
			Rectangle bounds = getShell().getDisplay().getBounds();
			parentSize = new Point(bounds.width, bounds.height);
			parentLocation = new Point(0, 0);
		}
		// We have to take parent location into account because SWT considers all
		// shell locations to be in display coordinates, even if the shell is parented.
		return new Point(parentSize.x - initialSize.x + parentLocation.x - POPUP_OFFSET, parentSize.y - initialSize.y + parentLocation.y - POPUP_OFFSET);
	}

	void handlePreferenceChange(PropertyChangeEvent event) {
		if (PreferenceConstants.PREF_REMIND_SCHEDULE.equals(event.getProperty())) {
			// Reminders turned on
			if (prefs.getBoolean(PreferenceConstants.PREF_REMIND_SCHEDULE)) {
				if (remindLink == null)
					createRemindSection(dialogArea);
				else {
					updateRemindText();
					getShell().layout(true, true);
				}
				computeRemindDelay();
				scheduleRemindJob();
			} else { // reminders turned off
				if (remindLink != null) {
					updateRemindText();
					getShell().layout(true, true);
				}
				cancelRemindJob();
			}
		} else if (PreferenceConstants.PREF_REMIND_ELAPSED.equals(event.getProperty())) {
			// Reminding schedule changed
			computeRemindDelay();
			scheduleRemindJob();
		}
	}

	/*
	 * Overridden so that clicking in the title menu area closes the dialog.
	 * Also creates a close box menu in the title area.
	 */
	protected Control createTitleMenuArea(Composite parent) {
		Composite titleComposite = (Composite) super.createTitleMenuArea(parent);
		titleComposite.addMouseListener(clickListener);

		ToolBar toolBar = new ToolBar(titleComposite, SWT.FLAT);
		ToolItem closeButton = new ToolItem(toolBar, SWT.PUSH, 0);

		GridDataFactory.fillDefaults().align(SWT.END, SWT.CENTER).applyTo(toolBar);
		closeButton.setImage(ProvUIImages.getImage(ProvUIImages.IMG_TOOL_CLOSE));
		closeButton.setHotImage(ProvUIImages.getImage(ProvUIImages.IMG_TOOL_CLOSE_HOT));
		closeButton.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				close();
			}
		});
		// See https://bugs.eclipse.org/bugs/show_bug.cgi?id=177183
		toolBar.addMouseListener(new MouseAdapter() {
			public void mouseDown(MouseEvent e) {
				close();
			}
		});
		return titleComposite;
	}

	/*
	 * Overridden to adjust the span of the title label.
	 * Reachy, reachy....
	 * (non-Javadoc)
	 * @see org.eclipse.jface.dialogs.PopupDialog#createTitleControl(org.eclipse.swt.widgets.Composite)
	 */
	protected Control createTitleControl(Composite parent) {
		Control control = super.createTitleControl(parent);
		Object data = control.getLayoutData();
		if (data instanceof GridData) {
			((GridData) data).horizontalSpan = 1;
		}
		return control;
	}
}
