/*******************************************************************************
 * Copyright (c) 2000, 2011 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.jdt.internal.ui.jarpackager;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Text;

import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.ResourcesPlugin;

import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.window.Window;
import org.eclipse.jface.wizard.IWizardPage;
import org.eclipse.jface.wizard.WizardPage;

import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.SaveAsDialog;

import org.eclipse.jdt.internal.corext.util.Messages;

import org.eclipse.jdt.ui.jarpackager.JarPackageData;

import org.eclipse.jdt.internal.ui.IJavaHelpContextIds;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.internal.ui.util.SWTUtil;

/**
 *	Page 2 of the JAR Package wizard
 */
class JarOptionsPage extends WizardPage implements IJarPackageWizardPage {

	// Untyped listener
	private class UntypedListener implements Listener {
		/*
		 * Implements method from Listener
		 */
		@Override
		public void handleEvent(Event e) {
			if (getControl() == null)
				return;
			update();
		}
	}

	private JarPackageData fJarPackage;

	// widgets
	private Button		fExportErrorsCheckbox;
	private Button		fExportWarningsCheckbox;
	private Button		fUseSourceFoldersCheckbox;
	private Composite	fDescriptionFileGroup;
	private Button		fSaveDescriptionCheckbox;
	private Label		fDescriptionFileLabel;
	private Text		fDescriptionFileText;
	private Button		fDescriptionFileBrowseButton;
	private Button		fBuildIfNeededCheckbox;

	// dialog store id constants
	private final static String PAGE_NAME= "jarOptionsWizardPage"; //$NON-NLS-1$

	private final static String STORE_EXPORT_WARNINGS= PAGE_NAME + ".EXPORT_WARNINGS"; //$NON-NLS-1$
	private final static String STORE_EXPORT_ERRORS= PAGE_NAME + ".EXPORT_ERRORS"; //$NON-NLS-1$
	private final static String STORE_SAVE_DESCRIPTION= PAGE_NAME + ".SAVE_DESCRIPTION"; //$NON-NLS-1$
	private final static String STORE_DESCRIPTION_LOCATION= PAGE_NAME + ".DESCRIPTION_LOCATION"; //$NON-NLS-1$
	private final static String STORE_USE_SRC_FOLDERS= PAGE_NAME + ".STORE_USE_SRC_FOLDERS"; //$NON-NLS-1$
	private final static String STORE_BUILD_IF_NEEDED= PAGE_NAME + ".BUILD_IF_NEEDED"; //$NON-NLS-1$

	/**
	 *	Create an instance of this class
	 * @param jarPackage
	 */
	public JarOptionsPage(JarPackageData jarPackage) {
		super(PAGE_NAME);
		setTitle(JarPackagerMessages.JarOptionsPage_title);
		setDescription(JarPackagerMessages.JarOptionsPage_description);
		fJarPackage= jarPackage;
	}

	/*
	 * Method declared on IDialogPage.
	 */
	@Override
	public void createControl(Composite parent) {
		Composite composite= new Composite(parent, SWT.NULL);
		composite.setLayout(new GridLayout());
		composite.setLayoutData(
			new GridData(GridData.VERTICAL_ALIGN_FILL | GridData.HORIZONTAL_ALIGN_FILL));

		createOptionsGroup(composite);

		restoreWidgetValues();
		setControl(composite);
		update();

		Dialog.applyDialogFont(composite);
		PlatformUI.getWorkbench().getHelpSystem().setHelp(composite, IJavaHelpContextIds.JAROPTIONS_WIZARD_PAGE);
	}

	/**
	 *	Create the export options specification widgets.
	 *
	 *	@param parent org.eclipse.swt.widgets.Composite
	 */
	protected void createOptionsGroup(Composite parent) {

		initializeDialogUnits(parent);

		Composite optionsGroup= new Composite(parent, SWT.NONE);
		GridLayout layout= new GridLayout();
		layout.marginHeight= 0;
		optionsGroup.setLayout(layout);

		createLabel(optionsGroup, JarPackagerMessages.JarOptionsPage_howTreatProblems_label, false);

		UntypedListener selectionListener= new UntypedListener();

		fExportErrorsCheckbox= new Button(optionsGroup, SWT.CHECK | SWT.LEFT);
		fExportErrorsCheckbox.setText(JarPackagerMessages.JarOptionsPage_exportErrors_text);
		fExportErrorsCheckbox.addListener(SWT.Selection, selectionListener);

		fExportWarningsCheckbox= new Button(optionsGroup, SWT.CHECK | SWT.LEFT);
		fExportWarningsCheckbox.setText(JarPackagerMessages.JarOptionsPage_exportWarnings_text);
		fExportWarningsCheckbox.addListener(SWT.Selection, selectionListener);

		createSpacer(optionsGroup);

		fUseSourceFoldersCheckbox= new Button(optionsGroup, SWT.CHECK | SWT.LEFT);
		fUseSourceFoldersCheckbox.setText(JarPackagerMessages.JarOptionsPage_useSourceFoldersHierarchy);
		fUseSourceFoldersCheckbox.addListener(SWT.Selection, selectionListener);
		fUseSourceFoldersCheckbox.setEnabled(fJarPackage.areJavaFilesExported() && !fJarPackage.areGeneratedFilesExported());

		createSpacer(optionsGroup);

		fBuildIfNeededCheckbox= new Button(optionsGroup, SWT.CHECK | SWT.LEFT);
		fBuildIfNeededCheckbox.setText(JarPackagerMessages.JarOptionsPage_buildIfNeeded);
		fBuildIfNeededCheckbox.addListener(SWT.Selection, selectionListener);

		createSpacer(optionsGroup);

		fSaveDescriptionCheckbox= new Button(optionsGroup, SWT.CHECK | SWT.LEFT);
		fSaveDescriptionCheckbox.setText(JarPackagerMessages.JarOptionsPage_saveDescription_text);
		fSaveDescriptionCheckbox.addListener(SWT.Selection, selectionListener);
		createDescriptionFileGroup(parent);
	}

	/**
	 * Persists resource specification control setting that are to be restored
	 * in the next instance of this page. Subclasses wishing to persist
	 * settings for their controls should extend the hook method
	 * <code>internalSaveWidgetValues</code>.
	 */
	public final void saveWidgetValues() {
		// update directory names history
		IDialogSettings settings= getDialogSettings();
		if (settings != null) {
			settings.put(STORE_EXPORT_WARNINGS, fJarPackage.exportWarnings());
			settings.put(STORE_EXPORT_ERRORS, fJarPackage.areErrorsExported());
			settings.put(STORE_USE_SRC_FOLDERS, fJarPackage.useSourceFolderHierarchy());
			settings.put(STORE_BUILD_IF_NEEDED, fJarPackage.isBuildingIfNeeded());
			settings.put(STORE_SAVE_DESCRIPTION, fJarPackage.isDescriptionSaved());
			settings.put(STORE_DESCRIPTION_LOCATION, fJarPackage.getDescriptionLocation().toString());
		}
		// Allow subclasses to save values
		internalSaveWidgetValues();
	}

	/**
	 * Hook method for subclasses to persist their settings.
	 */
	protected void internalSaveWidgetValues() {
	}

	/**
	 *	Hook method for restoring widget values to the values that they held
	 *	last time this wizard was used to completion.
	 */
	protected void restoreWidgetValues() {
		if (!((JarPackageWizard)getWizard()).isInitializingFromJarPackage())
			initializeJarPackage();

		fExportWarningsCheckbox.setSelection(fJarPackage.exportWarnings());
		fExportErrorsCheckbox.setSelection(fJarPackage.areErrorsExported());
		fBuildIfNeededCheckbox.setSelection(fJarPackage.isBuildingIfNeeded());
		fUseSourceFoldersCheckbox.setSelection(fJarPackage.useSourceFolderHierarchy());
		fSaveDescriptionCheckbox.setSelection(fJarPackage.isDescriptionSaved());
		fDescriptionFileText.setText(fJarPackage.getDescriptionLocation().toString());
	}

	/**
	 *	Initializes the JAR package from last used wizard page values.
	 */
	protected void initializeJarPackage() {
		IDialogSettings settings= getDialogSettings();
		if (settings != null) {
			fJarPackage.setExportWarnings(settings.getBoolean(STORE_EXPORT_WARNINGS));
			fJarPackage.setExportErrors(settings.getBoolean(STORE_EXPORT_ERRORS));
			fJarPackage.setUseSourceFolderHierarchy(settings.getBoolean(STORE_USE_SRC_FOLDERS));
			fJarPackage.setSaveDescription(false); // bug 15877
			String pathStr= settings.get(STORE_DESCRIPTION_LOCATION);
			if (pathStr == null)
				pathStr= ""; //$NON-NLS-1$
			fJarPackage.setDescriptionLocation(new Path(pathStr));
			if (settings.get(STORE_BUILD_IF_NEEDED) != null)
				fJarPackage.setBuildIfNeeded(settings.getBoolean(STORE_BUILD_IF_NEEDED));
		}
	}

	private void update() {
		updateModel();
		updateWidgetEnablements();
		updatePageCompletion();
	}

	/**
	 *	Stores the widget values in the JAR package.
	 */
	protected void updateModel() {
		if (getControl() == null)
			return;
		fJarPackage.setExportWarnings(fExportWarningsCheckbox.getSelection());
		fJarPackage.setExportErrors(fExportErrorsCheckbox.getSelection());
		fJarPackage.setBuildIfNeeded(fBuildIfNeededCheckbox.getSelection());
		fJarPackage.setSaveDescription(fSaveDescriptionCheckbox.getSelection());
		fJarPackage.setDescriptionLocation(new Path(fDescriptionFileText.getText()));
		fJarPackage.setUseSourceFolderHierarchy(fUseSourceFoldersCheckbox.getSelection());
	}

	/**
	 *	Open an appropriate destination browser so that the user can specify a source
	 *	to import from
	 */
	protected void handleDescriptionFileBrowseButtonPressed() {
		SaveAsDialog dialog= new SaveAsDialog(getContainer().getShell());
		dialog.create();
		dialog.getShell().setText(JarPackagerMessages.JarOptionsPage_saveAsDialog_title);
		dialog.setMessage(JarPackagerMessages.JarOptionsPage_saveAsDialog_message);
		dialog.setOriginalFile(createFileHandle(fJarPackage.getDescriptionLocation()));
		if (dialog.open() == Window.OK) {
			IPath path= dialog.getResult();
			path= path.removeFileExtension().addFileExtension(JarPackagerUtil.DESCRIPTION_EXTENSION);
			fDescriptionFileText.setText(path.toString());
		}
	}

	/**
	 * Updates the enablements of this page's controls. Subclasses may extend.
	 */
	protected void updateWidgetEnablements() {
		boolean saveDescription= fSaveDescriptionCheckbox.getSelection();
		fDescriptionFileGroup.setEnabled(saveDescription);
		fDescriptionFileBrowseButton.setEnabled(saveDescription);
		fDescriptionFileText.setEnabled(saveDescription);
		fDescriptionFileLabel.setEnabled(saveDescription);

		boolean exportClassFiles= fJarPackage.areClassFilesExported() && !fJarPackage.areOutputFoldersExported();
		fExportWarningsCheckbox.setEnabled(exportClassFiles);
		fExportErrorsCheckbox.setEnabled(exportClassFiles);

		boolean isAutobuilding= ResourcesPlugin.getWorkspace().isAutoBuilding();
		fBuildIfNeededCheckbox.setEnabled(exportClassFiles && !isAutobuilding);

		fUseSourceFoldersCheckbox.setEnabled(fJarPackage.areJavaFilesExported() && !fJarPackage.areGeneratedFilesExported());
	}

	/*
	 * Implements method from IJarPackageWizardPage
	 */
	@Override
	public boolean isPageComplete() {
		if (fJarPackage.isDescriptionSaved()){
			if (fJarPackage.getDescriptionLocation().toString().length() == 0) {
				setErrorMessage(null);
				return false;
			}
			IPath location= fJarPackage.getDescriptionLocation();
			if (!location.toString().startsWith("/")) { //$NON-NLS-1$
				setErrorMessage(JarPackagerMessages.JarOptionsPage_error_descriptionMustBeAbsolute);
				return false;
			}
			IResource resource= findResource(location);
			if (resource != null && resource.getType() != IResource.FILE) {
				setErrorMessage(JarPackagerMessages.JarOptionsPage_error_descriptionMustNotBeExistingContainer);
				return false;
			}
			resource= findResource(location.removeLastSegments(1));
			if (resource == null || resource.getType() == IResource.FILE) {
				setErrorMessage(JarPackagerMessages.JarOptionsPage_error_descriptionContainerDoesNotExist);
				return false;
			}
			String fileExtension= fJarPackage.getDescriptionLocation().getFileExtension();
			if (fileExtension == null || !JarPackagerUtil.DESCRIPTION_EXTENSION.equals(fileExtension)) {
				setErrorMessage(Messages.format(JarPackagerMessages.JarOptionsPage_error_invalidDescriptionExtension, JarPackagerUtil.DESCRIPTION_EXTENSION));
				return false;
			}
		}
		setErrorMessage(null);
		return true;
	}

	@Override
	public boolean canFlipToNextPage() {
		return fJarPackage.areGeneratedFilesExported() && super.canFlipToNextPage();
	}

	/*
	 * Overrides method from WizardDataTransferPage
	 */
	protected void createDescriptionFileGroup(Composite parent) {
		// destination specification group
		fDescriptionFileGroup= new Composite(parent, SWT.NONE);
		GridLayout layout= new GridLayout();
		layout.numColumns= 3;
		fDescriptionFileGroup.setLayout(layout);
		fDescriptionFileGroup.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.VERTICAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL));

		fDescriptionFileLabel= new Label(fDescriptionFileGroup, SWT.NONE);
		fDescriptionFileLabel.setText(JarPackagerMessages.JarOptionsPage_descriptionFile_label);

		// destination name entry field
		fDescriptionFileText= new Text(fDescriptionFileGroup, SWT.SINGLE | SWT.BORDER);
		fDescriptionFileText.addListener(SWT.Modify, new UntypedListener());
		GridData data= new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL);
		data.widthHint= convertWidthInCharsToPixels(40);
		fDescriptionFileText.setLayoutData(data);

		// destination browse button
		fDescriptionFileBrowseButton= new Button(fDescriptionFileGroup, SWT.PUSH);
		fDescriptionFileBrowseButton.setText(JarPackagerMessages.JarOptionsPage_browseButton_text);
		fDescriptionFileBrowseButton.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL));
		SWTUtil.setButtonDimensionHint(fDescriptionFileBrowseButton);
		fDescriptionFileBrowseButton.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				handleDescriptionFileBrowseButtonPressed();
			}
		});
	}

	/**
	 * Creates a file resource handle for the file with the given workspace path.
	 * This method does not create the file resource; this is the responsibility
	 * of <code>createFile</code>.
	 *
	 * @param filePath the path of the file resource to create a handle for
	 * @return the new file resource handle
	 */
	protected IFile createFileHandle(IPath filePath) {
		if (filePath.isValidPath(filePath.toString()) && filePath.segmentCount() >= 2)
			return JavaPlugin.getWorkspace().getRoot().getFile(filePath);
		else
			return null;
	}
	/*
	 * Method declared on IWizardPage.
	 */
	@Override
	public void setPreviousPage(IWizardPage page) {
		super.setPreviousPage(page);
		updateWidgetEnablements();
		if (getControl() != null)
			updatePageCompletion();
	}

	/*
	 * Implements method from IJarPackageWizardPage.
	 */
	@Override
	public void finish() {
		saveWidgetValues();
	}

	/**
	 * Creates a new label with a bold font.
	 *
	 * @param parent the parent control
	 * @param text the label text
	 * @param bold bold or not
	 * @return the new label control
	 */
	protected Label createLabel(Composite parent, String text, boolean bold) {
		Label label= new Label(parent, SWT.NONE);
		if (bold)
			label.setFont(JFaceResources.getBannerFont());
		label.setText(text);
		GridData data= new GridData();
		data.verticalAlignment= GridData.FILL;
		data.horizontalAlignment= GridData.FILL;
		label.setLayoutData(data);
		return label;
	}

	/**
	 * Creates a horizontal spacer line that fills the width of its container.
	 *
	 * @param parent the parent control
	 */
	protected void createSpacer(Composite parent) {
		Label spacer= new Label(parent, SWT.NONE);
		GridData data= new GridData();
		data.horizontalAlignment= GridData.FILL;
		data.verticalAlignment= GridData.BEGINNING;
		spacer.setLayoutData(data);
	}

	/**
	 * Determine if the page is complete and update the page appropriately.
	 */
	protected void updatePageCompletion() {
		boolean pageComplete= isPageComplete();
		setPageComplete(pageComplete);
		if (pageComplete) {
			setErrorMessage(null);
		}
	}

	/**
	 * Returns the resource for the specified path.
	 *
	 * @param path	the path for which the resource should be returned
	 * @return the resource specified by the path or <code>null</code>
	 */
	protected IResource findResource(IPath path) {
		IWorkspace workspace= JavaPlugin.getWorkspace();
		IStatus result= workspace.validatePath(
							path.toString(),
							IResource.ROOT | IResource.PROJECT | IResource.FOLDER | IResource.FILE);
		if (result.isOK() && workspace.getRoot().exists(path))
			return workspace.getRoot().findMember(path);
		return null;
	}
}
