/*******************************************************************************
 * Copyright (c) 2000, 2018 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.preferences;

import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;

import org.eclipse.core.runtime.IStatus;

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

import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.layout.PixelConverter;

import org.eclipse.ui.forms.widgets.ExpandableComposite;
import org.eclipse.ui.preferences.IWorkbenchPreferenceContainer;

import org.eclipse.jdt.core.JavaCore;

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

import org.eclipse.jdt.launching.JavaRuntime;

import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.internal.ui.dialogs.StatusInfo;
import org.eclipse.jdt.internal.ui.dialogs.StatusUtil;
import org.eclipse.jdt.internal.ui.wizards.IStatusChangeListener;


public class JavaBuildConfigurationBlock extends OptionsConfigurationBlock {

	private static final String SETTINGS_SECTION_NAME= "JavaBuildConfigurationBlock"; //$NON-NLS-1$

	private static final Key PREF_PB_MAX_PER_UNIT= getJDTCoreKey(JavaCore.COMPILER_PB_MAX_PER_UNIT);

	private static final Key PREF_RESOURCE_FILTER= getJDTCoreKey(JavaCore.CORE_JAVA_BUILD_RESOURCE_COPY_FILTER);
	private static final Key PREF_BUILD_INVALID_CLASSPATH= getJDTCoreKey(JavaCore.CORE_JAVA_BUILD_INVALID_CLASSPATH);
	private static final Key PREF_BUILD_CLEAN_OUTPUT_FOLDER= getJDTCoreKey(JavaCore.CORE_JAVA_BUILD_CLEAN_OUTPUT_FOLDER);
	private static final Key PREF_ENABLE_EXCLUSION_PATTERNS= getJDTCoreKey(JavaCore.CORE_ENABLE_CLASSPATH_EXCLUSION_PATTERNS);
	private static final Key PREF_ENABLE_MULTIPLE_OUTPUT_LOCATIONS= getJDTCoreKey(JavaCore.CORE_ENABLE_CLASSPATH_MULTIPLE_OUTPUT_LOCATIONS);

	private static final Key PREF_PB_INCOMPLETE_BUILDPATH= getJDTCoreKey(JavaCore.CORE_INCOMPLETE_CLASSPATH);
	private static final Key PREF_PB_CIRCULAR_BUILDPATH= getJDTCoreKey(JavaCore.CORE_CIRCULAR_CLASSPATH);
	private static final Key PREF_PB_INCOMPATIBLE_JDK_LEVEL= getJDTCoreKey(JavaCore.CORE_INCOMPATIBLE_JDK_LEVEL);
	private static final Key PREF_PB_MAIN_ONLY_PROJECT_HAS_TEST_ONLY_DEPENDENCY= getJDTCoreKey(JavaCore.CORE_MAIN_ONLY_PROJECT_HAS_TEST_ONLY_DEPENDENCY);
	private static final Key PREF_PB_OUTPUT_LOCATION_OVERLAPPING_ANOTHER_SOURCE= getJDTCoreKey(JavaCore.CORE_OUTPUT_LOCATION_OVERLAPPING_ANOTHER_SOURCE);
	private static final Key PREF_PB_DUPLICATE_RESOURCE= getJDTCoreKey(JavaCore.CORE_JAVA_BUILD_DUPLICATE_RESOURCE);
	private static final Key PREF_RECREATE_MODIFIED_CLASS_FILES= getJDTCoreKey(JavaCore.CORE_JAVA_BUILD_RECREATE_MODIFIED_CLASS_FILES_IN_OUTPUT_FOLDER);

	private static final Key PREF_PB_STRICTLY_COMPATIBLE_JRE_NOT_AVAILABLE= getJDTLaunchingKey(JavaRuntime.PREF_STRICTLY_COMPATIBLE_JRE_NOT_AVAILABLE);
	private static final Key PREF_PB_COMPILER_COMPLIANCE_DOES_NOT_MATCH_JRE= getJDTLaunchingKey(JavaRuntime.PREF_COMPILER_COMPLIANCE_DOES_NOT_MATCH_JRE);


	// values
	private static final String ERROR= JavaCore.ERROR;
	private static final String WARNING= JavaCore.WARNING;
	private static final String INFO= JavaCore.INFO;
	private static final String IGNORE= JavaCore.IGNORE;

	private static final String ABORT= JavaCore.ABORT;
	private static final String CLEAN= JavaCore.CLEAN;

	private static final String ENABLED= JavaCore.ENABLED;
	private static final String DISABLED= JavaCore.DISABLED;

	private PixelConverter fPixelConverter;

	private IStatus fMaxNumberProblemsStatus, fResourceFilterStatus;

	public JavaBuildConfigurationBlock(IStatusChangeListener context, IProject project, IWorkbenchPreferenceContainer container) {
		super(context, project, getKeys(), container);
		fMaxNumberProblemsStatus= new StatusInfo();
		fResourceFilterStatus= new StatusInfo();
	}

	public static Key[] getKeys() {
		Key[] keys= new Key[] {
				PREF_PB_MAX_PER_UNIT, PREF_RESOURCE_FILTER, PREF_BUILD_INVALID_CLASSPATH, PREF_PB_INCOMPLETE_BUILDPATH, PREF_PB_CIRCULAR_BUILDPATH,
				PREF_BUILD_CLEAN_OUTPUT_FOLDER, PREF_PB_DUPLICATE_RESOURCE,
				PREF_PB_INCOMPATIBLE_JDK_LEVEL, PREF_PB_OUTPUT_LOCATION_OVERLAPPING_ANOTHER_SOURCE, PREF_ENABLE_EXCLUSION_PATTERNS, PREF_ENABLE_MULTIPLE_OUTPUT_LOCATIONS,
				PREF_RECREATE_MODIFIED_CLASS_FILES,	PREF_PB_STRICTLY_COMPATIBLE_JRE_NOT_AVAILABLE, PREF_PB_COMPILER_COMPLIANCE_DOES_NOT_MATCH_JRE,
				PREF_PB_MAIN_ONLY_PROJECT_HAS_TEST_ONLY_DEPENDENCY
			};
		return keys;
	}


	/*
	 * @see org.eclipse.jface.preference.PreferencePage#createContents(Composite)
	 */
	@Override
	protected Control createContents(Composite parent) {
		fPixelConverter= new PixelConverter(parent);
		setShell(parent.getShell());

		Composite mainComp= new Composite(parent, SWT.NONE);
		mainComp.setFont(parent.getFont());
		GridLayout layout= new GridLayout();
		layout.marginHeight= 0;
		layout.marginWidth= 0;
		mainComp.setLayout(layout);

		Composite othersComposite= createBuildPathTabContent(mainComp);
		GridData gridData= new GridData(GridData.FILL, GridData.FILL, true, true);
		gridData.heightHint= fPixelConverter.convertHeightInCharsToPixels(20);
		othersComposite.setLayoutData(gridData);

		validateSettings(null, null, null);

		return mainComp;
	}


	private Composite createBuildPathTabContent(Composite parent) {
		String[] abortIgnoreValues= new String[] { ABORT, IGNORE };
		String[] cleanIgnoreValues= new String[] { CLEAN, IGNORE };
		String[] enableDisableValues= new String[] { ENABLED, DISABLED };
		String[] enableIgnoreValues= new String[] { ENABLED, IGNORE };

		String[] errorWarning= new String[] { ERROR, WARNING };

		String[] errorWarningLabels= new String[] {
			PreferencesMessages.JavaBuildConfigurationBlock_error,
			PreferencesMessages.JavaBuildConfigurationBlock_warning
		};

		String[] errorIgnore= new String[] { ERROR, IGNORE };

		String[] errorIgnoreLabels= new String[] {
			PreferencesMessages.JavaBuildConfigurationBlock_error,
			PreferencesMessages.JavaBuildConfigurationBlock_ignore
		};

		String[] errorWarningInfoIgnore= new String[] { ERROR, WARNING, INFO, IGNORE };
		String[] errorWarningInfoIgnoreLabels= new String[] {
			PreferencesMessages.JavaBuildConfigurationBlock_error,
			PreferencesMessages.JavaBuildConfigurationBlock_warning,
			PreferencesMessages.JavaBuildConfigurationBlock_info,
			PreferencesMessages.JavaBuildConfigurationBlock_ignore
		};

		int nColumns= 3;

		final ScrolledPageContent pageContent = new ScrolledPageContent(parent);

		GridLayout layout= new GridLayout();
		layout.numColumns= nColumns;
		layout.marginHeight= 0;
		layout.marginWidth= 0;

		Composite composite= pageContent.getBody();
		composite.setLayout(layout);

		String label= PreferencesMessages.JavaBuildConfigurationBlock_section_general;
		ExpandableComposite excomposite= createStyleSection(composite, label, nColumns);

		Composite othersComposite= new Composite(excomposite, SWT.NONE);
		excomposite.setClient(othersComposite);
		othersComposite.setLayout(new GridLayout(nColumns, false));

		label= PreferencesMessages.JavaBuildConfigurationBlock_pb_max_per_unit_label;
		Text text= addTextField(othersComposite, label, PREF_PB_MAX_PER_UNIT, 0, 0);
		GridData gd= (GridData) text.getLayoutData();
		gd.widthHint= fPixelConverter.convertWidthInCharsToPixels(8);
		gd.horizontalAlignment= GridData.END;
		text.setTextLimit(6);

		label= PreferencesMessages.JavaBuildConfigurationBlock_enable_exclusion_patterns_label;
		addCheckBox(othersComposite, label, PREF_ENABLE_EXCLUSION_PATTERNS, enableDisableValues, 0);

		label= PreferencesMessages.JavaBuildConfigurationBlock_enable_multiple_outputlocations_label;
		addCheckBox(othersComposite, label, PREF_ENABLE_MULTIPLE_OUTPUT_LOCATIONS, enableDisableValues, 0);

		label= PreferencesMessages.JavaBuildConfigurationBlock_section_build_path_problems;
		excomposite= createStyleSection(composite, label, nColumns);

		othersComposite= new Composite(excomposite, SWT.NONE);
		excomposite.setClient(othersComposite);
		othersComposite.setLayout(new GridLayout(nColumns, false));

		label= PreferencesMessages.JavaBuildConfigurationBlock_build_invalid_classpath_label;
		addCheckBox(othersComposite, label, PREF_BUILD_INVALID_CLASSPATH, abortIgnoreValues, 0);

		label= PreferencesMessages.JavaBuildConfigurationBlock_pb_incomplete_build_path_label;
		addComboBox(othersComposite, label, PREF_PB_INCOMPLETE_BUILDPATH, errorWarning, errorWarningLabels, 0);

		label= PreferencesMessages.JavaBuildConfigurationBlock_pb_build_path_cycles_label;
		addComboBox(othersComposite, label, PREF_PB_CIRCULAR_BUILDPATH, errorWarning, errorWarningLabels, 0);

		label= PreferencesMessages.JavaBuildConfigurationBlock_pb_check_prereq_binary_level_label;
		addComboBox(othersComposite, label, PREF_PB_INCOMPATIBLE_JDK_LEVEL, errorWarningInfoIgnore, errorWarningInfoIgnoreLabels, 0);

		label = PreferencesMessages.JavaBuildConfigurationBlock_pb_output_overlapping_with_source_label;
		addComboBox(othersComposite, label, PREF_PB_OUTPUT_LOCATION_OVERLAPPING_ANOTHER_SOURCE, errorWarningInfoIgnore, errorWarningInfoIgnoreLabels, 0);

		label= PreferencesMessages.JavaBuildConfigurationBlock_pb_strictly_compatible_jre_not_available_label;
		addComboBox(othersComposite, label, PREF_PB_STRICTLY_COMPATIBLE_JRE_NOT_AVAILABLE, errorWarningInfoIgnore, errorWarningInfoIgnoreLabels, 0);

		label= PreferencesMessages.JavaBuildConfigurationBlock_pb_compiler_compliance_does_not_match_jre_label;
		addComboBox(othersComposite, label, PREF_PB_COMPILER_COMPLIANCE_DOES_NOT_MATCH_JRE, errorWarningInfoIgnore, errorWarningInfoIgnoreLabels, 0);

		label= PreferencesMessages.JavaBuildConfigurationBlock_pb_main_only_project_has_test_only_dependency_label;
		addComboBox(othersComposite, label, PREF_PB_MAIN_ONLY_PROJECT_HAS_TEST_ONLY_DEPENDENCY, errorIgnore, errorIgnoreLabels, 0);

		label= PreferencesMessages.JavaBuildConfigurationBlock_section_output_folder;
		excomposite= createStyleSection(composite, label, nColumns);

		othersComposite= new Composite(excomposite, SWT.NONE);
		excomposite.setClient(othersComposite);
		othersComposite.setLayout(new GridLayout(nColumns, false));

		label= PreferencesMessages.JavaBuildConfigurationBlock_pb_duplicate_resources_label;
		addComboBox(othersComposite, label, PREF_PB_DUPLICATE_RESOURCE, errorWarning, errorWarningLabels, 0);

		label= PreferencesMessages.JavaBuildConfigurationBlock_build_clean_outputfolder_label;
		addCheckBox(othersComposite, label, PREF_BUILD_CLEAN_OUTPUT_FOLDER, cleanIgnoreValues, 0);

		label= PreferencesMessages.JavaBuildConfigurationBlock_build_recreate_modified;
		addCheckBox(othersComposite, label, PREF_RECREATE_MODIFIED_CLASS_FILES, enableIgnoreValues, 0);

		label= PreferencesMessages.JavaBuildConfigurationBlock_resource_filter_label;
		text= addTextField(othersComposite, label, PREF_RESOURCE_FILTER, 0, 0);
		gd= (GridData) text.getLayoutData();
		gd.grabExcessHorizontalSpace= true;
		gd.widthHint= fPixelConverter.convertWidthInCharsToPixels(10);

		Label description= new Label(othersComposite, SWT.WRAP);
		description.setText(PreferencesMessages.JavaBuildConfigurationBlock_resource_filter_description);
		gd= new GridData(GridData.HORIZONTAL_ALIGN_FILL);
		gd.horizontalSpan= nColumns;
		gd.widthHint= fPixelConverter.convertWidthInCharsToPixels(60);
		description.setLayoutData(gd);

		IDialogSettings section= JavaPlugin.getDefault().getDialogSettings().getSection(SETTINGS_SECTION_NAME);
		restoreSectionExpansionStates(section);

		return pageContent;
	}

	/* (non-javadoc)
	 * Update fields and validate.
	 * @param changedKey Key that changed, or null, if all changed.
	 */
	@Override
	protected void validateSettings(Key changedKey, String oldValue, String newValue) {
		if (!areSettingsEnabled()) {
			return;
		}

		if (changedKey != null) {
			if (PREF_PB_MAX_PER_UNIT.equals(changedKey)) {
				fMaxNumberProblemsStatus= validateMaxNumberProblems();
			} else if (PREF_RESOURCE_FILTER.equals(changedKey)) {
				fResourceFilterStatus= validateResourceFilters();
			} else {
				return;
			}
		} else {
			updateEnableStates();
			fMaxNumberProblemsStatus= validateMaxNumberProblems();
			fResourceFilterStatus= validateResourceFilters();
		}
		IStatus status= StatusUtil.getMostSevere(new IStatus[] { fMaxNumberProblemsStatus, fResourceFilterStatus });
		fContext.statusChanged(status);
	}

	private void updateEnableStates() {
	}

	@Override
	protected String[] getFullBuildDialogStrings(boolean workspaceSettings) {
		String title= PreferencesMessages.JavaBuildConfigurationBlock_needsbuild_title;
		String message;
		if (workspaceSettings) {
			message= PreferencesMessages.JavaBuildConfigurationBlock_needsfullbuild_message;
		} else {
			message= PreferencesMessages.JavaBuildConfigurationBlock_needsprojectbuild_message;
		}
		return new String[] { title, message };
	}

	private IStatus validateMaxNumberProblems() {
		String number= getValue(PREF_PB_MAX_PER_UNIT);
		StatusInfo status= new StatusInfo();
		if (number.length() == 0) {
			status.setError(PreferencesMessages.JavaBuildConfigurationBlock_empty_input);
		} else {
			try {
				int value= Integer.parseInt(number);
				if (value <= 0) {
					status.setError(Messages.format(PreferencesMessages.JavaBuildConfigurationBlock_invalid_input, number));
				}
			} catch (NumberFormatException e) {
				status.setError(Messages.format(PreferencesMessages.JavaBuildConfigurationBlock_invalid_input, number));
			}
		}
		return status;
	}

	private IStatus validateResourceFilters() {
		String text= getValue(PREF_RESOURCE_FILTER);

		IWorkspace workspace= ResourcesPlugin.getWorkspace();

		for (String filter : getTokens(text, ",")) { //$NON-NLS-1$
			String fileName= filter.replace('*', 'x');
			int resourceType= IResource.FILE;
			int lastCharacter= fileName.length() - 1;
			if (lastCharacter >= 0 && fileName.charAt(lastCharacter) == '/') {
				fileName= fileName.substring(0, lastCharacter);
				resourceType= IResource.FOLDER;
			}
			IStatus status= workspace.validateName(fileName, resourceType);
			if (status.matches(IStatus.ERROR)) {
				String message= Messages.format(PreferencesMessages.JavaBuildConfigurationBlock_filter_invalidsegment_error, status.getMessage());
				return new StatusInfo(IStatus.ERROR, message);
			}
		}
		return new StatusInfo();
	}

	@Override
	public void dispose() {
		IDialogSettings settings= JavaPlugin.getDefault().getDialogSettings().addNewSection(SETTINGS_SECTION_NAME);
		storeSectionExpansionStates(settings);
		super.dispose();
	}


}
