/*******************************************************************************
 * Copyright (c) 2006, 2008 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.jface.text;

import org.eclipse.text.edits.CopyTargetEdit;
import org.eclipse.text.edits.DeleteEdit;
import org.eclipse.text.edits.InsertEdit;
import org.eclipse.text.edits.MalformedTreeException;
import org.eclipse.text.edits.MoveTargetEdit;
import org.eclipse.text.edits.ReplaceEdit;
import org.eclipse.text.edits.TextEdit;
import org.eclipse.text.edits.TextEditProcessor;
import org.eclipse.text.edits.TextEditVisitor;
import org.eclipse.text.edits.UndoEdit;

/**
 * A text edit processor that brackets the application of edits into a document rewrite session.
 *
 * @since 3.3
 */
public final class RewriteSessionEditProcessor extends TextEditProcessor {
	/** The threshold for <em>large</em> text edits. */
	private static final int THRESHOLD= 1000;

	/**
	 * Text edit visitor that estimates the compound size of an edit tree in characters.
	 */
	private static final class SizeVisitor extends TextEditVisitor {
		int fSize= 0;

		@Override
		public boolean visit(CopyTargetEdit edit) {
			fSize += edit.getLength();
			return super.visit(edit);
		}

		@Override
		public boolean visit(DeleteEdit edit) {
			fSize += edit.getLength();
			return super.visit(edit);
		}

		@Override
		public boolean visit(InsertEdit edit) {
			fSize += edit.getText().length();
			return super.visit(edit);
		}

		@Override
		public boolean visit(MoveTargetEdit edit) {
			fSize += edit.getLength();
			return super.visit(edit);
		}

		@Override
		public boolean visit(ReplaceEdit edit) {
			fSize += Math.max(edit.getLength(), edit.getText().length());
			return super.visit(edit);
		}
	}

	/**
	 * Constructs a new edit processor for the given document.
	 *
	 * @param document the document to manipulate
	 * @param root the root of the text edit tree describing the modifications. By passing a text
	 *        edit a a text edit processor the ownership of the edit is transfered to the text edit
	 *        processors. Clients must not modify the edit (e.g adding new children) any longer.
	 * @param style {@link TextEdit#NONE}, {@link TextEdit#CREATE_UNDO} or
	 *        {@link TextEdit#UPDATE_REGIONS})
	 */
	public RewriteSessionEditProcessor(IDocument document, TextEdit root, int style) {
		super(document, root, style);
	}

	@Override
	public UndoEdit performEdits() throws MalformedTreeException, BadLocationException {
		IDocument document= getDocument();
		if (!(document instanceof IDocumentExtension4))
			return super.performEdits();

		IDocumentExtension4 extension= (IDocumentExtension4) document;
		boolean isLargeEdit= isLargeEdit(getRoot());
		DocumentRewriteSessionType type= isLargeEdit ? DocumentRewriteSessionType.UNRESTRICTED : DocumentRewriteSessionType.UNRESTRICTED_SMALL;

		DocumentRewriteSession session= extension.startRewriteSession(type);
		try {
			return super.performEdits();
		} finally {
			extension.stopRewriteSession(session);
		}
	}

	/**
	 * Returns <code>true</code> if the passed edit is considered <em>large</em>,
	 * <code>false</code> otherwise.
	 *
	 * @param edit the edit to check
	 * @return <code>true</code> if <code>edit</code> is considered <em>large</em>,
	 *         <code>false</code> otherwise
	 * @since 3.3
	 */
	public static boolean isLargeEdit(TextEdit edit) {
		SizeVisitor sizeVisitor= new SizeVisitor();
		edit.accept(sizeVisitor);
		return sizeVisitor.fSize > THRESHOLD;
	}

}
