/**
 * File:    SelectJarWizardPage.java
 * Created: 20.12.2006
 *
/*******************************************************************************
 * Copyright (c) 2005-2011
 * University of Augsburg, Germany <www.ds-lab.org>
 *
 * 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:
 *    Christian Seitz, Programming distributed Systems Lab, University of Augsburg 
 *    	- initial API and implementation
 *******************************************************************************/

package org.eclipse.jwt.we.misc.wizards.model;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.List;
import java.util.StringTokenizer;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.TreeNode;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.jwt.we.PluginProperties;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
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.Composite;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.PlatformUI;


/**
 * Wizard Page to specify JAR-archive, class and method
 * 
 * @author Christian Seitz (chr.seitz@gmx.de), Programming distributed Systems Lab,
 *         University of Augsburg, Germany, www.ds-lab.org.
 */
public class SelectJarWizardPage
		extends WizardPage
{

	/**
	 * The Tree for the Class selection
	 */
	protected Tree jarTree;

	/**
	 * The TextField for the path of the JAR-file
	 */
	protected Text jarField;

	/**
	 * The TextField for the method
	 */
	protected Text methodField;

	/**
	 * The TextField for the Java class
	 */
	protected Text classField;

	/**
	 * The path of the selected Jar-File
	 */
	protected String filePath;

	/**
	 * The Tree Viewer
	 */
	protected TreeViewer jarViewer;

	/**
	 * The ContentProvider
	 */
	protected JarContentProvider contentProvider;

	/**
	 * If a WebServiceApplication shall be created an own operation is needed
	 */
	protected Text webserviceOperation;

	/**
	 * If a WebServiceApplication shall be created an own interface is needed
	 */
	protected Text webserviceInterface;

	/**
	 * Check box to specify whether this application is a web service or not
	 */
	protected Button webserviceCheckbox;

	/**
	 * specifies whether this is a web service application or a simply application
	 */
	protected boolean isWebservice;


	/**
	 * Create a new wizard page instance
	 * 
	 * @param pageId
	 *            The PageId
	 */
	public SelectJarWizardPage(String pageId)
	{
		super(pageId);
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets.Composite
	 * )
	 */
	public void createControl(Composite parent)
	{
		Composite composite = new Composite(parent, SWT.NONE);
		{
			GridLayout layout = new GridLayout();
			layout.numColumns = 3;
			composite.setLayout(layout);
			GridData data = new GridData();
			data.verticalAlignment = GridData.FILL;
			data.grabExcessVerticalSpace = true;
			data.horizontalAlignment = GridData.FILL;
			composite.setLayoutData(data);
		}

		// JAR selection
		Label jarLabel = new Label(composite, SWT.RIGHT);
		jarLabel
				.setText(org.eclipse.jwt.meta.PluginProperties.model_Application_jarArchive_name
						+ ":"); //$NON-NLS-1$
		jarField = new Text(composite, SWT.BORDER);
		{
			GridData data = new GridData();
			data.horizontalAlignment = GridData.FILL;
			data.grabExcessHorizontalSpace = true;
			jarField.setLayoutData(data);
		}
		Button browseButton = new Button(composite, SWT.PUSH);
		browseButton.setText(PluginProperties.browse_button);
		browseButton.addSelectionListener(new SelectionAdapter()
		{

			@Override
			public void widgetSelected(SelectionEvent event)
			{
				String[] filterExtensions =
				{ "*.jar" }; //$NON-NLS-1$
				String[] extensionNames =
				{ PluginProperties.wizards_Files_jarfiles };
				FileDialog fileDialog = new FileDialog(getShell(), SWT.OPEN);
				fileDialog.setFilterExtensions(filterExtensions);
				fileDialog.setFilterNames(extensionNames);
				fileDialog.open();
				if (fileDialog.getFileName() != null
						&& fileDialog.getFileName().length() > 0)
				{
					filePath = fileDialog.getFilterPath() + File.separator
							+ fileDialog.getFileName();
				}
				jarField.setText(filePath);
				setTreeViewerInput();
			}
		});

		// Class selection out of a jar
		jarTree = new Tree(composite, SWT.SINGLE);
		{
			GridData data = new GridData();
			data.grabExcessHorizontalSpace = true;
			data.grabExcessVerticalSpace = true;
			data.horizontalAlignment = GridData.FILL;
			data.verticalAlignment = GridData.FILL;
			data.horizontalSpan = 3;
			jarTree.setLayoutData(data);
		}
		jarViewer = new TreeViewer(jarTree);
		contentProvider = new JarContentProvider();
		jarViewer.setContentProvider(contentProvider);
		jarViewer.setLabelProvider(new JarLabelProvider());
		jarViewer.setSorter(new JarSorter());
		jarTree.addSelectionListener(selListener);

		// Class selection
		Label classLabel = new Label(composite, SWT.RIGHT);
		classLabel
				.setText(org.eclipse.jwt.meta.PluginProperties.model_Application_javaClass_name);
		classField = new Text(composite, SWT.BORDER);
		{
			GridData data = new GridData();
			data.horizontalAlignment = GridData.FILL;
			data.grabExcessHorizontalSpace = true;
			data.horizontalSpan = 2;
			classField.setLayoutData(data);
		}
		classField.setFocus();
		classField.addModifyListener(validator);

		// Method selection
		Label methodLabel = new Label(composite, SWT.RIGHT);
		methodLabel
				.setText(org.eclipse.jwt.meta.PluginProperties.model_Application_method_name
						+ ":"); //$NON-NLS-1$
		methodField = new Text(composite, SWT.BORDER);
		{
			GridData data = new GridData();
			data.horizontalAlignment = GridData.FILL;
			data.grabExcessHorizontalSpace = true;
			data.horizontalSpan = 2;
			methodField.setLayoutData(data);
		}

		Label isWebserviceLabel = new Label(composite, SWT.RIGHT);
		isWebserviceLabel.setText(PluginProperties.wizards_IsWebServiceApplication_name);

		webserviceCheckbox = new Button(composite, SWT.CHECK);
		{
			GridData data = new GridData();
			data.horizontalAlignment = GridData.FILL;
			data.grabExcessHorizontalSpace = true;
			data.horizontalSpan = 2;
			webserviceCheckbox.setLayoutData(data);
		}

		webserviceCheckbox
				.addSelectionListener(new org.eclipse.swt.events.SelectionAdapter()
				{

					public void widgetSelected(org.eclipse.swt.events.SelectionEvent e)
					{
						boolean selected = webserviceCheckbox.getSelection();
						if (selected == true)
						{
							isWebservice = true;
							webserviceInterface.setEnabled(true);
							webserviceOperation.setEnabled(true);
						}
						else
						{
							isWebservice = false;
							webserviceInterface.setEnabled(false);
							webserviceOperation.setEnabled(false);
						}
					}
				});

		Label interfaceLabel = new Label(composite, SWT.RIGHT);
		interfaceLabel
				.setText(org.eclipse.jwt.meta.PluginProperties.model_WebServiceApplication_interface_name);
		webserviceInterface = new Text(composite, SWT.BORDER);
		{
			GridData data = new GridData();
			data.horizontalAlignment = GridData.FILL;
			data.grabExcessHorizontalSpace = true;
			data.horizontalSpan = 2;
			webserviceInterface.setLayoutData(data);
		}
		webserviceInterface.setFocus();
		webserviceInterface.addModifyListener(validator);
		webserviceInterface.setEnabled(false); // initially!

		Label operationLabel = new Label(composite, SWT.RIGHT);
		operationLabel
				.setText(org.eclipse.jwt.meta.PluginProperties.model_WebServiceApplication_operation_name);
		webserviceOperation = new Text(composite, SWT.BORDER);
		{
			GridData data = new GridData();
			data.horizontalAlignment = GridData.FILL;
			data.grabExcessHorizontalSpace = true;
			data.horizontalSpan = 2;
			webserviceOperation.setLayoutData(data);
		}
		webserviceOperation.setFocus();
		webserviceOperation.setEnabled(false);// initially!

		setPageComplete(validatePage());
		setControl(composite);
	}


	/**
	 * Sets the Input for the TreeViewer. Opens the JAR-Archive and builds a tree of the
	 * elements
	 * 
	 */
	private void setTreeViewerInput()
	{
		JarFile jarFile = null;
		File file = new File(filePath);
		try
		{
			jarFile = new JarFile(file);
		}
		catch (IOException e)
		{
		}

		if (jarFile != null)
		{
			int size = jarFile.size();
			Enumeration jarEntries = jarFile.entries();
			Hashtable<String, JarNode> nodes = new Hashtable<String, JarNode>();
			JarNode root = new JarNode(jarFile.getName());

			for (int i = 0; i < size; i++)
			{
				JarEntry jarEntry = (JarEntry) jarEntries.nextElement();
				String entryName = jarEntry.getName();
				int cutIndex = entryName.lastIndexOf('/');
				String dir = null;
				if (cutIndex != -1)
				{
					dir = entryName.substring(0, cutIndex);
					entryName = entryName.substring(cutIndex + 1);
				}
				JarNode parent = null;
				JarNode lastParent = root;
				if (!entryName.equals("")) //$NON-NLS-1$
				{
					if (dir == null)
						parent = root;
					else
					{
						StringTokenizer tokenizer = new StringTokenizer(dir, "/"); //$NON-NLS-1$
						String dirpath = ""; //$NON-NLS-1$
						while (tokenizer.hasMoreTokens())
						{
							String dirName = tokenizer.nextToken();
							dirpath += dirName;
							parent = nodes.get(dirpath);
							if (parent == null)
							{
								parent = new JarNode(dirName);
								lastParent.addChild(parent);
								nodes.put(dirpath, parent);
							}
							lastParent = parent;
							dirpath += "/"; //$NON-NLS-1$
						}
					}
					JarNode node = new JarNode(entryName);
					node.setJarEntry(jarEntry);
					lastParent.addChild(node);
				}
			}

			buildTree(root);
			jarViewer.setInput(contentProvider.root);
			jarViewer.add(contentProvider.root, root);
			jarViewer.remove(contentProvider.helper);
		}
		try
		{
			jarFile.close();
		}
		catch (IOException e)
		{
		}
	}


	/**
	 * Builds the complete Tree for the TreeViewer
	 * 
	 * @param node
	 *            The Root node of the Tree
	 */
	private void buildTree(JarNode node)
	{
		List<TreeNode> childrenList = node.getChildrenList();
		if (!childrenList.isEmpty())
		{
			TreeNode[] tn = new TreeNode[childrenList.size()];
			for (int i = 0; i < childrenList.size(); i++)
			{
				tn[i] = childrenList.get(i);
			}
			node.setChildren(tn);
			for (int i = 0; i < node.getChildren().length; i++)
			{
				buildTree((JarNode) node.getChildren()[i]);
			}
		}
	}


	/**
	 * Returns the Path of the Jar
	 * 
	 * @return The Jar
	 */
	public String getJar()
	{
		return jarField.getText();
	}


	/**
	 * Returns the class as a String
	 * 
	 * @return The Class
	 */
	public String getSelClass()
	{
		return classField.getText();
	}


	/**
	 * Returns the Method as a String
	 * 
	 * @return The Method
	 */
	public String getMethod()
	{
		return methodField.getText();
	}


	/**
	 * Returns the Interface of the Web Service as a String
	 * 
	 * @return The Interface
	 */
	public String getInterface()
	{
		return webserviceInterface.getText();
	}


	/**
	 * Returns the Operation of the Web Service as a String
	 * 
	 * @return The Operation
	 */
	public String getOperation()
	{
		return webserviceOperation.getText();
	}


	/**
	 * Returns whether an Application or a WebServiceApplication shall be created
	 * 
	 * @return true for WebServiceApplication, false otherwise
	 */
	public boolean getIsWebservice()
	{
		return isWebservice;
	}

	/**
	 * 
	 */
	protected ModifyListener validator = new ModifyListener()
	{

		public void modifyText(ModifyEvent e)
		{
			setPageComplete(validatePage());
		}
	};


	/**
	 * Enables Next/Finish Button
	 */
	protected boolean validatePage()
	{
		if (isWebservice)
		{
			if (webserviceInterface.getText() != "") //$NON-NLS-1$
				return true;
		}
		else
		{ // normal application
			if (classField.getText() != "") //$NON-NLS-1$
				return true;
		}
		return false;
	}

	/**
	 * 
	 */
	protected SelectionListener selListener = new SelectionListener()
	{

		public void widgetSelected(SelectionEvent e)
		{
			if (jarTree.getSelection()[0].getData() instanceof JarNode)
			{
				JarNode selection = (JarNode) jarTree.getSelection()[0].getData();
				if (selection.getJarEntry() != null)
				{
					String name = selection.getJarEntry().getName();
					name = name.replace('/', '.');
					String s = ".class"; //$NON-NLS-1$
					s = s.toLowerCase();
					if (name.endsWith(s))
					{
						name = name.substring(0, name.length() - 6);
					}
					classField.setText(name);
				}
			}

		}


		public void widgetDefaultSelected(SelectionEvent e)
		{

		}
	};

	// If needed somewhere else in Workflow Editor, the following section should
	// be extricacted
	// from this class.

	/**
	 * 
	 * ContentProvider for the JarTree
	 * 
	 * @author Christian Seitz (seitzchr@users.sourceforge.net)
	 */
	class JarContentProvider
			implements IStructuredContentProvider, ITreeContentProvider
	{

		/**
		 * The invisible root of the Tree
		 */
		public TreeNode root = new TreeNode("root"); //$NON-NLS-1$

		/**
		 * This Entry is never shown in the TreeViewer, but has to created here. Otherwise
		 * the TreeViewer would display nothing at all!
		 */
		public TreeNode helper = new TreeNode(PluginProperties.choose_jar_title);


		/**
		 * Creates an instance of this ContentProvider. Sets a first children. This has to
		 * be done or otherwise nothing will be displayd in the TreeViewer
		 * 
		 */
		public JarContentProvider()
		{
			root.setChildren(new TreeNode[]
			{ helper });

		}


		public void inputChanged(Viewer v, Object oldInput, Object newInput)
		{
		}


		public void dispose()
		{
		}


		public Object[] getElements(Object parent)
		{
			return getChildren(parent);
		}


		public Object getParent(Object child)
		{
			return ((TreeNode) child).getParent();

		}


		public Object[] getChildren(Object parent)
		{
			return ((TreeNode) parent).getChildren();

		}


		public boolean hasChildren(Object parent)
		{

			return ((TreeNode) parent).hasChildren();
		}
	}

	/**
	 * 
	 * Label Provider for the JarTree
	 * 
	 * @author Christian Seitz (seitzchr@users.sourceforge.net)
	 */
	class JarLabelProvider
			extends LabelProvider
	{

		@Override
		public String getText(Object obj)
		{
			if (obj instanceof TreeNode)
			{
				Object value = ((TreeNode) obj).getValue();
				if (value instanceof JarEntry)
				{
					return ((JarEntry) value).getName();
				}
				if (value instanceof File)
				{
					return ((File) value).getName();
				}
				if (value instanceof String)
				{
					return (String) value;
				}
			}
			return obj.toString();
		}


		@Override
		public Image getImage(Object obj)
		{
			String imageKey = ISharedImages.IMG_OBJ_FILE;
			if (obj instanceof JarNode)
			{
				JarNode node = (JarNode) obj;
				if (node.getJarEntry() == null)
				{
					imageKey = ISharedImages.IMG_OBJ_FOLDER;
				}
			}
			return PlatformUI.getWorkbench().getSharedImages().getImage(imageKey);
		}

	}

	/**
	 * 
	 * This ViewerSorter is responsible for sorting folders and files.
	 * 
	 * @author Christian Seitz (seitzchr@users.sourceforge.net)
	 */
	class JarSorter
			extends ViewerSorter
	{

		@Override
		public int category(Object element)
		{
			if (element instanceof JarNode)
			{
				if (((JarNode) element).getJarEntry() == null)
					return 0;
				else
					return 1;
			}
			else
				return 2;
		}
	}

	/**
	 * 
	 * Special version of a TreeNode. Contains a JarEntry (for the selection in the Tree
	 * viewer) and a childrenList for easier editing of the children. Also necessary to
	 * avoid some 'bugs' with the Original TreeNode and TreeViewer (could become obsolete
	 * in future Eclipse versions).
	 * 
	 * @author Christian Seitz (seitzchr@users.sourceforge.net)
	 */
	class JarNode
			extends TreeNode
	{

		/**
		 * The Jar Entry
		 */
		private JarEntry jarEntry;

		/**
		 * A list of the children
		 */
		private ArrayList<TreeNode> childrenList = new ArrayList<TreeNode>();


		public JarNode(Object value)
		{
			super(value);
		}


		public JarEntry getJarEntry()
		{
			return jarEntry;
		}


		public void setJarEntry(JarEntry jarEntry)
		{
			this.jarEntry = jarEntry;
		}


		public List<TreeNode> getChildrenList()
		{
			return childrenList;
		}


		public void addChild(TreeNode child)
		{
			childrenList.add(child);
		}

	}

}
