package gr.uom.java.jdeodorant.preferences;

import org.eclipse.jface.preference.*;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Group;
import org.eclipse.ui.IWorkbenchPreferencePage;
import org.eclipse.ui.IWorkbench;
import gr.uom.java.jdeodorant.refactoring.Activator;

/**
 * This class represents a preference page that
 * is contributed to the Preferences dialog. By 
 * subclassing <samp>FieldEditorPreferencePage</samp>, we
 * can use the field support built into JFace that allows
 * us to create a page that is small and knows how to 
 * save, restore and apply itself.
 * <p>
 * This page is used to modify preferences only. They
 * are stored in the preference store that belongs to
 * the main plug-in class. That way, preferences can
 * be accessed directly via the preference store.
 */

public class PropertyManagerPreferencePage
	extends FieldEditorPreferencePage
	implements IWorkbenchPreferencePage {

	private IntegerFieldEditor minimumSliceSizeFieldEditor;
	private IntegerFieldEditor maximumSliceSizeFieldEditor;
	private IntegerFieldEditor maximumDuplicationFieldEditor;
	private StringFieldEditor maximumRatioOfDuplicatedToExtractedFieldEditor;
	private IntegerFieldEditor minimumMethodSizeFieldEditor;
	private IntegerFieldEditor maximumCallGraphAnalysisDepthFieldEditor;
	private BooleanFieldEditor enableAliasAnalysisFieldEditor;
	private IntegerFieldEditor projectCompilationUnitCacheSizeFieldEditor;
	private IntegerFieldEditor libraryCompilationUnitCacheSizeFieldEditor;
	private BooleanFieldEditor enableUsageReportingFieldEditor;
	private BooleanFieldEditor enableSourceCodeReportingFieldEditor;
	private IntegerFieldEditor maximumSourceClassAccessedMembersByMoveMethodCandidate;
	private IntegerFieldEditor maximumSourceClassAccessedMembersByExtractClassCandidate;
	
	public PropertyManagerPreferencePage() {
		super(GRID);
		setPreferenceStore(Activator.getDefault().getPreferenceStore());
	}
	
	/**
	 * Creates the field editors. Field editors are abstractions of
	 * the common GUI blocks needed to manipulate various types
	 * of preferences. Each field editor knows how to save and
	 * restore itself.
	 */
	public void createFieldEditors() {
		Composite composite = new Composite(getFieldEditorParent(), SWT.NONE);
		composite.setLayout(new GridLayout(1, false));
		
		Group sliceExtractionPreferenceGroup = new Group(composite, SWT.SHADOW_ETCHED_IN);
		sliceExtractionPreferenceGroup.setLayout(new GridLayout(1, false));
		sliceExtractionPreferenceGroup.setText("Slice Extraction Preferences");
		
		minimumSliceSizeFieldEditor = new IntegerFieldEditor(
				PreferenceConstants.P_MINIMUM_SLICE_SIZE,
				"&Minimum number of slice statements:", sliceExtractionPreferenceGroup);
		minimumSliceSizeFieldEditor.setEmptyStringAllowed(false);
		addField(minimumSliceSizeFieldEditor);
		
		maximumSliceSizeFieldEditor = new IntegerFieldEditor(
				PreferenceConstants.P_MAXIMUM_SLICE_SIZE,
				"&Maximum number of slice statements (method size - n):", sliceExtractionPreferenceGroup);
		maximumSliceSizeFieldEditor.setEmptyStringAllowed(false);
		addField(maximumSliceSizeFieldEditor);
		
		maximumDuplicationFieldEditor = new IntegerFieldEditor(
				PreferenceConstants.P_MAXIMUM_DUPLICATION,
				"&Maximum number of duplicated statements:", sliceExtractionPreferenceGroup);
		maximumDuplicationFieldEditor.setEmptyStringAllowed(false);
		addField(maximumDuplicationFieldEditor);
		
		maximumRatioOfDuplicatedToExtractedFieldEditor = new StringFieldEditor(
				PreferenceConstants.P_MAXIMUM_RATIO_OF_DUPLICATED_TO_EXTRACTED,
				"&Maximum ratio of duplicated to extracted statements:", sliceExtractionPreferenceGroup);
		maximumRatioOfDuplicatedToExtractedFieldEditor.setEmptyStringAllowed(false);
		addField(maximumRatioOfDuplicatedToExtractedFieldEditor);
		
		minimumMethodSizeFieldEditor = new IntegerFieldEditor(
				PreferenceConstants.P_MINIMUM_METHOD_SIZE,
				"&Minimum number of statements in method:", sliceExtractionPreferenceGroup);
		minimumMethodSizeFieldEditor.setEmptyStringAllowed(false);
		addField(minimumMethodSizeFieldEditor);
		
		Group featureEnvyPreferenceGroup = new Group(composite, SWT.SHADOW_ETCHED_IN);
		featureEnvyPreferenceGroup.setLayout(new GridLayout(1, false));
		featureEnvyPreferenceGroup.setText("Feature Envy Preferences");
		
		maximumSourceClassAccessedMembersByMoveMethodCandidate = new IntegerFieldEditor(
				PreferenceConstants.P_MAXIMUM_NUMBER_OF_SOURCE_CLASS_MEMBERS_ACCESSED_BY_MOVE_METHOD_CANDIDATE,
				"&Maximum number of source class members accessed by a Move Method Candidate:", featureEnvyPreferenceGroup);
		maximumSourceClassAccessedMembersByMoveMethodCandidate.setEmptyStringAllowed(false);
		addField(maximumSourceClassAccessedMembersByMoveMethodCandidate);

		Group godClassPreferenceGroup = new Group(composite, SWT.SHADOW_ETCHED_IN);
		godClassPreferenceGroup.setLayout(new GridLayout(1, false));
		godClassPreferenceGroup.setText("God Class Preferences");
		
		maximumSourceClassAccessedMembersByExtractClassCandidate = new IntegerFieldEditor(
				PreferenceConstants.P_MAXIMUM_NUMBER_OF_SOURCE_CLASS_MEMBERS_ACCESSED_BY_EXTRACT_CLASS_CANDIDATE,
				"&Maximum number of source class members accessed by an Extract Class Candidate:", godClassPreferenceGroup);
		maximumSourceClassAccessedMembersByExtractClassCandidate.setEmptyStringAllowed(false);
		addField(maximumSourceClassAccessedMembersByExtractClassCandidate);
		
		Group callGraphAnalysisPreferenceGroup = new Group(composite, SWT.SHADOW_ETCHED_IN);
		callGraphAnalysisPreferenceGroup.setLayout(new GridLayout(1, false));
		callGraphAnalysisPreferenceGroup.setText("Call Graph Analysis Preferences");
		
		enableAliasAnalysisFieldEditor = new BooleanFieldEditor(
				PreferenceConstants.P_ENABLE_ALIAS_ANALYSIS,
				"&Enable Alias Analysis", callGraphAnalysisPreferenceGroup);
		addField(enableAliasAnalysisFieldEditor);
		
		maximumCallGraphAnalysisDepthFieldEditor = new IntegerFieldEditor(
				PreferenceConstants.P_MAXIMUM_CALL_GRAPH_ANALYSIS_DEPTH,
				"&Maximum depth of method call graph analysis:", callGraphAnalysisPreferenceGroup);
		maximumCallGraphAnalysisDepthFieldEditor.setEmptyStringAllowed(false);
		addField(maximumCallGraphAnalysisDepthFieldEditor);
		
		Group compilationUnitCachePreferenceGroup = new Group(composite, SWT.SHADOW_ETCHED_IN);
		compilationUnitCachePreferenceGroup.setLayout(new GridLayout(1, false));
		compilationUnitCachePreferenceGroup.setText("CompilationUnit Cache Preferences");
		
		projectCompilationUnitCacheSizeFieldEditor = new IntegerFieldEditor(
				PreferenceConstants.P_PROJECT_COMPILATION_UNIT_CACHE_SIZE,
				"&Project CompilationUnit cache size:", compilationUnitCachePreferenceGroup);
		projectCompilationUnitCacheSizeFieldEditor.setEmptyStringAllowed(false);
		addField(projectCompilationUnitCacheSizeFieldEditor);

		libraryCompilationUnitCacheSizeFieldEditor = new IntegerFieldEditor(
				PreferenceConstants.P_LIBRARY_COMPILATION_UNIT_CACHE_SIZE,
				"&Library CompilationUnit cache size:", compilationUnitCachePreferenceGroup);
		libraryCompilationUnitCacheSizeFieldEditor.setEmptyStringAllowed(false);
		addField(libraryCompilationUnitCacheSizeFieldEditor);
		
		Group usageReportingGroup = new Group(composite, SWT.SHADOW_ETCHED_IN);
		usageReportingGroup.setLayout(new GridLayout(1, false));
		usageReportingGroup.setText("Usage Reporting");
		
		enableUsageReportingFieldEditor = new BooleanFieldEditor(
				PreferenceConstants.P_ENABLE_USAGE_REPORTING,
				"&Allow the JDeodorant team to receive your ratings and refactoring applications", usageReportingGroup);
		addField(enableUsageReportingFieldEditor);
		
		enableSourceCodeReportingFieldEditor = new BooleanFieldEditor(
				PreferenceConstants.P_ENABLE_SOURCE_CODE_REPORTING,
				"&Allow the JDeodorant team to receive source code related information", usageReportingGroup);
		addField(enableSourceCodeReportingFieldEditor);
	}

	protected void checkState() {
		super.checkState();
		try {
			int minimumSliceSize = minimumSliceSizeFieldEditor.getIntValue();
			if(minimumSliceSize >= 0) {
				setErrorMessage(null);
				setValid(true);
			}
			else {
				setErrorMessage("Minimum number of slice statements must be >= 0");
				setValid(false);
				return;
			}
		}
		catch(NumberFormatException e) {
			setErrorMessage("Minimum number of slice statements must be an Integer");
			setValid(false);
			return;
		}
		try {
			int maximumSliceSize = maximumSliceSizeFieldEditor.getIntValue();
			if(maximumSliceSize >= 0) {
				setErrorMessage(null);
				setValid(true);
			}
			else {
				setErrorMessage("Maximum number of slice statements must be >= 0");
				setValid(false);
				return;
			}
		}
		catch(NumberFormatException e) {
			setErrorMessage("Maximum number of slice statements must be an Integer");
			setValid(false);
			return;
		}
		try {
			int maximumDuplication = maximumDuplicationFieldEditor.getIntValue();
			if(maximumDuplication >= 0) {
				setErrorMessage(null);
				setValid(true);
			}
			else {
				setErrorMessage("Maximum number of duplicated statements must be >= 0");
				setValid(false);
				return;
			}
		}
		catch(NumberFormatException e) {
			setErrorMessage("Maximum number of duplicated statements must be an Integer");
			setValid(false);
			return;
		}
		try {
			String ratio = maximumRatioOfDuplicatedToExtractedFieldEditor.getStringValue();
			double r = Double.parseDouble(ratio);
			if(r >= 0.0 && r <= 1.0) {
				setErrorMessage(null);
				setValid(true);
			}
			else {
				setErrorMessage("Duplication ratio must be a Double within range [0, 1]");
				setValid(false);
				return;
			}
		}
		catch(NumberFormatException e) {
			setErrorMessage("Duplication ratio must be a Double");
			setValid(false);
			return;
		}
		try {
			int minimumMethodSize = minimumMethodSizeFieldEditor.getIntValue();
			if(minimumMethodSize >= 0) {
				setErrorMessage(null);
				setValid(true);
			}
			else {
				setErrorMessage("Minimum number of statements in method must be >= 0");
				setValid(false);
				return;
			}
		}
		catch(NumberFormatException e) {
			setErrorMessage("Minimum number of statements in method must be an Integer");
			setValid(false);
			return;
		}
		try {
			int maximumCallGraphAnalysisDepth = maximumCallGraphAnalysisDepthFieldEditor.getIntValue();
			if(maximumCallGraphAnalysisDepth >= 0) {
				setErrorMessage(null);
				setValid(true);
			}
			else {
				setErrorMessage("Maximum depth of call graph analysis must be >= 0");
				setValid(false);
				return;
			}
		}
		catch(NumberFormatException e) {
			setErrorMessage("Maximum depth of call graph analysis must be an Integer");
			setValid(false);
			return;
		}
		try {
			int projectCompilationUnitCacheSize = projectCompilationUnitCacheSizeFieldEditor.getIntValue();
			if(projectCompilationUnitCacheSize >= 10) {
				setErrorMessage(null);
				setValid(true);
			}
			else {
				setErrorMessage("Cache size is recommended to be >= 10");
				setValid(false);
				return;
			}
		}
		catch(NumberFormatException e) {
			setErrorMessage("Cache size must be an Integer");
			setValid(false);
			return;
		}
		try {
			int libraryCompilationUnitCacheSize = libraryCompilationUnitCacheSizeFieldEditor.getIntValue();
			if(libraryCompilationUnitCacheSize >= 20) {
				setErrorMessage(null);
				setValid(true);
			}
			else {
				setErrorMessage("Cache size is recommended to be >= 20");
				setValid(false);
				return;
			}
		}
		catch(NumberFormatException e) {
			setErrorMessage("Cache size must be an Integer");
			setValid(false);
			return;
		}
		try {
			int sourceClassAccessedMembers = maximumSourceClassAccessedMembersByMoveMethodCandidate.getIntValue();
			if(sourceClassAccessedMembers >= 0) {
				setErrorMessage(null);
				setValid(true);
			}
			else {
				setErrorMessage("Maximum number of accessed source class members must be >= 0");
				setValid(false);
				return;
			}
		}
		catch(NumberFormatException e) {
			setErrorMessage("Maximum number of accessed source class members must be an Integer");
			setValid(false);
			return;
		}
		try {
			int sourceClassAccessedMembers = maximumSourceClassAccessedMembersByExtractClassCandidate.getIntValue();
			if(sourceClassAccessedMembers >= 0) {
				setErrorMessage(null);
				setValid(true);
			}
			else {
				setErrorMessage("Maximum number of accessed source class members must be >= 0");
				setValid(false);
				return;
			}
		}
		catch(NumberFormatException e) {
			setErrorMessage("Maximum number of accessed source class members must be an Integer");
			setValid(false);
			return;
		}
	}

	public void propertyChange(PropertyChangeEvent event) {
		super.propertyChange(event);
		if(event.getProperty().equals(FieldEditor.VALUE)) {
			checkState();
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.ui.IWorkbenchPreferencePage#init(org.eclipse.ui.IWorkbench)
	 */
	public void init(IWorkbench workbench) {
	}
	
}