/*******************************************************************************
 * Copyright (c) 2000, 2011 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.jdt.internal.ui.compare;

import java.lang.reflect.InvocationTargetException;
import java.util.Map;

import org.eclipse.team.core.TeamException;
import org.eclipse.team.ui.history.ElementLocalHistoryPageSource;
import org.eclipse.team.ui.history.HistoryPageCompareEditorInput;

import org.eclipse.swt.widgets.Shell;

import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;

import org.eclipse.core.resources.IFile;

import org.eclipse.core.filebuffers.FileBuffers;
import org.eclipse.core.filebuffers.ITextFileBuffer;
import org.eclipse.core.filebuffers.ITextFileBufferManager;
import org.eclipse.core.filebuffers.LocationKind;

import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.ISelection;

import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.TextUtilities;

import org.eclipse.compare.CompareConfiguration;
import org.eclipse.compare.CompareUI;
import org.eclipse.compare.IStreamContentAccessor;
import org.eclipse.compare.ITypedElement;

import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMember;
import org.eclipse.jdt.core.ISourceRange;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.SourceRange;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration;
import org.eclipse.jdt.core.dom.BodyDeclaration;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.EnumDeclaration;
import org.eclipse.jdt.core.dom.NodeFinder;
import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;

import org.eclipse.jdt.internal.corext.dom.ASTNodes;
import org.eclipse.jdt.internal.corext.util.Resources;

import org.eclipse.jdt.internal.ui.IJavaHelpContextIds;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.internal.ui.javaeditor.JavaEditor;
import org.eclipse.jdt.internal.ui.util.ExceptionHandler;


/**
 * Provides "Replace from local history" for Java elements.
 */
class JavaReplaceWithEditionActionImpl extends JavaHistoryActionImpl {

	protected boolean fPrevious= false;

	JavaReplaceWithEditionActionImpl(boolean previous) {
		super(true);
		fPrevious= previous;
	}

	@Override
	public void run(ISelection selection) {

		Shell shell= getShell();

		final IMember input= getEditionElement(selection);
		if (input == null) {
			MessageDialog.openInformation(shell, CompareMessages.ReplaceFromHistory_title, CompareMessages.ReplaceFromHistory_invalidSelectionMessage);
			return;
		}

		final IFile file= getFile(input);
		if (file == null) {
			showError();
			return;
		}

		IStatus status= Resources.makeCommittable(file, shell);
		if (!status.isOK()) {
			return;
		}

		if (fPrevious) {
			String errorTitle= CompareMessages.ReplaceFromHistory_title;
			String errorMessage= CompareMessages.ReplaceFromHistory_internalErrorMessage;
			try {
				ITypedElement ti = ElementLocalHistoryPageSource.getPreviousEdition(file, input);
				if (ti == null) {
					MessageDialog.openInformation(shell, errorTitle, CompareMessages.ReplaceFromHistory_parsingErrorMessage);
					return;
				}
				replace(input, file, ti);
			} catch (TeamException e) {
				ExceptionHandler.handle(e, shell, errorTitle, errorMessage);
			}
		} else {
			JavaElementHistoryPageSource pageSource = JavaElementHistoryPageSource.getInstance();
			CompareConfiguration cc = new CompareConfiguration();
			cc.setLeftEditable(false);
			cc.setRightEditable(false);
			HistoryPageCompareEditorInput ci = new HistoryPageCompareEditorInput(cc, pageSource, input) {
				@Override
				protected void performReplace(Object selectedElement) {
					if (selectedElement instanceof ITypedElement) {
						JavaReplaceWithEditionActionImpl.this.replace(input, file, (ITypedElement)selectedElement);
					}
				}
			};
			ci.setReplace(true);
			ci.setTitle(CompareMessages.JavaReplaceWithEditionActionImpl_0);
			ci.setHelpContextId(IJavaHelpContextIds.REPLACE_ELEMENT_WITH_HISTORY_DIALOG);
			CompareUI.openCompareDialog(ci);
		}
	}

	public void replace(IMember input, IFile file, ITypedElement element) {

		Shell shell= getShell();

		String errorTitle= CompareMessages.ReplaceFromHistory_title;
		String errorMessage= CompareMessages.ReplaceFromHistory_internalErrorMessage;

		// get the document where to insert the text
		IPath path= file.getFullPath();
		ITextFileBufferManager bufferManager= FileBuffers.getTextFileBufferManager();
		ITextFileBuffer textFileBuffer= null;
		try {
			bufferManager.connect(path, LocationKind.IFILE, null);
			textFileBuffer= bufferManager.getTextFileBuffer(path, LocationKind.IFILE);
			IDocument document= textFileBuffer.getDocument();
			performReplace(input, file, textFileBuffer, document, element);
	 	} catch(InvocationTargetException ex) {
			ExceptionHandler.handle(ex, shell, errorTitle, errorMessage);

		} catch(InterruptedException ex) {
			// shouldn't be called because is not cancelable
			Assert.isTrue(false);

		} catch(CoreException ex) {
			ExceptionHandler.handle(ex, shell, errorTitle, errorMessage);

		} finally {
			try {
				if (textFileBuffer != null)
					bufferManager.disconnect(path, LocationKind.IFILE, null);
			} catch (CoreException e) {
				JavaPlugin.log(e);
			}
		}
	}

	private void performReplace(IMember input, IFile file,
			ITextFileBuffer textFileBuffer, IDocument document, ITypedElement ti)
			throws CoreException, JavaModelException,
			InvocationTargetException, InterruptedException {

		if (ti instanceof IStreamContentAccessor) {

			boolean inEditor= beingEdited(file);

			String content= JavaCompareUtilities.readString((IStreamContentAccessor)ti);
			String newContent= trimTextBlock(content, TextUtilities.getDefaultLineDelimiter(document), input.getJavaProject());
			if (newContent == null) {
				showError();
				return;
			}

			ICompilationUnit compilationUnit= input.getCompilationUnit();
			CompilationUnit root= parsePartialCompilationUnit(compilationUnit);


			ISourceRange nameRange= input.getNameRange();
			if (nameRange == null)
				nameRange= input.getSourceRange();
			// workaround for bug in getNameRange(): for AnnotationMembers length is negative
			int length= nameRange.getLength();
			if (length < 0)
				length= 1;
			ASTNode node2= NodeFinder.perform(root, new SourceRange(nameRange.getOffset(), length));
			ASTNode node;
			if (node2.getNodeType() == ASTNode.INITIALIZER)
				node= node2;
			else
				node= ASTNodes.getParent(node2, BodyDeclaration.class);
			if (node == null)
				node= ASTNodes.getParent(node2, AnnotationTypeDeclaration.class);
			if (node == null)
				node= ASTNodes.getParent(node2, EnumDeclaration.class);

			//ASTNode node= getBodyContainer(root, input);
			if (node == null) {
				showError();
				return;
			}

			ASTRewrite rewriter= ASTRewrite.create(root.getAST());
			rewriter.replace(node, rewriter.createStringPlaceholder(newContent, node.getNodeType()), null);

			if (inEditor) {
				JavaEditor je= getEditor(file);
				if (je != null)
					je.setFocus();
			}

			Map<String, String> options= null;
			IJavaProject javaProject= compilationUnit.getJavaProject();
			if (javaProject != null)
				options= javaProject.getOptions(true);
			applyChanges(rewriter, document, textFileBuffer, getShell(), inEditor, options);

		}
	}

	private void showError() {
		MessageDialog.openError(getShell(), CompareMessages.ReplaceFromHistory_title, CompareMessages.ReplaceFromHistory_internalErrorMessage);
	}
}
