/*******************************************************************************
 * Copyright (c) 2007-2010 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
 *
 * Contributor:
 *     Red Hat, Inc. - initial API and implementation
 ******************************************************************************/
package org.hibernate.eclipse.launch;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.TitleAreaDialog;
import org.eclipse.jface.viewers.ComboViewer;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ILabelProviderListener;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.DirectoryDialog;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.hibernate.eclipse.console.HibernateConsoleMessages;
import org.hibernate.eclipse.console.model.impl.ExporterFactory;
import org.hibernate.eclipse.console.model.impl.ExporterProperty;
import org.hibernate.eclipse.console.utils.DialogSelectionHelper;
import org.hibernate.util.xpl.StringHelper;

/**
 * Add/edit property dialog to configure Hibernate Exporters.
 */
public class AddPropertyDialog extends TitleAreaDialog {

	private final ExporterFactory ef;
	private final String selectedPropertyId;
	private final boolean flagEdit;
	private ComboViewer propertyCombo;
	private Control value;
	private Button addPathButton;
	private String propertyName;
	private String propertyValue;
	private ModifyListener modifyListener = new ModifyListener() {
				
			public void modifyText(ModifyEvent e) {
				updateStatus();
			}
		
		};

	protected AddPropertyDialog(Shell parentShell, ExporterFactory ef, String selectedPropertyId, boolean flagEdit) {
		super( parentShell );
		this.ef = ef;
		this.selectedPropertyId = selectedPropertyId;
		this.flagEdit = flagEdit;
	}

	protected Control createDialogArea(Composite parent) {
		
		String dialogTitle = HibernateConsoleMessages.AddPropertyDialog_add_exporter_property;
		String editTitle = HibernateConsoleMessages.AddPropertyDialog_add_property_to;
		if (flagEdit) {
			dialogTitle = HibernateConsoleMessages.AddPropertyDialog_edit_exporter_property;
			editTitle = HibernateConsoleMessages.AddPropertyDialog_edit_property_to;
		}
		getShell().setText(dialogTitle);
		setTitle(editTitle + ef.getExporterDefinition().getDescription());
		Composite control = (Composite) super.createDialogArea( parent );

		Composite composite = new Composite(control,SWT.NONE);
		composite.setLayoutData(new GridData(GridData.FILL_BOTH));
		GridLayout layout = new GridLayout(3,false);
		layout.marginHeight = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN);
        layout.marginWidth = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN);
        layout.verticalSpacing = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING);
        layout.horizontalSpacing = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING);
        composite.setLayout(layout);

		Label label = new Label(composite, SWT.NONE);
		label.setText( HibernateConsoleMessages.AddPropertyDialog_name );
		final Combo combo = new Combo(composite, SWT.BORDER | SWT.DROP_DOWN);
		GridData pgd = new GridData(GridData.GRAB_HORIZONTAL | GridData.FILL_HORIZONTAL);
		pgd.horizontalSpan = 2;
		combo.setLayoutData(pgd);
		combo.setFocus();
		combo.addModifyListener( modifyListener );
		propertyCombo = new ComboViewer(combo);
		
		combo.addKeyListener(new KeyListener(){
		
				public void keyPressed(KeyEvent e) {}
			
				public void keyReleased(KeyEvent e) {
					if (e.keyCode == SWT.ARROW_UP || e.keyCode == SWT.ARROW_DOWN) {
						//linux doesn't call selectionChanged event on this events
						propertyCombo.setSelection(propertyCombo.getSelection(), false);
						return;
					}

				    for (int i = 0; i < combo.getItemCount(); i++) {
						if (combo.getText().equals(combo.getItem(i))){
							if (combo.getSelectionIndex() != i){
								combo.select(i);
								propertyCombo.setSelection(propertyCombo.getSelection(), false);
							}								
							return;
						}
				    }
				    disposeBrowseButton();
				    createTextValueComposite(2);
				}
						
			});
					
		if (flagEdit) {
			propertyCombo.getControl().setEnabled(false);
		}

		label = new Label(composite, SWT.NONE);
		label.setText( HibernateConsoleMessages.AddPropertyDialog_value );

		value = new Text(composite, SWT.BORDER);
		value.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL | GridData.FILL_HORIZONTAL));
		((Text)value).addModifyListener( modifyListener );

		initDefaultNames(ef, propertyCombo);

		return control;
	}

	private void initDefaultNames(ExporterFactory ef2, ComboViewer viewer) {
		viewer.setContentProvider( new IStructuredContentProvider() {

			ExporterFactory localEf;

			public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
				localEf = (ExporterFactory) newInput;
			}

			public void dispose() {
				localEf = null;
			}

			public Object[] getElements(Object inputElement) {
				Iterator<Map.Entry<String, ExporterProperty>> set = localEf.getDefaultExporterProperties().entrySet().iterator();
				List<ExporterProperty> values = new ArrayList<ExporterProperty>(4);
				while ( set.hasNext() ) {
					Map.Entry<String, ExporterProperty> element = set.next();
					//if(!localEf.hasLocalValueFor((String) element.getKey())) {
						ExporterProperty exporterProperty = localEf.getExporterProperty( element.getKey() );
						if(exporterProperty!=null) {
							values.add(exporterProperty);
						}
					//}
				}
				return values.toArray( new ExporterProperty[values.size()] );
			}
		});

		viewer.setLabelProvider( new ILabelProvider() {

			public void removeListener(ILabelProviderListener listener) {

			}

			public boolean isLabelProperty(Object element, String property) {
				return false;
			}

			public void dispose() {

			}

			public void addListener(ILabelProviderListener listener) {

			}

			public String getText(Object element) {
				ExporterProperty exporterProperty = ((ExporterProperty)element);
				return exporterProperty.getDescriptionForLabel();
			}

			public Image getImage(Object element) {
				return null;
			}

		} );

		viewer.addSelectionChangedListener( new ISelectionChangedListener() {

			private SelectionListener getSelectionListener(ExporterProperty prop){
				if (!("path".equals(prop.getType()) || "directory".equals(prop.getType()))) return null; //$NON-NLS-1$//$NON-NLS-2$				
				final boolean isPath = "path".equals(prop.getType()); //$NON-NLS-1$
				return new SelectionListener(){
						public void widgetDefaultSelected(SelectionEvent e) {
							widgetSelected(e);
						}
			
						public void widgetSelected(SelectionEvent e) {
							String title = isPath ? HibernateConsoleMessages.ExporterSettingsTab_select_path: HibernateConsoleMessages.ExporterSettingsTab_select_dir;
							String description = isPath ? HibernateConsoleMessages.ExporterSettingsTab_select_path2 : HibernateConsoleMessages.ExporterSettingsTab_select_dir2;
							
							MessageDialog dialog = new MessageDialog(getShell(),
									title,
									null,
									description,	
									MessageDialog.QUESTION,
									new String[] { HibernateConsoleMessages.CodeGenerationSettingsTab_filesystem, HibernateConsoleMessages.CodeGenerationSettingsTab_workspace, IDialogConstants.CANCEL_LABEL},
									1);
							int answer = dialog.open();
							String strPath = null;
							if (answer == 0) { // filesystem
								DirectoryDialog dialog2 = new DirectoryDialog(getShell());
								dialog2.setText(title);
								dialog2.setMessage(description);
								
								String dir = dialog2.open();
								if (dir != null)
								{
									strPath = dir;
								}
							} else if (answer == 1){ // workspace								
								IPath[] paths = DialogSelectionHelper.chooseFileEntries(getShell(), (IPath)null, new Path[0], 
										title, description,
										new String[0], isPath, true, false);
								if (paths != null && paths.length > 0){
									strPath = paths[0].toOSString();
									if (isPath){
										for (int i = 1; i < paths.length; i++) {
											strPath += ';' + paths[i].toOSString();
										}
									}
								}
							} else return;
							String oldPath = ((Text)value).getText();
							if (isPath && oldPath.trim().length() > 0 && strPath != null)
								((Text)value).setText( oldPath + ';' + strPath );
							else {
								if (strPath != null)
									((Text)value).setText( strPath );	
							}
						}
					};
			}

			public void selectionChanged(SelectionChangedEvent event) {
				if(value==null) return;
				IStructuredSelection iss = (IStructuredSelection) event.getSelection();
				if(!iss.isEmpty()) {
				ExporterProperty prop = (ExporterProperty)iss.getFirstElement();
					if ("boolean".equalsIgnoreCase(prop.getType())) {	//$NON-NLS-1$
						disposeBrowseButton();
						createComboValueComposite(new String[]{String.valueOf(true), String.valueOf(false)});
						((Combo)value).select(Boolean.valueOf(ef.getPropertyValue(prop.getName())).booleanValue() ? 0 : 1);
					} else if ("directory".equalsIgnoreCase(prop.getType())//$NON-NLS-1$
							|| "path".equalsIgnoreCase(prop.getType())) {	//$NON-NLS-1$
						disposeBrowseButton();
						createTextValueComposite(1);
						((Text) value).setText(ef.getPropertyValue(prop.getName()));
						createBrowseButton(getSelectionListener(prop), prop);
					} else {
						disposeBrowseButton();
						createTextValueComposite(2);
						((Text) value).setText(ef.getPropertyValue(prop.getName()));
					}
				} else {
					createTextValueComposite(2);
				}
			}
		} );
		viewer.setInput( ef );
		if(viewer.getCombo().getItemCount()>0) {
			Object selected = null;
			if (selectedPropertyId != null) {
				selected = ef.getExporterProperty(selectedPropertyId);
			}
			else {
				selected = viewer.getElementAt( 0 );
			}
			viewer.setSelection(new StructuredSelection(selected));
			viewer.getCombo().select(viewer.getCombo().getSelectionIndex());
		}
	}

	private void disposeBrowseButton(){
		if (addPathButton != null){
			Composite parent = addPathButton.getParent();
			addPathButton.dispose();
			addPathButton = null;
			parent.layout();
		}
	}
 	
	private void createBrowseButton(SelectionListener listener, ExporterProperty prop){
		disposeBrowseButton();
		addPathButton = new Button(value.getParent(), SWT.PUSH);
		if ("path".equals(prop.getType())){ //$NON-NLS-1$
			addPathButton.setText(HibernateConsoleMessages.AddPropertyDialog_add_path);
		} else {
			addPathButton.setText(HibernateConsoleMessages.AddPropertyDialog_browse);
		}		
		addPathButton.setLayoutData(new GridData(GridData.END));
		addPathButton.addSelectionListener(listener);		
		value.getParent().layout();
	}
	
	private void createTextValueComposite(int span){
		if (! (value instanceof Text)) {
			Composite parent = value.getParent();
			if (value != null){
				value.dispose();
			}
			value = new Text(parent, SWT.BORDER | SWT.LEAD | SWT.DROP_DOWN);
			((Text)value).addModifyListener( modifyListener );
			GridData vgd = new GridData(GridData.GRAB_HORIZONTAL | GridData.FILL_HORIZONTAL);
			vgd.horizontalSpan = span;
			value.setLayoutData(vgd);			
		} else {
			((GridData)value.getLayoutData()).horizontalSpan = span;
			((Text)value).setText(""); //$NON-NLS-1$
		}
		value.getParent().layout();
	}
	
	private void createComboValueComposite(String[] items){
		if (!(value instanceof Combo)) {
			Composite parent = value.getParent();
			if (value != null){
				value.dispose();
			}
			value = new Combo(parent, SWT.BORDER | SWT.LEAD | SWT.DROP_DOWN | SWT.READ_ONLY);
			GridData bgd = new GridData(GridData.GRAB_HORIZONTAL | GridData.FILL_HORIZONTAL);
			bgd.horizontalSpan = 2;
			value.setLayoutData(bgd);
			((Combo)value).setItems(items);
			((Combo)value).addModifyListener( modifyListener );
			if (items != null && items.length > 0){
				((Combo)value).select(0);
			}
			parent.layout();
		}
	}
	
	
	void updateStatus() {
		getEnteredValues();

		boolean ok = false;
		if(StringHelper.isEmpty( getPropertyName() )) {
			setMessage( HibernateConsoleMessages.AddPropertyDialog_the_property_name_must_be_chosen_or_entered, IMessageProvider.ERROR);
		} else if (getPropertyName().indexOf( ' ' )>=0 || getPropertyName().indexOf( '\t' )>=0) {
			setMessage( HibernateConsoleMessages.AddPropertyDialog_the_property_name_may_not_contain_whitespaces, IMessageProvider.ERROR);
		} else if(StringHelper.isEmpty( getPropertyValue() )) {
			setMessage( HibernateConsoleMessages.AddPropertyDialog_the_property_value_must_be_non_empty, IMessageProvider.ERROR);
		} else {
			if (!flagEdit && ef.hasLocalValueFor( getPropertyName() )) {
				String out = NLS.bind(HibernateConsoleMessages.AddPropertyDialog_the_property_is_already_set, getPropertyName());
				setMessage(out, IMessageProvider.WARNING);
			} else {
				setMessage(null, IMessageProvider.ERROR);
			}
			ok = true;
		}

		Button button = getButton(IDialogConstants.OK_ID);
		if(button!=null) {
			button.setEnabled( ok );
		}
	}

	public String getPropertyValue() {
		return propertyValue;
	}

	public String getPropertyName() {
		return propertyName;
	}

	void getEnteredValues() {
		if(propertyCombo==null) {
			propertyName = null;
		} else {
			IStructuredSelection selection = (IStructuredSelection) propertyCombo.getSelection();
			if(selection.isEmpty()) {
				propertyName = propertyCombo.getCombo().getText();
			} else {
				ExporterProperty p = (ExporterProperty) selection.getFirstElement();
				propertyName = p.getName();
			}
		}

		if(value!=null) {
			if (value instanceof Text) {
				propertyValue = ((Text) value).getText();
			} else if (value instanceof Combo) {
				propertyValue = ((Combo) value).getText();
			}
		} else {
			propertyValue = null;
		}
	}

	protected void okPressed() {
		getEnteredValues();
		super.okPressed();
	}

	public void create() {
		super.create();
		updateStatus();
	}
}
