//------------------------------------------------------------------------------
// 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.library.util;

import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.epf.common.serviceability.MsgBox;
import org.eclipse.epf.common.utils.StrUtil;
import org.eclipse.epf.library.LibraryPlugin;
import org.eclipse.epf.library.LibraryResources;
import org.eclipse.epf.library.LibraryService;
import org.eclipse.epf.library.edit.LibraryEditResources;
import org.eclipse.epf.library.edit.command.DeleteMethodElementCommand;
import org.eclipse.epf.library.edit.util.TngUtil;
import org.eclipse.epf.library.edit.validation.IValidator;
import org.eclipse.epf.library.edit.validation.internal.ContentElementNameValidator;
import org.eclipse.epf.library.edit.validation.internal.ValidatorFactory;
import org.eclipse.epf.persistence.MethodLibraryPersister;
import org.eclipse.epf.uma.ContentDescription;
import org.eclipse.epf.uma.ContentElement;
import org.eclipse.epf.uma.ContentPackage;
import org.eclipse.epf.uma.DescribableElement;
import org.eclipse.epf.uma.Guidance;
import org.eclipse.epf.uma.UmaFactory;
import org.eclipse.epf.uma.UmaPackage;
import org.eclipse.epf.uma.util.ContentDescriptionFactory;
import org.eclipse.epf.uma.util.IMethodLibraryPersister;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IInputValidator;
import org.eclipse.jface.dialogs.InputDialog;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.window.Window;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.dialogs.ListDialog;


public class ConvertGuidanceType {

	static IValidator validator;
	private static int[] compatibleGuidances = { UmaPackage.CONCEPT,
	// UmaPackage.CHECKLIST,
			// UmaPackage.EXAMPLE,
			UmaPackage.GUIDELINE,
			// UmaPackage.ESTIMATE,
			// UmaPackage.ESTIMATING_METRIC,
			// UmaPackage.ESTIMATION_CONSIDERATIONS,
			// UmaPackage.REPORT,
			// UmaPackage.TEMPLATE,
			UmaPackage.SUPPORTING_MATERIAL,
	// UmaPackage.TOOL_MENTOR,
	// UmaPackage.WHITEPAPER,
	// UmaPackage.TERM_DEFINITION,
	// UmaPackage.PRACTICE,
	// UmaPackage.REUSABLE_ASSET
	};

	private static List compatibleGuidancesList = new ArrayList();
	static {
		for (int i = 0; i < compatibleGuidances.length; i++)
			compatibleGuidancesList.add(new Integer(compatibleGuidances[i]));
	}

	public static Guidance convertGuidance(Guidance oldGuidance, Shell shell,
			DeleteMethodElementCommand command) {
		if (shell == null)
			shell = Display.getCurrent().getActiveShell();
		ListDialog dlg = new ListDialog(shell);
		dlg.setHeightInChars(5);
		dlg.setContentProvider(new ArrayContentProvider());
		dlg.setLabelProvider(new LabelProvider() {
			public String getText(Object element) {
				switch (((Integer) element).intValue()) {
				// TODO: refactor these strings (and this whole dialog) into
				// library.ui
				case UmaPackage.CONCEPT:
					return LibraryResources.concept_text; 
				case UmaPackage.CHECKLIST:
					return LibraryResources.checklist_text; 
				case UmaPackage.EXAMPLE:
					return LibraryResources.example_text; 
				case UmaPackage.GUIDELINE:
					return LibraryResources.guideline_text; 
				case UmaPackage.ESTIMATION_CONSIDERATIONS:
					return LibraryResources.estimationConsiderations_text;
				case UmaPackage.REPORT:
					return LibraryResources.report_text; 
				case UmaPackage.TEMPLATE:
					return LibraryResources.template_text; 
				case UmaPackage.SUPPORTING_MATERIAL:
					return LibraryResources.supportingMaterial_text;
				case UmaPackage.TOOL_MENTOR:
					return LibraryResources.toolMentor_text;
				case UmaPackage.WHITEPAPER:
					return LibraryResources.whitepaper_text;
				case UmaPackage.TERM_DEFINITION:
					return LibraryResources.termDefinition_text;
				case UmaPackage.PRACTICE:
					return LibraryResources.practice_text; 
				case UmaPackage.REUSABLE_ASSET:
					return LibraryResources.reusableAsset_text;
				default:
					return LibraryResources.unknownGuidance_text;
				}

			}
		});
		List newGuidanceTypeList = getValidNewGuidanceTypes(oldGuidance);
		if (newGuidanceTypeList == null) {
			LibraryPlugin
					.getDefault()
					.getMsgDialog()
					.displayError(
							LibraryResources.convertGuidanceError_title,
							LibraryResources.unsupportedGuidanceTypeError_msg,
							NLS.bind(LibraryResources.unsupportedGuidanceTypeError_reason, StrUtil.toLower(TngUtil.getTypeText(oldGuidance)))); 
			return null;
		}
		dlg.setInput(newGuidanceTypeList);
		dlg.setTitle(LibraryResources.convertGuidanceDialog_title);
		dlg.setMessage(LibraryResources.convertGuidanceDialog_text);
		if (dlg.open() == Dialog.CANCEL)
			return null;

		Object[] selectionResult = dlg.getResult();
		if (selectionResult == null)
			return null;
		int chosenGuidance = ((Integer) selectionResult[0]).intValue();
		Guidance newGuidance = doConvert(oldGuidance, chosenGuidance, command);

		return newGuidance;
	}

	private static Guidance doConvert(Guidance oldGuidance,
			int newGuidanceType, DeleteMethodElementCommand command) {
		// removedReferencesMap = new HashMap();
		Guidance newGuidance = null;
		try {
			File oldGuidancePathFile = new File(LibraryService.getInstance()
					.getCurrentMethodLibraryPath(), MethodLibraryPersister
					.getElementPath(oldGuidance));
			
			// create new guidance
			newGuidance = createNewGuidance(newGuidanceType);

			// TODO: store oldGuidance's features here, and don't set them until
			// after oldguidance is deleted
			// copy all data from oldGuidance to newGuidance
			Iterator iter = oldGuidance.eClass().getEAllStructuralFeatures()
					.iterator();
			while (iter.hasNext()) {
				EStructuralFeature feature = (EStructuralFeature) iter.next();
				Object o = oldGuidance.eGet(feature);
				if (feature.getFeatureID() != UmaPackage.DESCRIBABLE_ELEMENT__PRESENTATION
						&& feature.getFeatureID() != UmaPackage.METHOD_ELEMENT__GUID)
					newGuidance.eSet(feature, o);
			}

			// store Guid here
			String oldGuidanceGuid = oldGuidance.getGuid();

			// store old guidance's eContainer
			ContentPackage oldGuidanceContainer = (ContentPackage) oldGuidance
					.eContainer();
			
			//Verify unique name for the changing guidance.
			//https://bugs.eclipse.org/bugs/show_bug.cgi?id=152552
			try{
				String name = checkName(oldGuidance, newGuidance);
				if(name != null) {
					newGuidance.setName(name);
				}
			}catch(Exception e){
				return null;
			}
			
			// set container
			oldGuidanceContainer.getContentElements().add(newGuidance);

			// create presentation  -- modify to use appropriate instead of deprecated.
			ContentDescription newContentDesc = ContentDescriptionFactory
					.createContentDescription((DescribableElement)newGuidance);
			
			newGuidance.setPresentation(newContentDesc);

			File newGuidancePathFile = new File(LibraryService.getInstance()
					.getCurrentMethodLibraryPath(), MethodLibraryPersister
					.getElementPath(newGuidance));
			ContentResourceScanner scanner = new ContentResourceScanner(
					oldGuidancePathFile);
			scanner.setTargetRootPath(newGuidancePathFile);

			// copy all presentation data from oldGuidance to newGuidance
			ContentDescription oldContentDesc = oldGuidance.getPresentation();
			iter = oldContentDesc.eClass().getEAllAttributes().iterator();
			while (iter.hasNext()) {
				EAttribute attrib = (EAttribute) iter.next();
				Object o = oldContentDesc.eGet(attrib);
				if (o instanceof String && ((String) o).trim().length() > 0) {
					// process links
					scanner.resolveResources(newGuidance, (String) o, ""); //$NON-NLS-1$
					// scanner.resolveResources(newGuidance, (String) o,
					// MethodLibraryPersister.getElementPath(newGuidance));
					// //$NON-NLS-1$
				}
				newContentDesc.eSet(attrib, o);
			}

			// delete old guidance here - if fails, roll back by deleting new
			// guidance
			command.execute();

			if (!command.executed || command.failed) {
				// delete failed, or user selected cancel on the "delete
				// references" dialog
				// clean up new guidance

				// unset container
				oldGuidanceContainer.getContentElements().remove(newGuidance);

				newGuidance = null;
				newContentDesc = null;
				return null;
			}

			// set new guidance's GUID
			newGuidance.setGuid(oldGuidanceGuid);

			// TODO: update editor contents (eg, if a Task with a Supporting
			// Material copyright has open
			// editor, the copyright field is not updated when that SM is
			// changed to a Concept)

			// TODO: on rollback, library may be dirty because of the
			// container/contentdesc changes we've done
			// fix this by storing all the old guidance's info, then only create
			// new guidance after old one has
			// been deleted.

			// save new guidance and its presentation
			IMethodLibraryPersister.FailSafeMethodLibraryPersister persister = ContentDescriptionFactory
					.getMethodLibraryPersister().getFailSafePersister();
			try {
				if (newGuidance.eResource() != null)
					persister.save(newGuidance.eResource());
				if (newGuidance.getPresentation().eResource() != null)
					persister.save(newGuidance.getPresentation().eResource());
				if (oldGuidanceContainer.eResource() != null)
					persister.save(oldGuidanceContainer.eResource());

				persister.commit();

			} catch (Exception e) {
				persister.rollback();
				command.undo();
				LibraryPlugin
						.getDefault()
						.getMsgDialog()
						.displayError(
								LibraryResources.convertGuidanceError_title,
								NLS.bind(LibraryResources.saveConvertedGuidanceError_msg, newGuidance.getName()), 
								LibraryResources.error_reason,
								e);
				newGuidance = null;
			}
		} catch (Exception ex) {
			command.undo();
			LibraryPlugin
					.getDefault()
					.getMsgDialog()
					.displayError(
							LibraryResources.convertGuidanceError_title,
							NLS.bind(LibraryResources.convertGuidanceError_msg, newGuidance.getName()), 
							LibraryResources.error_reason, 
							ex);
			newGuidance = null;
		}
		return newGuidance;
	}

	/**
	 * @param oldGuidance
	 * @param newGuidance
	 * @return
	 */
	private static String checkName(Guidance oldGuidance, Guidance newGuidance) {
		// TODO Auto-generated method stub
		
		
		validator = new ContentElementNameValidator(oldGuidance.eContainer()
				, UmaPackage.eINSTANCE.getContentPackage_ContentElements(),
				(ContentElement) newGuidance, new ValidatorFactory.TypeFilter(newGuidance));
		
		String msg = validator.isValid(newGuidance.getName());
		
		if (msg != null) {
			String featureTxt = TngUtil.getFeatureText(UmaPackage.eINSTANCE
					.getNamedElement_Name());
			String title = LibraryEditResources.resolveNameConflictDialog_title; //$NON-NLS-1$			
			String dlgMsg = NLS.bind(
					LibraryEditResources.resolveNameConflictDialog_text,
					StrUtil.toLower(featureTxt), newGuidance.getName());
			String currentValue = (String) newGuidance.eGet(UmaPackage.eINSTANCE
					.getNamedElement_Name());

			IInputValidator inputValidator = new IInputValidator() {
				public String isValid(String newText) {
					return validator.isValid(newText);
				}
			};

			InputDialog inputDlg = new InputDialog(
					MsgBox.getDefaultShell(), title, dlgMsg, currentValue,
					inputValidator);
			if (inputDlg.open() == Window.CANCEL) {
				throw new OperationCanceledException();
			}
			return inputDlg.getValue();
		}
		return null;
	}

	private static Guidance createNewGuidance(int newGuidanceType) {
		switch (newGuidanceType) {
		case UmaPackage.CONCEPT:
			return UmaFactory.eINSTANCE.createConcept();
		case UmaPackage.CHECKLIST:
			return UmaFactory.eINSTANCE.createChecklist();
		case UmaPackage.EXAMPLE:
			return UmaFactory.eINSTANCE.createExample();
		case UmaPackage.GUIDELINE:
			return UmaFactory.eINSTANCE.createGuideline();
		case UmaPackage.ESTIMATION_CONSIDERATIONS:
			return UmaFactory.eINSTANCE.createEstimationConsiderations();
		case UmaPackage.REPORT:
			return UmaFactory.eINSTANCE.createReport();
		case UmaPackage.TEMPLATE:
			return UmaFactory.eINSTANCE.createTemplate();
		case UmaPackage.SUPPORTING_MATERIAL:
			return UmaFactory.eINSTANCE.createSupportingMaterial();
		case UmaPackage.TOOL_MENTOR:
			return UmaFactory.eINSTANCE.createToolMentor();
		case UmaPackage.WHITEPAPER:
			return UmaFactory.eINSTANCE.createWhitepaper();
		case UmaPackage.TERM_DEFINITION:
			return UmaFactory.eINSTANCE.createTermDefinition();
		case UmaPackage.PRACTICE:
			return UmaFactory.eINSTANCE.createPractice();
		case UmaPackage.REUSABLE_ASSET:
			return UmaFactory.eINSTANCE.createReusableAsset();
		default:
			return null;
		}
	}

	public static List getValidNewGuidanceTypes(Guidance oldGuidance) {
		if (oldGuidance == null)
			return null;
		Integer oldGuidanceClassID = new Integer(oldGuidance.eClass()
				.getClassifierID());
		if (!compatibleGuidancesList.contains(oldGuidanceClassID))
			return null;
		List guidanceList = new ArrayList();
		for (int i = 0; i < compatibleGuidances.length; i++) {
			Integer compatibleGuidanceTypeClassID = new Integer(
					compatibleGuidances[i]);
			if (!oldGuidanceClassID.equals(compatibleGuidanceTypeClassID))
				guidanceList.add(compatibleGuidanceTypeClassID);
		}
		return guidanceList;
	}

}
