/*************************************************************************************
 * Copyright (c) 2008-2011 Red Hat, Inc. and others.
 * All rights reserved. This program and the accompanying materials 
 * are 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:
 *     JBoss by Red Hat - Initial implementation.
 ************************************************************************************/
package org.jboss.tools.seam.ui.dialog;

import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.resources.ProjectScope;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.core.runtime.preferences.IScopeContext;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.TitleAreaDialog;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.CheckboxTableViewer;
import org.eclipse.jface.viewers.IBaseLabelProvider;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.ListViewer;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.wizard.Wizard;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Font;
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.Display;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.IOverwriteQuery;
import org.eclipse.ui.wizards.datatransfer.FileSystemStructureProvider;
import org.eclipse.ui.wizards.datatransfer.IImportStructureProvider;
import org.eclipse.ui.wizards.datatransfer.ImportOperation;
import org.eclipse.wst.common.componentcore.ComponentCore;
import org.eclipse.wst.common.componentcore.resources.IVirtualComponent;
import org.eclipse.wst.common.componentcore.resources.IVirtualFolder;
import org.eclipse.wst.common.project.facet.core.IFacetedProject;
import org.eclipse.wst.common.project.facet.core.IProjectFacetVersion;
import org.jboss.tools.seam.core.SeamCorePlugin;
import org.jboss.tools.seam.core.project.facet.SeamRuntime;
import org.jboss.tools.seam.core.project.facet.SeamRuntimeManager;
import org.jboss.tools.seam.core.project.facet.SeamVersion;
import org.jboss.tools.seam.internal.core.project.facet.ISeamFacetDataModelProperties;
import org.jboss.tools.seam.ui.widget.editor.SeamRuntimeListFieldEditor.SeamRuntimeNewWizard;
import org.jboss.tools.seam.ui.wizard.SeamFormWizard;
import org.jboss.tools.seam.ui.wizard.SeamWizardUtils;

/**
 * @author snjeza
 * 
 */
public class SeamFacetVersionChangeDialog extends TitleAreaDialog {
	private IProjectFacetVersion fv;
	private IEclipsePreferences preferences;
	private IProject warProject;
	private Image _dlgTitleImage;
	private Combo seamRuntimeCombo;
	private Text seamRuntimeNameText;
	private CheckboxTableViewer removed;
	private ListViewer added;
	private ListViewer addedEar;
	private CheckboxTableViewer removedEar;
	private Button updateLibs;
	private static IOverwriteQuery OVERWRITE_ALL = new IOverwriteQuery() {
		public String queryOverwrite(String file) {
			return ALL;
		}	
	};
	private static String[] filePattern = { "jboss-seam", "antlr-", //$NON-NLS-1$ //$NON-NLS-2$
			"commons-beanutils", "commons-digester", "commons-jci" , //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
			"commons-collections", //$NON-NLS-1$
			"core.", "drools-", "ajax4jsf",  //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
			"jboss-el", "jbpm", "jsf-facelets", "jxl.jar",  //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
			"mvel", "richfaces" , //$NON-NLS-1$ //$NON-NLS-2$
			"janino", "el-ri", "stringtemplate", "oscache-"}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$

	/**
	 * @param parentShell
	 * @param newWizard
	 */
	public SeamFacetVersionChangeDialog(Shell parentShell,
			IFacetedProject facetedProject, IProjectFacetVersion fv) {
		super(parentShell);
		Assert.isNotNull(facetedProject);
		setShellStyle(SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL | SWT.MAX
				| SWT.TITLE | SWT.BORDER | SWT.RESIZE | getDefaultOrientation());
		this.fv = fv;
		warProject = SeamWizardUtils.getRootSeamProject(facetedProject
				.getProject());
		Assert.isNotNull(warProject);
		preferences = SeamCorePlugin.getSeamPreferences(warProject);
		_dlgTitleImage = ImageDescriptor.createFromFile(SeamFormWizard.class,
				"SeamWebProjectWizBan.png").createImage(); //$NON-NLS-1$
		setTitleImage(_dlgTitleImage);
	}

	@Override
	protected Control createDialogArea(Composite parent) {
		Font mainFont = parent.getFont();
		Composite area = (Composite) super.createDialogArea(parent);
		Composite contents = new Composite(area, SWT.NONE);
		GridData gd = new GridData(GridData.FILL_BOTH);
		// gd.heightHint = 300;
		contents.setLayoutData(gd);
		contents.setLayout(new GridLayout(3, false));
		setTitle(Messages.SeamFacetVersionChangeDialog_Seam_Runtime_Settings);
		setMessage(Messages.SeamFacetVersionChangeDialog_Set_Seam_Runtime);
		getShell().setText(Messages.SeamFacetVersionChangeDialog_Seam_Runtime_Settings);
		applyDialogFont(contents);
		initializeDialogUnits(area);

		Label oldSeamRuntimeLabel = new Label(contents, SWT.NONE);
		oldSeamRuntimeLabel.setText(Messages.SeamFacetVersionChangeDialog_Old_Seam_Runtime);
		String seamRuntimeName = ""; //$NON-NLS-1$
		if (preferences != null) {
			seamRuntimeName = preferences.get(
					ISeamFacetDataModelProperties.SEAM_RUNTIME_NAME, ""); //$NON-NLS-1$
		}
		seamRuntimeNameText = new Text(contents, SWT.BORDER);
		seamRuntimeNameText.setText(seamRuntimeName);
		gd = new GridData(GridData.FILL_HORIZONTAL);
		gd.horizontalSpan = 2;
		seamRuntimeNameText.setEnabled(false);
		seamRuntimeNameText.setLayoutData(gd);

		Label seamRuntimeLabel = new Label(contents, SWT.NONE);
		seamRuntimeLabel.setText(Messages.SeamFacetVersionChangeDialog_New_Seam_Runtime);
		seamRuntimeCombo = new Combo(contents, SWT.READ_ONLY);
		gd = new GridData(GridData.FILL_HORIZONTAL);
		seamRuntimeCombo.setLayoutData(gd);
		final SeamVersion version = refreshSeamRuntimeCombo();

		Button addSeamRuntime = new Button(contents, SWT.PUSH);
		addSeamRuntime.setText(Messages.SeamFacetVersionChangeDialog_Add);
		addSeamRuntime.addSelectionListener(new SelectionAdapter() {

			@Override
			public void widgetSelected(SelectionEvent e) {
				List<SeamRuntime> added = new ArrayList<SeamRuntime>();
				List<SeamVersion> versions = new ArrayList<SeamVersion>();
				versions.add(version);
				Wizard wiz = new SeamRuntimeNewWizard(
						(List<SeamRuntime>) new ArrayList<SeamRuntime>(Arrays
								.asList(SeamRuntimeManager.getInstance()
										.getRuntimes())), added, versions);
				WizardDialog dialog = new WizardDialog(Display.getCurrent()
						.getActiveShell(), wiz);
				int ok = dialog.open();
				if (ok == Dialog.OK && !added.isEmpty()) {
					SeamRuntimeManager.getInstance().addRuntime(added.get(0));
					refreshSeamRuntimeCombo();
				}
				refresh();
			}

		});

		Group libraryGroup = new Group(contents, SWT.NONE);
		libraryGroup.setText(Messages.SeamFacetVersionChangeDialog_Libraries);
		gd = new GridData(GridData.FILL_BOTH);
		gd.horizontalSpan = 3;
		libraryGroup.setLayoutData(gd);
		libraryGroup.setLayout(new GridLayout(2, false));
		updateLibs = new Button(libraryGroup, SWT.CHECK);
		gd = new GridData(GridData.FILL_HORIZONTAL);
		gd.horizontalSpan = 2;
		updateLibs.setLayoutData(gd);
		updateLibs.setText(Messages.SeamFacetVersionChangeDialog_Update_libraries);
		updateLibs.setSelection(false);

		Label warProjectLabel = new Label(libraryGroup,SWT.NONE);
		warProjectLabel.setText(NLS.bind(Messages.SeamFacetVersionChangeDialog_Project, warProject.getName()));
		gd = new GridData(GridData.FILL_HORIZONTAL | GridData.CENTER);
		gd.horizontalSpan=2;
		warProjectLabel.setLayoutData(gd);
		addLabels(libraryGroup);
		
		removed = CheckboxTableViewer.newCheckList(
				libraryGroup, SWT.SINGLE | SWT.TOP | SWT.BORDER);
		gd = new GridData(GridData.FILL_BOTH);
		gd.heightHint = 150;
		removed.getTable().setLayoutData(gd);
		removed.getTable().setFont(mainFont);
		IBaseLabelProvider labelProvider = new FileSetLabelProvider();
		removed.setLabelProvider(labelProvider);
		File webLibFolder = getWebLibFolder();

		IStructuredContentProvider rContentProvider = new RemovedFileSetProvider();
		removed.setContentProvider(rContentProvider);
		removed.setInput(webLibFolder);
		removed.getTable().setEnabled(false);

		removed.addCheckStateListener(new ICheckStateListener() {
			public void checkStateChanged(CheckStateChangedEvent event) {
				removed
						.setSelection(new StructuredSelection(event
								.getElement()));
			}
		});

		check(removed, rContentProvider);
		added = new ListViewer(libraryGroup, SWT.V_SCROLL
				| SWT.H_SCROLL | SWT.BORDER);
		gd = new GridData(GridData.FILL_BOTH);
		gd.heightHint = 150;
		added.getList().setLayoutData(gd);
		added.getList().setFont(mainFont);
		added.setLabelProvider(labelProvider);
		SeamVersion newVersion = null;
		if (seamRuntimeCombo.getText().trim().length() > 0) {
			newVersion = SeamRuntimeManager.getInstance().findRuntimeByName(
					seamRuntimeCombo.getText()).getVersion();
		} else {
			newVersion = SeamVersion.findByString(fv.getVersionString());
		}
		IStructuredContentProvider aContentProvider = new WarFileSetProvider(
				isWarConfiguration(), newVersion);
		added.setContentProvider(aContentProvider);

		added.getList().setEnabled(false);

		File earContentsFolder = getEarContentsFolder();
		if (earContentsFolder != null && earContentsFolder.isDirectory()) {
			Label earProjectLabel = new Label(libraryGroup,SWT.NONE);
			String earProjectName = preferences.get(ISeamFacetDataModelProperties.SEAM_EAR_PROJECT, ""); //$NON-NLS-1$
			earProjectLabel.setText(NLS.bind(Messages.SeamFacetVersionChangeDialog_Project, earProjectName));
			gd = new GridData(GridData.FILL_HORIZONTAL | GridData.CENTER);
			gd.horizontalSpan=2;
			earProjectLabel.setLayoutData(gd);
			addLabels(libraryGroup);
			removedEar = CheckboxTableViewer
					.newCheckList(libraryGroup, SWT.SINGLE | SWT.TOP
							| SWT.BORDER);
			gd = new GridData(GridData.FILL_BOTH);
			gd.heightHint = 150;
			removedEar.getTable().setLayoutData(gd);
			removedEar.getTable().setFont(mainFont);
			removedEar.setLabelProvider(labelProvider);
			
			IStructuredContentProvider rEarContentProvider = new RemovedFileSetProvider();
			removedEar.setContentProvider(rEarContentProvider);
			removedEar.setInput(earContentsFolder);
			removedEar.getTable().setEnabled(false);

			removedEar.addCheckStateListener(new ICheckStateListener() {
				public void checkStateChanged(CheckStateChangedEvent event) {
					removedEar.setSelection(new StructuredSelection(event
							.getElement()));
				}
			});

			check(removedEar, rEarContentProvider);
			addedEar = new ListViewer(libraryGroup,
					SWT.V_SCROLL | SWT.H_SCROLL | SWT.BORDER);
			gd = new GridData(GridData.FILL_BOTH);
			gd.heightHint = 150;
			addedEar.getList().setLayoutData(gd);
			addedEar.getList().setFont(mainFont);
			addedEar.setLabelProvider(labelProvider);
			IStructuredContentProvider aEarContentProvider = new EarFileSetProvider(
					newVersion);
			addedEar.setContentProvider(aEarContentProvider);

			addedEar.getList().setEnabled(false);
			
		}
		updateLibs.addSelectionListener(new SelectionAdapter() {

			@Override
			public void widgetSelected(SelectionEvent e) {
				added.getList().setEnabled(updateLibs.getSelection());
				removed.getTable().setEnabled(updateLibs.getSelection());
				if (addedEar != null) {
					removedEar.getTable().setEnabled(updateLibs.getSelection());
					addedEar.getList().setEnabled(updateLibs.getSelection());
				}
			}

		});
		Group noteGroup = new Group(contents, SWT.NONE);
		noteGroup.setText(Messages.SeamFacetVersionChangeDialog_Note);
		noteGroup.setLayout(new GridLayout(2,false));
		gd = new GridData(GridData.FILL_BOTH);
		gd.horizontalSpan=3;
		noteGroup.setLayoutData(gd);
		
		Label noteImage = new Label(noteGroup,SWT.NONE);
		Image image = PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJS_WARN_TSK);
		noteImage.setImage(image);
		Label noteLabel = new Label(noteGroup,SWT.NONE);
		noteLabel.setText(Messages.SeamFacetVersionChangeDialog_Note_description);
		
		seamRuntimeCombo.addSelectionListener(new SelectionAdapter() {

			@Override
			public void widgetSelected(SelectionEvent e) {
				refresh();
			}
			
		});
		refresh();
		return area;
	}

	private SeamVersion refreshSeamRuntimeCombo() {
		final SeamVersion version = SeamVersion.findByString(fv
				.getVersionString());
		String[] runtimeNames = getRuntimeNames(version);
		seamRuntimeCombo.setItems(runtimeNames);
		if (runtimeNames.length > 0 && seamRuntimeCombo.getText().trim().length() <=0 ) {
			seamRuntimeCombo.select(0);
		}
		
		return version;
	}

	private void refresh() {
		updateButton();
		File seamHomePath = getSeamHomePath();
		added.setInput(seamHomePath);
		if (addedEar != null) {
			addedEar.setInput(seamHomePath);
		}
	}

	private void updateButton() {
		Button okButton = getButton(IDialogConstants.OK_ID);
		if (okButton != null) {
			boolean ok = seamRuntimeCombo.getText().trim().length() > 0;
			okButton.setEnabled(ok);
		}
	}

	private void addLabels(Composite composite) {
		Label removedLibs = new Label(composite, SWT.NONE);
		GridData gd = new GridData(GridData.FILL_HORIZONTAL);
		removedLibs.setLayoutData(gd);
		removedLibs.setText(Messages.SeamFacetVersionChangeDialog_Libraries_to_be_removed);
		
		Label addedLibs = new Label(composite, SWT.NONE);
		addedLibs.setText(Messages.SeamFacetVersionChangeDialog_Libraries_to_be_added);
		gd = new GridData(GridData.FILL_HORIZONTAL);
		addedLibs.setLayoutData(gd);
	}
	private File getEarContentsFolder() {
		IContainer earContentsFolder = getEarContentsEclipseFolder();
		if (earContentsFolder == null) {
			return null;
		} else {
			return earContentsFolder.getLocation().toFile();
		}
	}

	private IContainer getEarContentsEclipseFolder() {
		String earProjectName = preferences.get(ISeamFacetDataModelProperties.SEAM_EAR_PROJECT, ""); //$NON-NLS-1$
		if (earProjectName != null && earProjectName.trim().length() > 0) {
			IProject earProject = ResourcesPlugin.getWorkspace().getRoot().getProject(earProjectName);
			if (earProject != null && earProject.isOpen()) {
				IVirtualComponent component = ComponentCore.createComponent(earProject);
				IVirtualFolder rootVirtFolder = component.getRootFolder().getFolder(new Path("/")); //$NON-NLS-1$
				return rootVirtFolder.getUnderlyingFolder();
			}
		}
		return null;
		
	}
	
	private void check(final CheckboxTableViewer removed,
			IStructuredContentProvider rContentProvider) {
		Object[] elements = rContentProvider.getElements(null);
		for (int i = 0; i < elements.length; i++) {
			if (elements[i] instanceof File) {
				File file = (File) elements[i];
				removed.setChecked(elements[i], getChecked(file));

			}
		}
	}

	private boolean getChecked(File file) {
		for (int i = 0; i < filePattern.length; i++) {
			if (file.getName().startsWith(filePattern[i])) {
				return true;
			}
		}
		return false;
	}

	private File getSeamHomePath() {
		String selectedSeamName = seamRuntimeCombo.getText();
		if (selectedSeamName != null && selectedSeamName.trim().length() > 0) {
			SeamRuntime selectedSeamRuntime = SeamRuntimeManager.getInstance()
					.findRuntimeByName(selectedSeamName);
			if (selectedSeamRuntime != null) {
				return new File(selectedSeamRuntime.getHomeDir());
			}
		}
		return null;
	}

	private File getWebLibFolder() {
		IContainer webLibFolder = getWebLibEclipseFolder();

		return webLibFolder.getLocation().toFile();

	}

	private IContainer getWebLibEclipseFolder() {
		IVirtualComponent component = ComponentCore
				.createComponent(warProject);
		IVirtualFolder libFolder = component.getRootFolder().getFolder(
				new Path("/WEB-INF/lib")); //$NON-NLS-1$
		IContainer webLibFolder = libFolder.getUnderlyingFolder();
		return webLibFolder;
	}

	private String[] getRuntimeNames(SeamVersion seamVersion) {
		SeamRuntime[] rts = SeamRuntimeManager.getInstance().getRuntimes(
				seamVersion);
		String[] names = new String[rts.length];
		for (int i = 0; i < names.length; i++) {
			names[i] = rts[i].getName();
		}
		return names;
	}

	@Override
	public boolean close() {
		if (_dlgTitleImage != null) {
			_dlgTitleImage.dispose();
		}
		return super.close();
	}

	private boolean isWarConfiguration() {
		if (preferences == null) {
			return false;
		}
		return preferences.get(
				ISeamFacetDataModelProperties.JBOSS_AS_DEPLOY_AS,
				ISeamFacetDataModelProperties.DEPLOY_AS_WAR).equals(
				ISeamFacetDataModelProperties.DEPLOY_AS_WAR);
	}

	@Override
	protected void createButtonsForButtonBar(Composite parent) {
		super.createButtonsForButtonBar(parent);
		updateButton();
	}

	@Override
	protected void okPressed() {
		
		try {
			ResourcesPlugin.getWorkspace().run(new IWorkspaceRunnable() {

				public void run(IProgressMonitor monitor) throws CoreException {
					setSeamSettings();
					updateLibraries();
				}
				
			}, new NullProgressMonitor());
			
		} catch (CoreException e) {
			SeamCorePlugin.getPluginLog().logError(e);
		}
		super.okPressed();
	}

	protected void setSeamSettings() {
		IEclipsePreferences prefs = preferences;
		if(prefs==null) {
			IScopeContext projectScope = new ProjectScope(warProject);
			prefs = projectScope.getNode(SeamCorePlugin.PLUGIN_ID);
		}
		prefs.put(ISeamFacetDataModelProperties.SEAM_SETTINGS_VERSION, 
				ISeamFacetDataModelProperties.SEAM_SETTINGS_VERSION_1_1);
		prefs.put(ISeamFacetDataModelProperties.SEAM_RUNTIME_NAME, 
				seamRuntimeCombo.getText());
	
	}

	private void updateLibraries() throws CoreException {
		if (updateLibs.getSelection()) {
			IContainer webLibContainer = getWebLibEclipseFolder();
			processProject(webLibContainer,removed,added);
			if (removedEar != null) {
				IContainer earContentsFolder = getEarContentsEclipseFolder();
				processProject(earContentsFolder, removedEar, addedEar);
			}
		}
	}

	private void processProject(IContainer container,
			CheckboxTableViewer cbtViewer, ListViewer listViewer)
			throws CoreException {
		if (container != null) {
			removeFiles(container, cbtViewer.getCheckedElements());
			IStructuredContentProvider structuredProvider = (IStructuredContentProvider) listViewer
					.getContentProvider();
			Object[] addedElements = structuredProvider.getElements(null);
			Map<File, List<File>> parentMaps = getImportMaps(addedElements);
			IPath containerPath = container.getFullPath();
			IImportStructureProvider provider = FileSystemStructureProvider.INSTANCE;
			for (File parent : parentMaps.keySet()) {
				try {
					ImportOperation op = new ImportOperation(containerPath,
							parent, provider, OVERWRITE_ALL, parentMaps
									.get(parent));
					op.setCreateContainerStructure(false);
					op.run(new NullProgressMonitor());
				} catch (InvocationTargetException e) {
					SeamCorePlugin.getPluginLog().logError(e);
				} catch (InterruptedException e) {
					SeamCorePlugin.getPluginLog().logError(e);
				}
			}
		}
	}

	private void removeFiles(IContainer container, Object[] elements) throws CoreException {
		for (int i = 0; i < elements.length; i++) {
			if (elements[i] instanceof File) {
				File file = (File) elements[i];
				String name = file.getName();
				IResource resource = container.findMember(name);
				resource.delete(true, null);
			}
		}
	}

	private Map<File, List<File>> getImportMaps(Object[] elements) {
		Map<File, List<File>> parentMaps = new HashMap<File, List<File>>();
		for (int i = 0; i < elements.length; i++) {
			if (elements[i] instanceof File) {
				File file = (File) elements[i];
				File parent = file.getParentFile();
				Set<File> parents = parentMaps.keySet();
				if (parents.contains(parent)) {
					List<File> files = parentMaps.get(parent);
					files.add(file);
				} else {
					List<File> files = new ArrayList<File>();
					files.add(file);
					parentMaps.put(parent, files);
				}
			}
		}
		return parentMaps;
	}
}
