/*******************************************************************************
 * Copyright (c) 2005, 2015 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
 *     Tom Eicher <eclipse@tom.eicher.name> - [content assist] prefix complete casted method proposals - https://bugs.eclipse.org/bugs/show_bug.cgi?id=247547
 *     Mohamed Azab (Mentor Graphics) - Bug 438549. Add mechanism for parameter guessing.
 *******************************************************************************/
package org.eclipse.cdt.internal.ui.text.contentassist;

import org.eclipse.cdt.core.dom.ast.IFunction;
import org.eclipse.cdt.core.dom.ast.IParameter;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.link.ILinkedModeListener;
import org.eclipse.jface.text.link.LinkedModeModel;
import org.eclipse.jface.text.link.LinkedModeUI;
import org.eclipse.jface.text.link.LinkedModeUI.ExitFlags;
import org.eclipse.jface.text.link.LinkedModeUI.IExitPolicy;
import org.eclipse.jface.text.link.LinkedPosition;
import org.eclipse.jface.text.link.LinkedPositionGroup;
import org.eclipse.swt.events.VerifyEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.ui.texteditor.link.EditorLinkedModeUI;

/**
 * This is a modified version of org.eclipse.jdt.internal.ui.text.java.JavaMethodCompletionProposal
 *
 * This class adds a linked mode function compilation proposal with exit policy.
 */
public class FunctionCompletionProposal extends CCompletionProposal {
	private boolean fHasParametersComputed;
	private boolean fHasParameters;
	protected IParameter[] fFunctionParameters;
	protected int fInvocationOffset;
	protected int fParseOffset;
	protected ITranslationUnit fTranslationUnit;
	protected IDocument fDocument;

	public FunctionCompletionProposal(String replacementString, int replacementOffset, int replacementLength,
			Image image, String displayString, String idString, int relevance, ITextViewer viewer, IFunction function,
			int invocationOffset, int parseOffset, ITranslationUnit translationUnit, IDocument document) {
		super(replacementString, replacementOffset, replacementLength, image, displayString, idString, relevance,
				viewer);
		fFunctionParameters = function.getParameters();
		fInvocationOffset = invocationOffset;
		fParseOffset = parseOffset;
		fTranslationUnit = translationUnit;
		fDocument = document;
	}

	@Override
	public void apply(IDocument document, char trigger, int offset) {
		if (trigger == ' ' || trigger == '(')
			trigger = '\0';
		super.apply(document, trigger, offset);
		if (hasParameters()) {
			setUpLinkedMode(document, ')');
		} else if (getReplacementString().endsWith(";")) { //$NON-NLS-1$
			setUpLinkedMode(document, ';');
		}
	}

	/**
	 * Returns {@code true} if the method has any parameters, {@code true} if it has no parameters
	 */
	protected final boolean hasParameters() {
		if (!fHasParametersComputed) {
			fHasParametersComputed = true;
			fHasParameters = computeHasParameters();
		}
		return fHasParameters;
	}

	private boolean computeHasParameters() {
		return (fFunctionParameters != null && fFunctionParameters.length != 0);
	}

	protected static class ExitPolicy implements IExitPolicy {
		final char fExitCharacter;

		public ExitPolicy(char exitCharacter) {
			fExitCharacter = exitCharacter;
		}

		@Override
		public ExitFlags doExit(LinkedModeModel environment, VerifyEvent event, int offset, int length) {
			if (event.character == fExitCharacter) {
				if (environment.anyPositionContains(offset)) {
					return new ExitFlags(ILinkedModeListener.UPDATE_CARET, false);
				} else {
					return new ExitFlags(ILinkedModeListener.UPDATE_CARET, true);
				}
			}

			switch (event.character) {
			case ';':
				return new ExitFlags(ILinkedModeListener.NONE, true);
			default:
				return null;
			}
		}
	}

	protected void setUpLinkedMode(IDocument document, char closingCharacter) {
		if (fTextViewer != null) {
			int exit = getReplacementOffset() + getReplacementString().length();
			try {
				LinkedPositionGroup group = new LinkedPositionGroup();
				group.addPosition(new LinkedPosition(document, fInvocationOffset, 0, LinkedPositionGroup.NO_STOP));

				LinkedModeModel model = new LinkedModeModel();
				model.addGroup(group);
				model.forceInstall();

				LinkedModeUI ui = new EditorLinkedModeUI(model, fTextViewer);
				ui.setSimpleMode(true);
				ui.setExitPolicy(new ExitPolicy(closingCharacter));
				ui.setExitPosition(fTextViewer, exit, 0, Integer.MAX_VALUE);
				ui.setCyclingMode(LinkedModeUI.CYCLE_NEVER);
				ui.enter();
			} catch (BadLocationException x) {
				CUIPlugin.log(x);
			}
		}
	}
}
