/* ====================================================================
 * 
 * The ObjectStyle Group Software License, Version 1.0 
 *
 * Copyright (c) 2007 The ObjectStyle Group 
 * and individual authors of the software.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer. 
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution, if
 *    any, must include the following acknowlegement:  
 *       "This product includes software developed by the 
 *        ObjectStyle Group (http://objectstyle.org/)."
 *    Alternately, this acknowlegement may appear in the software itself,
 *    if and wherever such third-party acknowlegements normally appear.
 *
 * 4. The names "ObjectStyle Group" and "Cayenne" 
 *    must not be used to endorse or promote products derived
 *    from this software without prior written permission. For written 
 *    permission, please contact andrus@objectstyle.org.
 *
 * 5. Products derived from this software may not be called "ObjectStyle"
 *    nor may "ObjectStyle" appear in their names without prior written
 *    permission of the ObjectStyle Group.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE OBJECTSTYLE GROUP OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the ObjectStyle Group.  For more
 * information on the ObjectStyle Group, please see
 * <http://objectstyle.org/>.
 *
 */
package org.objectstyle.wolips.refactoring;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Status;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
import org.eclipse.jdt.internal.corext.refactoring.changes.RenameCompilationUnitChange;
import org.eclipse.jdt.internal.corext.util.Messages;
import org.eclipse.ltk.core.refactoring.Change;
import org.eclipse.ltk.core.refactoring.ChangeDescriptor;
import org.eclipse.ltk.core.refactoring.CompositeChange;
import org.eclipse.ltk.core.refactoring.RefactoringChangeDescriptor;
import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
import org.eclipse.ltk.core.refactoring.TextFileChange;
import org.eclipse.ltk.core.refactoring.resource.RenameResourceChange;
import org.eclipse.text.edits.MultiTextEdit;
import org.eclipse.text.edits.ReplaceEdit;
import org.objectstyle.wolips.locate.LocateException;
import org.objectstyle.wolips.locate.LocatePlugin;
import org.objectstyle.wolips.locate.result.LocalizedComponentsLocateResult;

public final class RenameWOComponentChange extends CompositeChange {

	public static IPath renamedResourcePath(IPath path, String newName) {
		return path.removeLastSegments(1).append(newName);
	}

	private final String _comment;

	private final RefactoringDescriptor _descriptor;

	private final String _newName;

	private final IPath _resourcePath;

	private final boolean _renameClass;
	
	private final LocatePlugin locate = LocatePlugin.getDefault();
	
	private ICompilationUnit _compilationUnit;
	
	private IFile _groovyFile;

	private RenameWOComponentChange(RefactoringDescriptor descriptor, IPath resourcePath, String newName, String comment, boolean renameClass) {
		super("Rename WOComponent");
		_descriptor = descriptor;
		_resourcePath = resourcePath;
		_newName = newName;
		_comment = comment;
		_renameClass = renameClass;
		try {
			createChanges();
		} catch (OperationCanceledException e) {
			e.printStackTrace();
		} catch (CoreException e) {
			e.printStackTrace();
		}
	}

	public RenameWOComponentChange(RefactoringDescriptor descriptor, IResource resource, String newName, String comment, boolean renameClass) {
		this(descriptor, resource.getFullPath(), newName, comment, renameClass);
	}

	public RenameWOComponentChange(RefactoringDescriptor descriptor, IResource resource, String newName, String comment) {
		this(descriptor, resource.getFullPath(), newName, comment, true);
	}

	public ChangeDescriptor getDescriptor() {
		if (_descriptor != null)
			return new RefactoringChangeDescriptor(_descriptor);
		return super.getDescriptor();
	}

	public Object getModifiedElement() {
		return getResource();
	}

	public String getName() {
		return "Rename WOComponent " + getOldName() + " to " + getNewName();
	}

	public String getNewName() {
		return locate.fileNameWithoutExtension(_newName);
	}

	public String getOldName() {
		return locate.fileNameWithoutExtension(_resourcePath.lastSegment());
	}

	private IResource getResource() {
		return ResourcesPlugin.getWorkspace().getRoot().findMember(_resourcePath);
	}

	public RefactoringStatus isValid(final IProgressMonitor pm) throws CoreException {
		IResource resource = getResource();
		if (resource == null || !resource.exists()) {
			return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.RenameResourceChange_does_not_exist, _resourcePath.toString()));
		}
		return new RefactoringStatus();
	}

	
	public Change perform(final IProgressMonitor pm) throws CoreException {
		try {
			pm.beginTask("Renaming WO Component", 1);
			IPath newPath = renamedResourcePath(_resourcePath, _newName);
			String oldName = _resourcePath.lastSegment();
			super.perform(pm);
			return new RenameWOComponentChange(null, newPath, oldName, _comment, _renameClass);
		} finally {
			pm.done();
		}
	}
	
	private void createChanges() throws CoreException, OperationCanceledException {
		IFile oldApiFile = null;
		IFolder[] oldWoFolders = new IFolder[0];
	
		try {
			LocalizedComponentsLocateResult result = 
				LocatePlugin.getDefault().getLocalizedComponentsLocateResult(getResource());
			if (result != null) {
				oldWoFolders = result.getComponents();
				oldApiFile = result.getDotApi();
			}
		} catch (LocateException e) {
			throw new CoreException(new Status(IStatus.ERROR, RefactoringPlugin.getDefault().getBundleID(), IStatus.ERROR, "Could not locate component: " + _resourcePath.lastSegment(), null)); //$NON-NLS-1$
		}
		if (oldApiFile != null || oldWoFolders.length > 0) {
			CompositeChange compositeChange = new CompositeChange("Rename WOComponent Files");
			if (oldApiFile != null) {
				String javaFileName = getCompilationUnit().getElementName();
				if (!renameJavaClassInAPIFile(compositeChange, oldApiFile, javaFileName)) {
					int dotPosition = javaFileName.indexOf('.');
					if (dotPosition > -1) {
						javaFileName = javaFileName.substring(0, dotPosition);
						renameJavaClassInAPIFile(compositeChange, oldApiFile, javaFileName);
					}
				}
				compositeChange.add(new RenameResourceChange(oldApiFile.getFullPath(), getNewName() + ".api"));
			}
			for (int i = 0; i < oldWoFolders.length; i++) {
				IFolder oldWoFolder = oldWoFolders[i];
				if (oldWoFolder == null) {
					continue;
				}
				CompositeChange renameWoFolderChange = new CompositeChange("Rename " + oldWoFolder.getName());

				String[] renameExtensions = { ".html", ".wod", ".woo", ".xml", ".xhtml" };
				for (int j = 0; j < renameExtensions.length; j++) {
					IFile woFile = oldWoFolder.getFile(getOldName() + renameExtensions[j]);
					if (woFile.exists()) {
						renameWoFolderChange.add(new RenameResourceChange(woFile.getFullPath(), getNewName() + renameExtensions[j]));
					}
				}
				renameWoFolderChange.add(new RenameResourceChange(oldWoFolder.getFullPath(), getNewName() + ".wo"));
				compositeChange.add(renameWoFolderChange);

			}
			add(compositeChange);
			if (_renameClass) {
				if (getCompilationUnit() != null) {
					add(new RenameCompilationUnitChange(getCompilationUnit(), getNewName() + ".java"));
				}
				//if (getGroovyFile() != null) {
				//TODO Refactoring of groovy file would go here if there was a refactoring for it
				//     We could just rename it, but that would not refactor the contents
				//     A refactoring plugin is in development http://sifsstud4.hsr.ch/trac/GroovyRefactoring/wiki
				//}
			}
		}
	}
	
	private boolean renameJavaClassInAPIFile(CompositeChange compositeChange, IFile oldApiFile, String javaFileName) throws CoreException {
		TextFileChange apiTextFileChange = new TextFileChange("Rename Java class name in API file", oldApiFile);
		apiTextFileChange.setTextType("xml");
		String apiContent = apiTextFileChange.getCurrentContent(null);
		int javaFileNamePosition = apiContent.indexOf(javaFileName);
		if (javaFileNamePosition > -1) {
			apiTextFileChange.setEdit(new ReplaceEdit(javaFileNamePosition, javaFileName.length(), getNewName() + ".java"));
			compositeChange.add(apiTextFileChange);
			return true;
		}
		return false;
	}
	
	private ICompilationUnit getCompilationUnit() {
		if (_compilationUnit != null) {
			return _compilationUnit;
		}
		try {
			LocalizedComponentsLocateResult results = locate.getLocalizedComponentsLocateResult(getResource());
			if (results != null) {
				IType javaType = results.getDotJavaType();
				if (javaType != null)
					_compilationUnit = javaType.getCompilationUnit();				
			}
		} catch (CoreException e) {
			e.printStackTrace();
		} catch (LocateException e) {
			e.printStackTrace();
		}
		return _compilationUnit;
	}
	
	private IFile getGroovyFile() {
		if (_groovyFile != null) {
			return _groovyFile;
		}
		try {
			LocalizedComponentsLocateResult results = locate.getLocalizedComponentsLocateResult(getResource());
			if (results != null) {
				_groovyFile = results.getDotGroovy();
			}
		} catch (CoreException e) {
			e.printStackTrace();
		} catch (LocateException e) {
			e.printStackTrace();
		}
		return _groovyFile;
	}
}
