//------------------------------------------------------------------------------
// 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.export.msp;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;

import org.eclipse.core.runtime.Path;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.xmi.XMLResource;
import org.eclipse.emf.edit.provider.ComposedAdapterFactory;
import org.eclipse.emf.edit.ui.provider.AdapterFactoryContentProvider;
import org.eclipse.epf.common.utils.XMLUtil;
import org.eclipse.epf.library.edit.TngAdapterFactory;
import org.eclipse.epf.library.edit.process.ActivityWrapperItemProvider;
import org.eclipse.epf.library.edit.process.BreakdownElementWrapperItemProvider;
import org.eclipse.epf.library.edit.process.RoleDescriptorWrapperItemProvider;
import org.eclipse.epf.library.edit.process.TaskDescriptorWrapperItemProvider;
import org.eclipse.epf.library.edit.ui.UserInteractionHelper;
import org.eclipse.epf.library.edit.util.ProcessUtil;
import org.eclipse.epf.library.edit.util.Suppression;
import org.eclipse.epf.library.edit.util.TngUtil;
import org.eclipse.epf.library.util.ResourceHelper;
import org.eclipse.epf.msproject.Assignment;
import org.eclipse.epf.msproject.DocumentRoot;
import org.eclipse.epf.msproject.MsprojectFactory;
import org.eclipse.epf.msproject.PredecessorLink;
import org.eclipse.epf.msproject.Project;
import org.eclipse.epf.msproject.Resource;
import org.eclipse.epf.msproject.Task;
import org.eclipse.epf.msproject.util.MsprojectResourceImpl;
import org.eclipse.epf.publishing.services.AbstractPublishManager;
import org.eclipse.epf.publishing.services.PublishManager;
import org.eclipse.epf.publishing.services.PublishOptions;
import org.eclipse.epf.publishing.wizards.PublishProgressMonitorDialog;
import org.eclipse.epf.uma.Activity;
import org.eclipse.epf.uma.BreakdownElement;
import org.eclipse.epf.uma.CapabilityPattern;
import org.eclipse.epf.uma.DeliveryProcess;
import org.eclipse.epf.uma.DescribableElement;
import org.eclipse.epf.uma.MethodConfiguration;
import org.eclipse.epf.uma.MethodElement;
import org.eclipse.epf.uma.Milestone;
import org.eclipse.epf.uma.Process;
import org.eclipse.epf.uma.Role;
import org.eclipse.epf.uma.RoleDescriptor;
import org.eclipse.epf.uma.TaskDescriptor;
import org.eclipse.epf.uma.WorkBreakdownElement;
import org.eclipse.epf.uma.WorkOrder;
import org.eclipse.epf.uma.WorkProductDescriptor;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.swt.widgets.Display;

/**
 * The default Export Microsoft Project Plan Service implementation.
 * 
 * @author Bingxue Xu
 * @author Kelvin Low
 * @since 1.0
 * 
 * fix for https://bugs.eclipse.org/bugs/show_bug.cgi?id=155089
 * fix for https://bugs.eclipse.org/bugs/show_bug.cgi?id=155086
 * fix for https://bugs.eclipse.org/bugs/show_bug.cgi?id=155095
 * fix for https://bugs.eclipse.org/bugs/show_bug.cgi?id=155155
 * 
 */
public class ExportMSPXMLService {

	// If true, generate debug traces.
	private static boolean debug = ExportMSPPlugin.getDefault().isDebugging();

	// The View Builder.
	private AbstractPublishManager publishMgr;

	// All Tasks referenced by the exported Project Template.
	private List referencedTasks = new ArrayList();

	private HashMap rolesToUidMap = new HashMap();

	private HashMap wbsToUidMap = new HashMap();

	private HashMap taskToGuidMap = new HashMap();

	private HashMap predListMap = new HashMap();

	// The name of the content folder. This will be named after the context.
	private String contentFolderName;

	private String abTargetDir;

	private Suppression suppression;

	// If true, only export breakdown elements with 'isPlanned' attributes set
	// to true.
	private boolean exportOnlyPlannedElements;

	private boolean publishContentSite = false;

	private int task_uid = 1;

	private int res_uid = 1;

	private int assign_uid = 1;

	private MsprojectFactory projectFactory = MsprojectFactory.eINSTANCE;
	
	private IStructuredContentProvider contentProvider;

	/**
	 * Export a process to a Microsoft Project XML file.
	 * 
	 * @param process
	 *            a capability pattern or delivery process
	 * @param msprojectName
	 *            the name of the exported Microsoft Project
	 * @param targetDir
	 *            the target directory
	 * @param exportOptions
	 *            a collection of user specified export options
	 * @param publishConfigOptions
	 *            a collection of user specified publishing options for the
	 *            selected configuration
	 * @return <code>true</code> if the operation was completed successfully
	 * @throws ExportMSPServiceException
	 *             if an error occurs while executing the operation
	 */
	public boolean export(Process process, String msprojectName,
			File targetDir, ExportOptions exportOptions,
			PublishOptions publishConfigOptions)
			throws ExportMSPServiceException {
		if (debug) {
			System.out.println("$$$ exporting to Microsoft Project!"); //$NON-NLS-1$
			System.out.println("$$$ process                   = " + process); //$NON-NLS-1$
			System.out.println("$$$ msprojectName             = " //$NON-NLS-1$
					+ msprojectName);
			System.out.println("$$$ targetDir                 = " + targetDir); //$NON-NLS-1$
			System.out.println("$$$ exportOnlyPlannedElements = " //$NON-NLS-1$
					+ exportOnlyPlannedElements);
			System.out.println("$$$ publishConfigOptions      = " //$NON-NLS-1$
					+ publishConfigOptions);
		}

		// construct the export target xml file path
		if (!targetDir.exists()) {
			targetDir.mkdirs();
		}
		abTargetDir = targetDir.getAbsolutePath();
		Path exportPath = new Path(abTargetDir);
		boolean endWithXmlExt = msprojectName.toLowerCase().endsWith(".xml"); //$NON-NLS-1$
		String exportPathStr = (exportPath.append(endWithXmlExt ? msprojectName
				: (msprojectName + ".xml"))).toOSString(); //$NON-NLS-1$
		if (debug)
			System.out.println("$$$ exportPathStr                 = " //$NON-NLS-1$
					+ exportPathStr);

		// construct the empty MS project xml template file path
		String emptyTemplateFile = ExportMSPPlugin.getDefault()
				.getInstallPath()
				+ "template" //$NON-NLS-1$
				+ File.separator + "msproject_2003_template.xml"; //$NON-NLS-1$
		if (debug)
			System.out.println("$$$ emptyTemplateFile             = " //$NON-NLS-1$
					+ emptyTemplateFile);

		// copy the empty template MS project xml file to the export target
		try {
			File src = new File(emptyTemplateFile);
			File dst = new File(exportPathStr);
			copy(src, dst);
		} catch (Exception e) {
			e.printStackTrace();
		}

		Project project = null;
		URI fileURI = URI.createFileURI(exportPathStr);

		try {

			XMLResource res = new MsprojectResourceImpl(fileURI);
			res.getDefaultSaveOptions().put(
					XMLResource.OPTION_EXTENDED_META_DATA, Boolean.TRUE);
			res.getDefaultLoadOptions().put(
					XMLResource.OPTION_EXTENDED_META_DATA, Boolean.TRUE);
			res.getDefaultSaveOptions().put(XMLResource.OPTION_SCHEMA_LOCATION,
					Boolean.TRUE);
			res.getDefaultSaveOptions().put(
					XMLResource.OPTION_USE_ENCODED_ATTRIBUTE_STYLE,
					Boolean.TRUE);
			res.getDefaultLoadOptions().put(
					XMLResource.OPTION_USE_LEXICAL_HANDLER, Boolean.TRUE);
			res.load(null);

			DocumentRoot docRoot = (DocumentRoot) res.getEObject("/"); //$NON-NLS-1$
			project = (Project) docRoot.getProject();

			generateMSProject(process, project, targetDir, exportOptions,
					publishConfigOptions);

			res.save(null);

			if (debug) {
				printMSProject(project);
			}

		} catch (Exception e) {
			e.printStackTrace();
			throw new ExportMSPException(e);
		}

		return true;
	}

	/**
	 * Exports a capability pattern or delivery process to a Microsoft Project
	 * XML file.
	 * 
	 * @param process
	 *            a capability pattern or delivery process
	 * @param project
	 *            a Microsoft Project object
	 * @param targetDir
	 *            the target directory
	 * @param exportOptions
	 *            a collection of user specified export options
	 * @param publishConfigOptions
	 *            a collection of user specified publishing options for the
	 *            selected configuration
	 * @return <code>true</code> if the operation was completed successfully
	 * @throws ExportMSPServiceException
	 *             if an error occurs while executing the operation
	 */
	public boolean generateMSProject(Process process, Project project,
			File targetDir, ExportOptions exportOptions,
			PublishOptions publishConfigOptions) throws Exception {
		if (process == null || project == null) {
			throw new IllegalArgumentException();
		}

		try {
			// Get the default method configuration associated with the process.
			MethodConfiguration methodConfig = null;
			if (process instanceof DeliveryProcess) {
				methodConfig = ((DeliveryProcess) process).getDefaultContext();
			} else if (process instanceof CapabilityPattern) {
				methodConfig = ((CapabilityPattern) process)
						.getDefaultContext();
			} else {
				throw new IllegalArgumentException();
			}

			// Create the sub folder to store the published HTML content files.
			contentFolderName = methodConfig.getName();

			if (publishConfigOptions != null) {
				publishContentSite = true;
				File contentDir = new File(targetDir, contentFolderName);
				if (!contentDir.exists()) {
					contentDir.mkdirs();
				}
				if (debug) {
					System.out.println("$$$ vieBuilder methodConfig = " //$NON-NLS-1$
							+ methodConfig);
					System.out.println("$$$ vieBuilder publishConfigOptions = " //$NON-NLS-1$
							+ publishConfigOptions);
				}
				publishMgr = new PublishManager();
				publishMgr.init(contentDir.getAbsolutePath(), methodConfig,
						publishConfigOptions);

				// Publish the associated configuration.
				if (!publishConfiguration()) {
					return false;
				}
			}

			// Sets the export options.
			setExportOptions(exportOptions);

			// Get the suppression object for the process. Suppressed
			// breakdown elements will be exluded from the export.
			suppression = new Suppression(process);

			// Generate the Microsoft Project XML file.
			// populate the project's attributes
			project.setName(process.getName());
			project.setStartDate(new Date());
			project.setCreationDate(new Date());
			project.setLastSaved(new Date());
			project.setFinishDate(new Date());
			
			generateProjectResources(process, project);
			generateProjectTasks(process, project);
			generateLinks(process, project);

			return true;
		} catch (Exception e) {
			throw e;
		} finally {
			if (publishMgr != null) {
				publishMgr.dispose();
				publishMgr = null;
			}
		}
	}

	/**
	 * Sets the export options.
	 * 
	 * @param exportOptions
	 *            a collection of user specified export options
	 */
	protected void setExportOptions(ExportOptions exportOptions) {
		Boolean exportOnlyPlannedElements = (Boolean) exportOptions
				.get(ExportOptions.EXPORT_ONLY_PLANNED_ELEMENTS);
		this.exportOnlyPlannedElements = exportOnlyPlannedElements
				.booleanValue();
	}

	/**
	 * Generates the project resources for all the task descriptors in the
	 * process.
	 */
	protected void generateProjectResources(Process process, Project project)
			throws Exception {
		ComposedAdapterFactory adapterFactory = null;
		try {
			adapterFactory = TngAdapterFactory.INSTANCE
					.createTBSComposedAdapterFactory();
			IStructuredContentProvider contentProvider = new AdapterFactoryContentProvider(
					adapterFactory);
			List elements = process.getBreakdownElements();
			if (elements.size() > 0) {
				for (int i = 0; i < elements.size(); i++) {
					if (debug)
						System.out.println("$$$ 1st level TBS = " //$NON-NLS-1$
								+ elements.get(i));
					generateProjectResource(contentProvider,
							(BreakdownElement) elements.get(i), project);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
			throw e;
		} finally {
			if (adapterFactory != null) {
				adapterFactory.dispose();
			}
		}
	}

	/**
	 * Generates the project resources for all the role descriptors in the
	 * process.
	 */
	protected void generateProjectResource(
			IStructuredContentProvider contentProvider,
			BreakdownElement breakdownElement, Project project)
			throws Exception {
		if (breakdownElement instanceof WorkProductDescriptor
				|| breakdownElement.getSuppressed().booleanValue()
				|| (exportOnlyPlannedElements && !breakdownElement
						.getIsPlanned().booleanValue())) {
			return;
		}

		if (breakdownElement instanceof RoleDescriptor) {
			addResource(breakdownElement, project);
			return;
		}

		if (contentProvider != null) {
			Object[] elements = contentProvider.getElements(breakdownElement);
			for (int i = 0; i < elements.length; i++) {
				Object element = elements[i];
				if (element instanceof RoleDescriptorWrapperItemProvider) {
					RoleDescriptorWrapperItemProvider provider = (RoleDescriptorWrapperItemProvider) element;
					Object value = provider.getValue();
					if (value instanceof RoleDescriptor) {
						addResource((BreakdownElement) value, project);
					}
				} else if (element instanceof RoleDescriptor) {
					addResource((BreakdownElement) element, project);
				} else if (element instanceof BreakdownElementWrapperItemProvider) {
					BreakdownElementWrapperItemProvider provider = (BreakdownElementWrapperItemProvider) element;
					Object value = provider.getValue();
					if (value instanceof WorkBreakdownElement) {
						generateProjectResource(contentProvider,
								(WorkBreakdownElement) value, project);
					}
				} else if (element instanceof WorkBreakdownElement) {
					generateProjectResource(contentProvider,
							(WorkBreakdownElement) element, project);
				}
			}
		}

	}

	// for each RoleDescriptor, create a MS Project resource
	// for the RoleDescriptor and its underlying role, remember the assigned
	// res_uid
	protected void addResource(BreakdownElement breakdownElement, Project proj)
			throws Exception {
		if (!(breakdownElement instanceof RoleDescriptor)) {
			return;
		}

		// check the existence of the underlying role of this roleDescriptor
		// and skip the creation if a resource for the role already exists
		RoleDescriptor roleDescriptor = (RoleDescriptor) breakdownElement;
		if (debug)
			System.out.println("$$$ handle RoleDescriptor = " + roleDescriptor); //$NON-NLS-1$
		Role ref_edRole = roleDescriptor.getRole();
		if (debug)
			System.out.println("$$$ handle Ref-ed Role = " + ref_edRole); //$NON-NLS-1$

		// check the need to add a new resource based on RoleDescriptor's disply
		// name
		boolean newRDResource = true;
		String rdResourceName = getDisplayName(roleDescriptor);
		if (rolesToUidMap.get(rdResourceName) != null) {
			newRDResource = false;
		}

		// check the need to add a new resource based on associated role's
		// disply name
		boolean newRoleReource = false;
		String roleResourceName = null;
		if (ref_edRole != null
				&& rolesToUidMap
						.get((roleResourceName = getDisplayName(ref_edRole))) == null
				&& !roleResourceName.equalsIgnoreCase(rdResourceName)) {
			newRoleReource = true;
		}

		// create a resource for the RoleDescriptor
		if (newRDResource) {
			Resource aRes = projectFactory.createResource();

			aRes.setUID(BigInteger.valueOf(res_uid));
			aRes.setID(BigInteger.valueOf(res_uid));
			aRes.setName(rdResourceName);
			proj.getResources().getResource().add(aRes);

			rolesToUidMap.put(rdResourceName, BigInteger.valueOf(res_uid));
			res_uid++;
		}

		// create a resource for the underlying associated role too
		if (newRoleReource) {
			Resource aRes = projectFactory.createResource();

			aRes.setUID(BigInteger.valueOf(res_uid));
			aRes.setID(BigInteger.valueOf(res_uid));
			aRes.setName(roleResourceName);
			proj.getResources().getResource().add(aRes);

			rolesToUidMap.put(roleResourceName, BigInteger.valueOf(res_uid));
			res_uid++;
		}
	}

	/**
	 * Generates the MS Project WBS for a capability pattern or delivery
	 * process.
	 * 
	 * @param process
	 *            a capability pattern or delivery process
	 * @param project
	 *            an object to store the generated Microsoft Project WBS
	 * @throws Exception
	 *             if an error occurs while generating the Microsoft Project WBS
	 */
	protected void generateProjectTasks(Process process, Project project)
			throws Exception {
		ComposedAdapterFactory adapterFactory = null;
		try {
			adapterFactory = TngAdapterFactory.INSTANCE
					.createWBSComposedAdapterFactory();
			contentProvider = new AdapterFactoryContentProvider(
					adapterFactory);
			if (process instanceof CapabilityPattern) {
				generateProjectTask(contentProvider,
						(BreakdownElement) process, 1,
						project);
			} else {
				List breakdownElements = process.getBreakdownElements();
				if (breakdownElements.size() > 0) {
					for (int i = 0; i < breakdownElements.size(); i++) {
						if (debug) {
							System.out.println("$$$ 1st level WBS = " //$NON-NLS-1$
								+ breakdownElements.get(i));
						}
						generateProjectTask(contentProvider,
							(BreakdownElement) breakdownElements.get(i), 1,
							project);
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
			throw e;
		} finally {
			if (adapterFactory != null) {
				adapterFactory.dispose();
			}
		}
	}

	/**
	 * Generates the Microsft Project task for a breakdown element.
	 * 
	 * @param contentProvider
	 *            a content provider
	 * @param breakdownElement
	 *            a breakdown element
	 * @param strBuf
	 *            a <code>StringBuffer</code> to store the generated XML
	 */
	protected void generateProjectTask(
			IStructuredContentProvider contentProvider,
			BreakdownElement breakdownElement, int taskOLevel, Project proj)
			throws Exception {
		if (breakdownElement instanceof WorkProductDescriptor
				|| breakdownElement instanceof RoleDescriptor
				|| breakdownElement.getSuppressed().booleanValue()
				|| (exportOnlyPlannedElements && !breakdownElement
						.getIsPlanned().booleanValue())) {
			return;
		}

		boolean isSubTasksPlanned = false;
		if (exportOnlyPlannedElements && breakdownElement instanceof Activity
				&& contentProvider != null) {
			Object[] elements = contentProvider.getElements(breakdownElement);
			for (int i = 0; i < elements.length; i++) {
				Object element = elements[i];
				if (element instanceof Activity) {
					if (!exportOnlyPlannedElements
							|| ((Activity) element).getIsPlanned()
									.booleanValue()) {
						isSubTasksPlanned = true;
						break;
					}
				} else if (element instanceof ActivityWrapperItemProvider) {
					Object wrappedElement = TngUtil.unwrap(element);
					if (wrappedElement instanceof Activity) {
						if (!exportOnlyPlannedElements
								|| ((Activity) wrappedElement).getIsPlanned()
										.booleanValue()) {
							isSubTasksPlanned = true;
							break;
						}
					}
				} else if (element instanceof TaskDescriptor) {
					TaskDescriptor descriptor = (TaskDescriptor) element;
					if (!exportOnlyPlannedElements
							|| descriptor.getIsPlanned().booleanValue()) {
						isSubTasksPlanned = true;
						break;
					}
				} else if (element instanceof TaskDescriptorWrapperItemProvider) {
					Object wrappedElement = TngUtil.unwrap(element);
					if (wrappedElement instanceof TaskDescriptor) {
						TaskDescriptor descriptor = (TaskDescriptor) wrappedElement;
						if (!exportOnlyPlannedElements
								|| descriptor.getIsPlanned().booleanValue()) {
							isSubTasksPlanned = true;
							break;
						}
					}
				}
			}
		}

		// create a task for the WorkBreakdownElement
		// decided just to check the isPlanned flag on the WBS
		boolean isPlannedTask = breakdownElement.getIsPlanned().booleanValue();
		Task newTask = null;
		if (!exportOnlyPlannedElements || exportOnlyPlannedElements
				&& isPlannedTask) {
			newTask = addTask((WorkBreakdownElement) breakdownElement,
					taskOLevel, proj);
		}

		if (exportOnlyPlannedElements && newTask != null
				&& breakdownElement instanceof Activity
				&& contentProvider != null && !isSubTasksPlanned) {
			ArrayList rollupRoles = new ArrayList();
			boolean rb = calculateRollupRoles(contentProvider,
					breakdownElement, rollupRoles);
			// if the whole subtree of the breakdownElemnt is not planned
			// then roll up all the roles
			if (!rb) {
				HashSet rolesSet = new HashSet(rollupRoles);
				for (Iterator iter = rolesSet.iterator(); iter.hasNext();) {
					String roleName = (String) iter.next();
					addAssignment(roleName, newTask.getUID().intValue(), proj);
				}
			}
		}

		if (contentProvider != null) {
			Object[] elements = contentProvider.getElements(breakdownElement);
			for (int i = 0; i < elements.length; i++) {
				Object element = elements[i];
				if (element instanceof BreakdownElementWrapperItemProvider) {
					BreakdownElementWrapperItemProvider provider = (BreakdownElementWrapperItemProvider) element;
					Object value = provider.getValue();
					generateProjectTask(contentProvider,
							(BreakdownElement) value, taskOLevel + 1, proj);
				} else if (element instanceof WorkBreakdownElement) {
					generateProjectTask(contentProvider,
							(WorkBreakdownElement) element, taskOLevel + 1,
							proj);
				}
			}
		}
	}

	/**
	 * Adds a task to a Microsoft Project.
	 * 
	 * @param element
	 *            a work breakdown element
	 * @param taskOLevel
	 *            the outline level
	 * @param project
	 *            a Microsoft Project object
	 * @return the newly added task
	 * @throws Exception
	 *             if an error occurrs while performing the operation
	 */
	protected Task addTask(WorkBreakdownElement element, int taskOLevel,
			Project project) throws Exception {
		if (element == null) {
			return null;
		}

		Task task = projectFactory.createTask();
		task.setName(getDisplayName(element));
		task.setOutlineLevel(BigInteger.valueOf(taskOLevel));
		task.setUID(BigInteger.valueOf(task_uid));
		task.setID(BigInteger.valueOf(task_uid));
		task.setType(BigInteger.valueOf(0));
		
		GregorianCalendar gcDate = new GregorianCalendar();
//		gcDate.add(GregorianCalendar.DATE, 80);
	    Date startDate = gcDate.getTime();
		task.setStart(startDate);
		
//		task.setDuration("PT20H0M0S");

		String notes = getBriefDescription(element);
		if (notes != null || notes.trim().length() > 0) {
			task.setNotes(notes);
		}

		if (element instanceof Milestone) {
			task.setMilestone(true);
			task.setDurationFormat(BigInteger.valueOf(7));
		}
		
		// Assign the task to all the associated roles.
		List rolesList = null;
		if (element instanceof TaskDescriptor) {
			rolesList = getRolesForTaskD((TaskDescriptor) element);
		} else if (element instanceof org.eclipse.epf.uma.Task) {
			rolesList = getRolesForTask((org.eclipse.epf.uma.Task) element);
		}
		if (rolesList != null) {
			for (Iterator iter = rolesList.iterator(); iter.hasNext();) {
				String roleName = (String) iter.next();
				addAssignment(roleName, task_uid, project);
			}
		}

		// Add the generated URL link to the task.
		if (publishContentSite) {
			String linkURL = getElementURL(element);
			if (element instanceof TaskDescriptor) {
				TaskDescriptor taskDescriptor = (TaskDescriptor) element;
				org.eclipse.epf.uma.Task ref_edTask = taskDescriptor.getTask();
				linkURL = getElementURL(ref_edTask);
				referencedTasks.add(ref_edTask);
			}
			task.setHyperlinkAddress(linkURL);
		}

		// aTask.setType(new BigInteger("1"));
		// aTask.setStart(new Date());
		// aTask.setSummary(false);
		// aTask.setConstraintType(new BigInteger("2"));

		// Store the task's predecessors.
		storeTaskPredecessors(element);

		// Add the task to the Microsoft Project object.
		project.getTasks().getTask().add(task);

		wbsToUidMap.put(element.getGuid(), BigInteger.valueOf(task_uid));
		taskToGuidMap.put(BigInteger.valueOf(task_uid), element.getGuid());

		task_uid++;

		return task;
	}

	private String getBriefDescription(WorkBreakdownElement element) {
		if (element == null)
			return null;

		String briefDesc = element.getBriefDescription();
		if ((briefDesc == null || briefDesc.trim().length() <= 0)) {
			if (element instanceof TaskDescriptor) {
				TaskDescriptor taskDescriptor = (TaskDescriptor) element;
				org.eclipse.epf.uma.Task ref_edTask = taskDescriptor.getTask();
				if (ref_edTask != null)
					briefDesc = ref_edTask.getBriefDescription();
			}
		}

		return briefDesc;
	}

	private void addAssignment(String resName, int taskUid, Project proj) {
		Assignment assignment = projectFactory.createAssignment();

		assignment.setUID(BigInteger.valueOf(assign_uid));
		assignment.setTaskUID(BigInteger.valueOf(taskUid));
		BigInteger resID = (BigInteger) rolesToUidMap.get(resName);
		assignment.setResourceUID(resID);

		proj.getAssignments().getAssignment().add(assignment);

		assign_uid++;
	}

	private List getRolesForTaskD(TaskDescriptor taskDescriptor) {
		ArrayList rolesList = new ArrayList();

		RoleDescriptor roleDescrp = taskDescriptor.getPerformedPrimarilyBy();
		if (roleDescrp != null) {
			rolesList.add(getDisplayName(roleDescrp));
		}
		List roleDescrpList = taskDescriptor.getAdditionallyPerformedBy();
		for (Iterator iter = roleDescrpList.iterator(); iter.hasNext();) {
			roleDescrp = (RoleDescriptor) iter.next();
			rolesList.add(getDisplayName(roleDescrp));
		}

		return rolesList;
	}

	private List getRolesForTask(org.eclipse.epf.uma.Task umaTask) {
		ArrayList rolesList = new ArrayList();

		Role role = umaTask.getPerformedBy();
		if (role != null) {
			rolesList.add(getDisplayName(role));
		}
		List list = umaTask.getAdditionallyPerformedBy();
		for (Iterator iter = list.iterator(); iter.hasNext();) {
			role = (Role) iter.next();
			rolesList.add(getDisplayName(role));
		}

		return rolesList;
	}

	private boolean calculateRollupRoles(
			IStructuredContentProvider contentProvider,
			BreakdownElement breakdownElement, List rollupRoles) {

		boolean isAnySubTaskPlanned = false;

		Object[] elements = contentProvider.getElements(breakdownElement);
		for (int i = 0; i < elements.length; i++) {
			Object element = elements[i];
			if (element instanceof Activity) {
				if (!exportOnlyPlannedElements
						|| ((Activity) element).getIsPlanned().booleanValue()) {
					isAnySubTaskPlanned = true;
				} else {
					isAnySubTaskPlanned = calculateRollupRoles(contentProvider,
							(BreakdownElement) element, rollupRoles);
				}
			} else if (element instanceof ActivityWrapperItemProvider) {
				Object wrappedElement = TngUtil.unwrap(element);
				if (wrappedElement instanceof Activity) {
					if (!exportOnlyPlannedElements
							|| ((Activity) wrappedElement).getIsPlanned()
									.booleanValue()) {
						isAnySubTaskPlanned = true;
					} else {
						isAnySubTaskPlanned = calculateRollupRoles(
								contentProvider,
								(BreakdownElement) wrappedElement, rollupRoles);
					}
				}
			} else if (element instanceof TaskDescriptor) {
				TaskDescriptor descriptor = (TaskDescriptor) element;
				if (!exportOnlyPlannedElements
						|| descriptor.getIsPlanned().booleanValue()) {
					isAnySubTaskPlanned = true;
				} else {
					rollupRoles
							.addAll(getRolesForTaskD((TaskDescriptor) element));
				}
			} else if (element instanceof TaskDescriptorWrapperItemProvider) {
				Object wrappedElement = TngUtil.unwrap(element);
				if (wrappedElement instanceof TaskDescriptor) {
					TaskDescriptor descriptor = (TaskDescriptor) wrappedElement;
					if (!exportOnlyPlannedElements
							|| descriptor.getIsPlanned().booleanValue()) {
						isAnySubTaskPlanned = true;
					} else {
						rollupRoles
								.addAll(getRolesForTaskD((TaskDescriptor) wrappedElement));
					}
				}
			}
		}

		return isAnySubTaskPlanned;
	}

	/**
	 * Retrieves a work breakdown element's predecessors, stores them in an
	 * <ocde>ArrayList</code>, and put it into a map.
	 * 
	 * @param element
	 *            a work breakdown element
	 */
	private void storeTaskPredecessors(WorkBreakdownElement element) {
		if (element == null) {
			return;
		}
		List predecessors = element.getLinkToPredecessor();
		ArrayList predList = new ArrayList();
		if (predecessors != null && predecessors.size() > 0) {
			for (Iterator i = predecessors.iterator(); i.hasNext();) {
				WorkOrder workOrder = (WorkOrder) i.next();
				BreakdownElement predecessor = workOrder.getPred();
				if (predecessor != null) {
					String predGuid = predecessor.getGuid();
					if (predGuid != null)
						predList.add(predGuid);
				}
			}
		}
		if (debug) {
			System.out.println("$$$ predlist for " + element.getName() + " = " + predList.toString());
		}
		predListMap.put(element.getGuid(), predList);
	}

	protected void generateLinks(Process process, Project project)
			throws Exception {
		EList tasks = project.getTasks().getTask();
		for (Iterator iter = tasks.iterator(); iter.hasNext();) {
			Task task = (Task) iter.next();
			BigInteger taskUid = task.getUID();
			// skip the MS project hidden task with uid = 0
			if (taskUid.intValue() == 0)
				continue;
			String wbsGuid = (String) taskToGuidMap.get(taskUid);
			ArrayList predList = (ArrayList) predListMap.get(wbsGuid);
			if (predList != null) {
				for (Iterator iterator = predList.iterator(); iterator
						.hasNext();) {
					String predGuid = (String) iterator.next();
					BigInteger predTaskUid = (BigInteger) wbsToUidMap
							.get(predGuid);

					if (predTaskUid == null
							|| predTaskUid.intValue() == taskUid.intValue()) {
						continue;
					}

					PredecessorLink preLink = projectFactory
							.createPredecessorLink();
					task.getPredecessorLink().add(preLink);
					preLink.setPredecessorUID(predTaskUid);
					preLink.setType(new BigInteger("1")); //$NON-NLS-1$
					preLink.setCrossProject(false);
					preLink.setLinkLag(new BigInteger("0")); //$NON-NLS-1$
					preLink.setLagFormat(new BigInteger("7")); //$NON-NLS-1$
				}
			}
		}
	}

	/**
	 * Returns the generated HTML content file URL for a method element.
	 * 
	 * @param element
	 *            a method element
	 * @return the URL of the generated HTML content file
	 */
	protected String getElementURL(MethodElement element) {
		String elementPath = null;
		String elementFileName = null;
		try {
			elementPath = ResourceHelper.getElementPath(element);
			elementFileName = ResourceHelper.getFileName(element,
					ResourceHelper.FILE_EXT_HTML);
		} catch (Exception e) {
			e.printStackTrace();
			return ""; //$NON-NLS-1$
		}
		if (elementPath != null && elementFileName != null) {
			elementPath = elementPath.replace('\\', '/');
			elementFileName = elementFileName.replace('\\', '/');
			String url = contentFolderName
					+ "/" + elementPath + elementFileName; //$NON-NLS-1$
			return abTargetDir + File.separator + url;
		} else {
			return ""; //$NON-NLS-1$
		}
	}

	/**
	 * Returns the display name of a method element.
	 * 
	 * @param element
	 *            a method element
	 * @return the display name of the given element
	 */
	protected String getDisplayName(MethodElement element) {
		String name = null;
		if (element instanceof BreakdownElement) {
			name = ProcessUtil.getPresentationName((BreakdownElement) element);
		} else if (element instanceof DescribableElement) {
			name = ((DescribableElement) element).getPresentationName();
		}
		if (name == null || name.length() == 0) {
			name = element.getName();
		}
		return XMLUtil.escapeAttr(name);
	}

	/**
	 * Publishes the selected configuration associated with the exported
	 * process.
	 * 
	 * @return <code>true</code> if the configuration was published
	 *         successfully, <code>false</code> otherwise
	 * @throws Exception
	 *             if an error occurs while publishing the configuration
	 */
	protected boolean publishConfiguration() throws Exception {
		ExportMSPXMLOperation operation = new ExportMSPXMLOperation(publishMgr);
		PublishProgressMonitorDialog dlg = new PublishProgressMonitorDialog(
				Display.getCurrent().getActiveShell(), publishMgr
						.getViewBuilder());
		boolean success = UserInteractionHelper.runWithProgress(operation, dlg,
				true, ExportMSPResources.exportMSPWizard_title);
		if (operation.getException() != null) {
			throw operation.getException();
		}
		return success && !dlg.getProgressMonitor().isCanceled();
	}

	/**
	 * Copies a source file to a destination file. The destination file is
	 * created automatically if it odes not exists.
	 * 
	 * @param src
	 *            the source file
	 * @param tgt
	 *            the target file
	 * @throws IOException
	 *             if an error occurs while copy the file
	 */
	private void copy(File src, File dst) throws IOException {
		InputStream in = new FileInputStream(src);
		OutputStream out = new FileOutputStream(dst);

		// Transfer bytes from in to out
		byte[] buf = new byte[1024];
		int len;
		while ((len = in.read(buf)) > 0) {
			out.write(buf, 0, len);
		}
		in.close();
		out.close();
	}

	/**
	 * Prints the trace information for the Microsoft Project.
	 */
	private static void printMSProject(Project project) {
		System.out.println("\n$$$ read-in project = " + project); //$NON-NLS-1$

		EList tasks = project.getTasks().getTask();
		for (Iterator iter = tasks.iterator(); iter.hasNext();) {
			Task element = (Task) iter.next();
			System.out.println("$$$ a task = " + element); //$NON-NLS-1$
		}

		EList resources = project.getResources().getResource();
		for (Iterator iter = resources.iterator(); iter.hasNext();) {
			org.eclipse.epf.msproject.Resource element = (org.eclipse.epf.msproject.Resource) iter
					.next();
			System.out.println("$$$ a resource = " + element); //$NON-NLS-1$
		}

		EList assignments = project.getAssignments().getAssignment();
		for (Iterator iter = assignments.iterator(); iter.hasNext();) {
			Assignment element = (Assignment) iter.next();
			System.out.println("$$$ an assignment = " + element); //$NON-NLS-1$
		}

		System.out.println("$$$===\n"); //$NON-NLS-1$
	}

}
