/******************************************************************************* 
 * Copyright (c) 2012 Red Hat, Inc. 
 * Distributed under license by Red Hat, Inc. All rights reserved. 
 * This program is 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: 
 * Red Hat, Inc. - initial API and implementation 
 ******************************************************************************/

package org.jboss.tools.common.ui.wizard.service;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.search.IJavaSearchConstants;
import org.eclipse.jdt.core.search.SearchEngine;
import org.eclipse.jdt.internal.ui.IJavaHelpContextIds;
import org.eclipse.jdt.internal.ui.dialogs.OpenTypeSelectionDialog;
import org.eclipse.jdt.internal.ui.dialogs.StatusInfo;
import org.eclipse.jdt.internal.ui.dialogs.StatusUtil;
import org.eclipse.jdt.internal.ui.wizards.dialogfields.SelectionButtonDialogFieldGroup;
import org.eclipse.jdt.ui.wizards.NewClassWizardPage;
import org.eclipse.jdt.ui.wizards.NewTypeWizardPage;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.PlatformUI;
import org.jboss.tools.common.ui.CommonUIMessages;
import org.jboss.tools.common.ui.CommonUIPlugin;
import org.jboss.tools.common.ui.IValidator;
import org.jboss.tools.common.ui.CommonUIImages;
import org.jboss.tools.common.ui.widget.editor.ButtonFieldEditor.ButtonPressedAction;
import org.jboss.tools.common.ui.widget.editor.IFieldEditor;
import org.jboss.tools.common.ui.widget.editor.SwtFieldEditorFactory;
import org.jboss.tools.common.util.EclipseJavaUtil;

/**
 * 
 * @author Viacheslav Kabanovich
 * 
 */
public class NewServiceWizardPage extends NewClassWizardPage {
	String defaultTypeName = null;

	protected StatusInfo interfaceNameStatus = new StatusInfo();

	IFieldEditor interfaceField;

	public NewServiceWizardPage() {
		setTitle(CommonUIMessages.NEW_SERVICE_WIZARD_PAGE_NAME);
		setDescription(CommonUIMessages.NEW_SERVICE_WIZARD_DESCRIPTION);
		setImageDescriptor(CommonUIImages.getInstance().getOrCreateImageDescriptor(CommonUIImages.NEW_JAVA_SERVICE_PROVIDER_IMAGE));
	}

	public void init(IStructuredSelection selection) {
		super.init(selection);
		defaultTypeName = null;
		if (selection != null && !selection.isEmpty()) {
			Object o = selection.iterator().next();
			IType type = null;
			if (o instanceof IType) {
				type = (IType) o;
			} else if (o instanceof ICompilationUnit) {
				ICompilationUnit cu = (ICompilationUnit) o;
				try {
					IType[] ts = cu.getTypes();
					if (ts != null && ts.length > 0)
						type = ts[0];
				} catch (JavaModelException e) {
					CommonUIPlugin.getDefault().logError(e);
				}

			}
			boolean isInterface = false;
			try {
				isInterface = type != null && type.isInterface();
			} catch (JavaModelException e) {
				CommonUIPlugin.getDefault().logError(e);
			}
			if (isInterface) {
				ArrayList<String> interfacesNames = new ArrayList<String>();
				String name = "";
				try {
					name = type.getFullyQualifiedParameterizedName();
				} catch (JavaModelException e) {
					name = type.getFullyQualifiedName();
				}
				interfacesNames.add(name);
				setSuperInterfaces(interfacesNames, true);
				superInterfacesChanged();
				setDefaultTypeName(name);
			}
		}
		setModifiers(getModifiers(), true);
		try {
			Field f = NewTypeWizardPage.class.getDeclaredField("fOtherMdfButtons");
			if(f != null) {
				f.setAccessible(true);
				SelectionButtonDialogFieldGroup g = (SelectionButtonDialogFieldGroup)f.get(this);
				g.enableSelectionButton(0, false);
			}
		} catch (Exception e) {
			CommonUIPlugin.getDefault().logError(e);
		}
		doStatusUpdate();
	}

	void setDefaultTypeName(String interfaceName) {
		int d = interfaceName.lastIndexOf('.');
		int b = interfaceName.indexOf('<');
		if (b < 0)
			b = interfaceName.length();
		String elementName = interfaceName.substring(d + 1, b);
		String typeName = elementName + "Service";
//		typeName += interfaceName.substring(b);

		String currentTypeName = getTypeName();
		boolean isDefault = currentTypeName == null || currentTypeName.length() == 0 || currentTypeName.equals(defaultTypeName);
		if(isDefault)  {
			setTypeName(typeName, true);
			typeNameChanged();
		}
		defaultTypeName = typeName;
	}

	public void createControl(Composite parent) {
		initializeDialogUnits(parent);

		Composite composite = new Composite(parent, SWT.NONE);
		composite.setFont(parent.getFont());

		int nColumns = 4;

		GridLayout layout = new GridLayout();
		layout.numColumns = nColumns;
		composite.setLayout(layout);

		// pick & choose the wanted UI components

		createContainerControls(composite, nColumns);
		createPackageControls(composite, nColumns);
		// createEnclosingTypeControls(composite, nColumns);

		createSeparator(composite, nColumns);

		createTypeNameControls(composite, nColumns);
		createModifierControls(composite, nColumns);

		// createSuperClassControls(composite, nColumns);
		// createSuperInterfacesControls(composite, nColumns);

		// createMethodStubSelectionControls(composite, nColumns);

		createCustomFields(composite);

		createCommentControls(composite, nColumns);
		enableCommentControl(true);

		setControl(composite);

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

		onInterfaceChange();
		doStatusUpdate();
	}

	protected void createTypeMembers(IType newType,
			final ImportsManager imports, IProgressMonitor monitor)
			throws CoreException {
		createInheritedMethods(newType, true, true, imports,
				new SubProgressMonitor(monitor, 1));
	}

	protected void createCustomFields(Composite composite) {
		createInterfaceField(composite);
	}

	protected void createInterfaceField(Composite composite) {
		interfaceField = SwtFieldEditorFactory.INSTANCE.createButtonFieldEditor("Interface", 
			CommonUIMessages.NEW_SERVICE_WIZARD_INTERFACES_LABEL, "", 
			new ButtonPressedAction(CommonUIMessages.SWT_FIELD_EDITOR_FACTORY_BROWS) {
				public void run() {
					OpenTypeSelectionDialog dialog = new OpenTypeSelectionDialog(getShell(), false, getContainer(),
							getJavaProject() != null
							? SearchEngine.createJavaSearchScope(new IJavaProject[] {getJavaProject()})
							: SearchEngine.createJavaSearchScope(new IJavaProject[0]), 
							IJavaSearchConstants.CLASS_AND_INTERFACE);
					dialog.setTitle(CommonUIMessages.NEW_SERVICE_WIZARD_SELECT_SERVICE_TYPE_TITLE);
					if(dialog.open() == IDialogConstants.OK_ID) {
						Object[] os = dialog.getResult();
						if(os != null && os.length == 1) {
							IType t = (IType)os[0];
							interfaceField.setValue(t.getFullyQualifiedName());
						}
					}
				}
		    }, 
			new IValidator() {
				@Override
				public Map<String, IStatus> validate(Object value, Object context) {
					return null;
				}
			});
		List<String> s = getSuperInterfaces();
		if(!s.isEmpty()) {
			setServiceType(s.get(0));
		}
		interfaceField.doFillIntoGrid(composite);
		interfaceField.addPropertyChangeListener(new PropertyChangeListener() {
			public void propertyChange(PropertyChangeEvent evt) {
				onInterfaceChange();
			}
		});
	}

	public void setServiceType(String typeName) {
		interfaceField.setValue(typeName);
	}

	protected void onInterfaceChange() {
		interfaceNameStatus = new StatusInfo();
		String q = interfaceField == null ? "" : interfaceField.getValue().toString().trim();
		
		boolean isInterface = false, isClass = false;

		if(getJavaProject() != null && q.length() > 0) {
			String qRaw = q;
			if(qRaw.indexOf('<') > 0) {
				qRaw = qRaw.substring(0, qRaw.indexOf('<'));
			}
			try {
				IType t = EclipseJavaUtil.findType(getJavaProject(), qRaw);
				if(t != null) {
					isInterface = t.isInterface();
					isClass = !t.isInterface();
					if(Flags.isFinal(t.getFlags())) {
						interfaceNameStatus.setError(CommonUIMessages.NEW_SERVICE_WIZARD_SERVICE_TYPE_FINAL);
					} else if(isClass && !Flags.isAbstract(t.getFlags())) {
						interfaceNameStatus.setWarning(CommonUIMessages.NEW_SERVICE_WIZARD_SERVICE_TYPE_CONCRETE);
					}
				} else {
					isInterface = true;
					interfaceNameStatus.setError(NLS.bind(CommonUIMessages.NEW_SERVICE_WIZARD_SERVICE_TYPE_NOT_EXISTS, q));
				}
			} catch (JavaModelException e) {
				CommonUIPlugin.getDefault().logError(e);
				interfaceNameStatus.setError(NLS.bind(CommonUIMessages.NEW_SERVICE_WIZARD_SERVICE_TYPE_NOT_EXISTS, q));
			}
		}
		if(q.length() == 0) {
			interfaceNameStatus.setError(CommonUIMessages.NEW_SERVICE_WIZARD_SERVICE_TYPE_EMPTY);
		}
		if(isInterface) {
			List<String> is = new ArrayList<String>();
			if(q.length() > 0) is.add(q.trim());
			setSuperInterfaces(is, true);
			setSuperClass("", true);
		} else if(isClass) {
			setSuperInterfaces(new ArrayList<String>(), true);
			setSuperClass(q, true);
		}
		
		doStatusUpdate();
	}

	@Override
	protected IStatus superInterfacesChanged() {
		List list = getSuperInterfaces();
		if(list != null && !list.isEmpty()) {
			setDefaultTypeName(list.get(0).toString());
		}
		StatusInfo result = (StatusInfo) super.superInterfacesChanged();
		return result;
	}

	@Override
	protected IStatus superClassChanged() {
		String n = getSuperClass();
		if(n != null && n.length() > 0 && !n.endsWith("Object")) {
			setDefaultTypeName(n);
		}
		StatusInfo result = (StatusInfo) super.superClassChanged();
		return result;
	}

	private void doStatusUpdate() {
		// status of all used components
		IStatus[] status = new IStatus[] {
				fContainerStatus,
				isEnclosingTypeSelected() ? fEnclosingTypeStatus
						: fPackageStatus, fTypeNameStatus, fModifierStatus,
				fSuperClassStatus, fSuperInterfacesStatus };

		// the mode severe status will be displayed and the OK button
		// enabled/disabled.
		updateStatus(status);
	}

	protected void updateStatus(IStatus[] status) {
		if(!CommonUIMessages.NEW_SERVICE_WIZARD_DESCRIPTION.equals(getDescription())) {
			setDescription(CommonUIMessages.NEW_SERVICE_WIZARD_DESCRIPTION);
		}
		IStatus[] ns = new IStatus[status.length + 1];
		System.arraycopy(status, 0, ns, 0, status.length);
		ns[status.length] = interfaceNameStatus;
		status = ns;
		updateStatus(StatusUtil.getMostSevere(status));
	}
	
	public boolean isToBeRegisteredInMetaInf() {
		return true;
	}

	@Override
	public void setVisible(boolean visible) {
		if(!getControl().isVisible() && visible && fSuperInterfacesStatus.matches(IStatus.ERROR) && !fTypeNameStatus.matches(IStatus.ERROR)) {
			setDescription(fSuperInterfacesStatus.getMessage());
		}
		super.setVisible(visible);
	}

	protected String getSuperInterfacesLabel() {
		return CommonUIMessages.NEW_SERVICE_WIZARD_INTERFACES_LABEL;
	}

	public String getServiceRawType() {
		return (interfaceField == null) ? "" : toRawTypeName(interfaceField.getValueAsString());
	}

	private static String toRawTypeName(String typeName) {
		return typeName.indexOf('<') >= 0 ? typeName.substring(0, typeName.indexOf('<')) : typeName;
	}
}
