//------------------------------------------------------------------------------
// Copyright (c) 2005, 2006 IBM Corporation 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:
// IBM Corporation - initial implementation
//------------------------------------------------------------------------------
package org.eclipse.epf.publishing.wizards;

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

import org.eclipse.emf.ecore.EObject;
import org.eclipse.epf.library.LibraryService;
import org.eclipse.epf.library.LibraryServiceUtil;
import org.eclipse.epf.library.configuration.ConfigurationHelper;
import org.eclipse.epf.library.edit.util.TngUtil;
import org.eclipse.epf.library.ui.wizards.BaseWizardPage;
import org.eclipse.epf.publishing.services.PublishOptions;
import org.eclipse.epf.publishing.ui.PublishingUIResources;
import org.eclipse.epf.uma.Activity;
import org.eclipse.epf.uma.MethodConfiguration;
import org.eclipse.epf.uma.MethodPlugin;
import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.CheckboxTreeViewer;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.wizard.IWizardPage;
import org.eclipse.swt.SWT;
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.Event;
import org.eclipse.swt.widgets.Listener;

/**
 * The Select publishing proceses wizard.
 * 
 * @author Jinhua Xi
 * @since 1.0
 */
public class PublishConfigSelectProcessPage extends BaseWizardPage implements
		Listener {

	private Button radio_entireConfig;

	private Button radio_processes;

	private Composite processContainer;

	private Button includeBaseProcesses;
	
	// private List check_processes = new ArrayList();

	Map checkBoxTpProcMap = new HashMap();

	CheckboxTreeViewer procViewer;

	private PublishOptions dataModel = null;

	/**
	 * Creates a new instance.
	 */
	public PublishConfigSelectProcessPage(String pageName,
			PublishOptions dataModel) {
		super(pageName);
		setTitle(PublishingUIResources.PublishConfigSelectProcessPage_title); //$NON-NLS-1$
		setDescription(PublishingUIResources.PublishConfigSelectProcessPage_text); //$NON-NLS-1$
		this.dataModel = dataModel;
	}

	/**
	 * @see org.eclipse.jface.dialogs.IDialogPage#createControl(Composite)
	 */
	public void createControl(Composite parent) {
		// Create the composite to hold the widgets.
		Composite composite = new Composite(parent, SWT.NULL);
		composite.setLayout(new GridLayout(1, false));

		createLabel(
				composite,
				PublishingUIResources.PublishConfigSelectProcessPage_label_selectType); //$NON-NLS-1$

		radio_entireConfig = createRadioButton(
				composite,
				PublishingUIResources.PublishConfigSelectProcessPage_label_type_config, 1, true); //$NON-NLS-1$

		radio_processes = createRadioButton(
				composite,
				PublishingUIResources.PublishConfigSelectProcessPage_label_type_processes, 1, false); //$NON-NLS-1$

		processContainer = new Composite(composite, SWT.NONE);
		processContainer.setLayoutData(new GridData(GridData.FILL_BOTH
				| GridData.GRAB_VERTICAL));
		GridLayout layout = new GridLayout();
		layout.marginLeft = 13;
		processContainer.setLayout(layout);

		createProcessContainer(processContainer);

		radio_entireConfig.addListener(SWT.Selection, this);
		radio_processes.addListener(SWT.Selection, this);

		processContainer.setVisible(radio_processes.getSelection());

		setControl(composite);
		setPageComplete(true);
	}

	private void createProcessContainer(Composite parent) {
		createTreeViewer(parent);
		includeBaseProcesses = createCheckbox(parent, 
				PublishingUIResources.PublishConfigSelectProcessPage_label_publish_base_processes); 
		includeBaseProcesses.addListener(SWT.Selection, this);
	}
	
	private void createTreeViewer(Composite parent) {
		procViewer = new CheckboxTreeViewer(parent);
		{
			GridData gridData = new GridData(GridData.FILL_BOTH
					| GridData.GRAB_HORIZONTAL);
			gridData.heightHint = 300;
			procViewer.getTree().setLayoutData(gridData);
		}
		procViewer.setContentProvider(new ProcessContentProvider());
		procViewer.setLabelProvider(new ProcessLabelProvider());

		procViewer.addCheckStateListener(new ICheckStateListener() {
			public void checkStateChanged(CheckStateChangedEvent event) {
				boolean checked = event.getChecked();
				updateCheckStates(event.getElement(), checked);
				selectbaseProcesses();
				
				setPageComplete(isPageComplete());
				getWizard().getContainer().updateButtons();

			}
		});

		String selecedConfigName = dataModel.getSelectedConfig();
		MethodConfiguration config = LibraryServiceUtil.getMethodConfiguration(
				LibraryService.getInstance().getCurrentMethodLibrary(),
				selecedConfigName);

		procViewer.setInput(config);

		procViewer.expandAll();
	}

	private void updateCheckStates(Object element, boolean checked) {

		// Object element = event.getElement();

		if (checked == true) {
			ITreeContentProvider cp = (ITreeContentProvider) procViewer
					.getContentProvider();
			checkParent(cp, element);
		} else
			procViewer.setChecked(element, false);

		selectionChildren(element, checked);

	}

	private void checkParent(ITreeContentProvider cp, Object element) {
		// if (element == null ) {
		// return;
		// }
		// Object parent = cp.getParent(element);
		// if (parent != null) {
		// procViewer.setChecked(parent, true);
		// checkParent(cp, parent);
		// }

	}

	public void selectionChildren(Object element, boolean checked) {
		ITreeContentProvider cp = (ITreeContentProvider) procViewer
				.getContentProvider();
		Object[] childs = cp.getChildren(element);
		for (int i = 0; i < childs.length; i++) {
			procViewer.setChecked(childs[i], checked);
			selectionChildren(childs[i], checked);
		}
	}

	public void handleEvent(Event event) {

		if ( event.widget == includeBaseProcesses ) {
			// if selected, alto select all base processes
			selectbaseProcesses();
		}

		processContainer.setVisible(radio_processes.getSelection());

		setPageComplete(isPageComplete());
		getWizard().getContainer().updateButtons();
	}

	public IWizardPage getNextPage() {

		return super.getNextPage();
	}

	public boolean isPageComplete() {
		if (getErrorMessage() != null)
			return false;

		dataModel.publishProcess = false;
		if (radio_entireConfig.getSelection()) {
			dataModel.publishConfiguration = true;
			return true;
		} else {
			List procs = getSelectedProcesses();

			// for (int i = 0; i < check_processes.size(); i++) {
			// Button b = (Button) check_processes.get(i);
			// if (b.getSelection()) {
			//
			// // set the selections
			// procs.add(checkBoxTpProcMap.get(b));
			// }
			// }

//			Object[] selected = procViewer.getCheckedElements();
//			if (selected != null && selected.length > 0) {
//				for (int i = 0; i < selected.length; i++) {
//					if (selected[i] instanceof org.eclipse.epf.uma.Process) {
//						procs.add(selected[i]);
//					}
//				}
//			}
			if (procs.size() > 0) {
				dataModel.publishProcess = true;
				dataModel.publishConfiguration = false;
				dataModel.setDeliverProcessList(procs);
				return true;
			}
		}

		return false;
	}

	private List getSelectedProcesses() {
		List procs = new ArrayList();

		Object[] selected = procViewer.getCheckedElements();
		if (selected != null && selected.length > 0) {
			for (int i = 0; i < selected.length; i++) {
				if (selected[i] instanceof org.eclipse.epf.uma.Process) {
					procs.add(selected[i]);
				}
			}
		}
		
		return procs;
	}
	
	private void selectbaseProcesses() {
		if ( !includeBaseProcesses.getSelection() ) {
			return;
		}
		
		String selecedConfigName = dataModel.getSelectedConfig();
		MethodConfiguration config = LibraryServiceUtil.getMethodConfiguration(
				LibraryService.getInstance().getCurrentMethodLibrary(),
				selecedConfigName);

		List procs = getSelectedProcesses();
		List bases = new ArrayList();
		for (Iterator it = procs.iterator(); it.hasNext(); ) {
			ConfigurationHelper.getBaseProcesses((Activity)it.next(), config, bases);
		}
		
		// update the selections
		for (Iterator it = bases.iterator(); it.hasNext(); ) {
			Object element = it.next();
			procViewer.setChecked(element, true);
			updateCheckStates(element, true);
		}		
	}
	
	public class ProcessContentProvider implements ITreeContentProvider {

		MethodConfiguration input = null;

		String NODE_CP = PublishingUIResources.PublishConfigSelectProcessPage_label_capability_pattern; //$NON-NLS-1$

		String NODE_DP = PublishingUIResources.PublishConfigSelectProcessPage_label_delivery_process; //$NON-NLS-1$

		String[] DPCP_NODES = new String[] { NODE_CP, NODE_DP };

		public Object[] getChildren(Object parentElement) {
			if (parentElement instanceof MethodConfiguration) {
				return DPCP_NODES;
			} else if (NODE_CP.equals(parentElement.toString())
					|| NODE_DP.equals(parentElement.toString())) {
				return getPlugins(parentElement).toArray();
			} else if (parentElement instanceof PluginNode) {
				Object parent = ((PluginNode) parentElement).parent;
				if (NODE_CP.equals(parent.toString())) {
					return ConfigurationHelper.getAllCapabilityPatterns(
							((PluginNode) parentElement).plugin, input)
							.toArray();
				} else if (NODE_DP.equals(parent.toString())) {
					return ConfigurationHelper.getAllDeliveryProcesses(
							((PluginNode) parentElement).plugin, input)
							.toArray();
				}
			}

			return new Object[] {};
		}

		public Object getParent(Object element) {
			if (element instanceof String) {
				return null;
			} else if (element instanceof PluginNode) {
				return ((PluginNode) element).parent;
			} else if (element instanceof org.eclipse.epf.uma.Process) {
				EObject o = (EObject) element;
				while ((o != null) && !(o instanceof MethodPlugin)) {
					o = o.eContainer();
				}

				return o;
			}

			return null;
		}

		public boolean hasChildren(Object element) {
			if (element instanceof PluginNode || element instanceof String) {
				return true;
			} else {
				return false;
			}
		}

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

		public void dispose() {
			pluginNodeMap.clear();
			input = null;
		}

		public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
			if (newInput instanceof MethodConfiguration) {
				input = (MethodConfiguration) newInput;
			} else {
				input = null;
			}

			pluginNodeMap.clear();
		}

		private List getPlugins(Object parent) {
			List validItems = new ArrayList();
			if (input != null) {
				List plugins = input.getMethodPluginSelection();
				for (Iterator it = plugins.iterator(); it.hasNext();) {
					MethodPlugin plugin = (MethodPlugin) it.next();
					List procs = null;
					if (NODE_CP.equals(parent.toString())) {
						procs = ConfigurationHelper.getAllCapabilityPatterns(
								plugin, input);
					} else if (NODE_DP.equals(parent.toString())) {
						procs = ConfigurationHelper.getAllDeliveryProcesses(
								plugin, input);
					}

					if (procs != null && procs.size() > 0) {
						validItems.add(getPluginNode(parent, plugin));
					}
				}
			}
			return validItems;
		}

		Map pluginNodeMap = new HashMap();

		private PluginNode getPluginNode(Object parent, MethodPlugin plugin) {
			String key = parent.toString() + ":" + plugin.getName(); //$NON-NLS-1$
			PluginNode node = (PluginNode) pluginNodeMap.get(key);
			if (node == null) {
				node = new PluginNode(parent, plugin);
				pluginNodeMap.put(key, node);
			}

			return node;
		}

		class PluginNode {
			Object parent;

			MethodPlugin plugin;

			public PluginNode(Object parent, MethodPlugin plugin) {
				this.parent = parent;
				this.plugin = plugin;
			}

			public String toString() {
				return plugin.getName();
			}
		}
	}

	public class ProcessLabelProvider extends LabelProvider {

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

		public String getText(Object element) {
			if (element instanceof org.eclipse.epf.uma.Process) {
				// ((org.eclipse.epf.uma.Process)element).getPresentationName()
				org.eclipse.epf.uma.Process proc = (org.eclipse.epf.uma.Process) element;
				return TngUtil.getPresentationName(proc);
			} else {
				return (element == null) ? "" : element.toString(); //$NON-NLS-1$
			}
		}
	}

}
