/*******************************************************************************
 * Copyright (c) 2014, 2015 Google, Inc 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:
 * 	   Sergey Prigogin (Google) - initial API and implementation
 *******************************************************************************/
package org.eclipse.cdt.internal.ui.refactoring.rename;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceProxy;
import org.eclipse.core.resources.IResourceProxyVisitor;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.ltk.core.refactoring.Change;
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext;
import org.eclipse.ltk.core.refactoring.participants.ISharableParticipant;
import org.eclipse.ltk.core.refactoring.participants.MoveArguments;
import org.eclipse.ltk.core.refactoring.participants.MoveParticipant;
import org.eclipse.ltk.core.refactoring.participants.RefactoringArguments;

/**
 * Updates include statements and include guards in response to a file or a folder move.
 */
public class HeaderFileMoveParticipant extends MoveParticipant implements ISharableParticipant {
	private Map<IResource, MoveArguments> movedResources;
	private Change change;

	public HeaderFileMoveParticipant() {
	}

	@Override
	protected boolean initialize(Object element) {
		addElement(element, getArguments());
		return movedResources != null;
	}

	@Override
	public void addElement(Object element, RefactoringArguments arguments) {
		if (element instanceof IResource && arguments instanceof MoveArguments) {
			if (movedResources == null)
				movedResources = new HashMap<>();
			movedResources.put((IResource) element, (MoveArguments) arguments);
		}
	}

	@Override
	public RefactoringStatus checkConditions(IProgressMonitor pm, CheckConditionsContext context)
			throws OperationCanceledException {
		try {
			if (movedResources == null)
				return RefactoringStatus.create(Status.OK_STATUS);

			// Maps the affected files to new, not yet existing, files.
			final Map<IFile, IFile> movedFiles = new HashMap<>();

			for (Map.Entry<IResource, MoveArguments> entry : movedResources.entrySet()) {
				IResource movedResource = entry.getKey();
				MoveArguments args = entry.getValue();
				if (!args.getUpdateReferences())
					continue;
				if (movedResource.isLinked())
					continue;

				Object destinationResource = args.getDestination();
				if (!(destinationResource instanceof IContainer))
					continue;
				final IContainer destination = (IContainer) destinationResource;
				final IPath destinationLocation = destination.getLocation();
				if (destinationLocation.equals(movedResource.getLocation().removeLastSegments(1)))
					continue;
		
				if (movedResource instanceof IFolder) {
					IFolder folder = (IFolder) movedResource;
					final int prefixLength = folder.getFullPath().segmentCount() - 1;
					folder.accept(new IResourceProxyVisitor() {
						@Override
						public boolean visit(IResourceProxy proxy) throws CoreException {
							if (proxy.isLinked())
								return false;
							if (proxy.getType() == IResource.FILE) {
								IFile file = (IFile) proxy.requestResource();
								movedFiles.put(file, destination.getFile(file.getFullPath().removeFirstSegments(prefixLength)));
								return false;
							}
							return true;
						}
					}, IResource.NONE);
				} else if (movedResource instanceof IFile) {
					IFile file = (IFile) movedResource;
					movedFiles.put(file, destination.getFile(new Path(movedResource.getName())));
				}
			}

			HeaderFileReferenceAdjuster includeAdjuster = new HeaderFileReferenceAdjuster(movedFiles,
					Collections.<IContainer, IContainer>emptyMap(),	getProcessor());
			change = includeAdjuster.createChange(context, pm);
		} catch (CoreException e) {
			return RefactoringStatus.create(e.getStatus());
		} finally {
			pm.done();
		}
		return RefactoringStatus.create(Status.OK_STATUS);
	}

	@Override
	public Change createPreChange(IProgressMonitor pm) throws CoreException, OperationCanceledException {
		change = RenameParticipantHelper.postprocessParticipantChange(change, this);
		pm.done();
		return change;
	}

	@Override
	public Change createChange(IProgressMonitor pm) throws CoreException, OperationCanceledException {
		pm.done();
		return null;
	}

	@Override
	public String getName() {
		return RenameMessages.HeaderFileMoveParticipant_name;
	}
}
