/*******************************************************************************
 * Copyright (c) 2012 IBM Corporation and others.
 * All rights reserved. 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.wst.xml.ui.internal.contentassist;

import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.contentassist.IContextInformation;
import org.eclipse.jface.text.link.LinkedModeModel;
import org.eclipse.jface.text.link.LinkedModeUI;
import org.eclipse.jface.text.link.LinkedPosition;
import org.eclipse.jface.text.link.LinkedPositionGroup;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.ui.texteditor.link.EditorLinkedModeUI;
import org.eclipse.wst.sse.ui.internal.contentassist.CustomCompletionProposal;
import org.eclipse.wst.xml.ui.internal.Logger;

/**
 * A completion proposal that is capable of establishing linked positions within
 * inserted markup. 
 *
 */
public class MarkupCompletionProposal extends CustomCompletionProposal {

	private IRegion fSelectedRegion = null;

	public MarkupCompletionProposal(String replacementString, int replacementOffset, int replacementLength, int cursorPosition, Image image, String displayString, IContextInformation contextInformation, String additionalProposalInfo, int relevance) {
		super(replacementString, replacementOffset, replacementLength, cursorPosition, image, displayString, contextInformation, additionalProposalInfo, relevance);
	}

	public MarkupCompletionProposal(String replacementString, int replacementOffset, int replacementLength, int cursorPosition, Image image, String displayString, IContextInformation contextInformation, String additionalProposalInfo, int relevance, boolean updateReplacementLengthOnValidate) {
		super(replacementString, replacementOffset, replacementLength, cursorPosition, image, displayString, null, contextInformation, additionalProposalInfo, relevance, updateReplacementLengthOnValidate);
	}

	public MarkupCompletionProposal(String replacementString, int replacementOffset, int replacementLength, int cursorPosition, Image image, String displayString, String alternateMatch, IContextInformation contextInformation, String additionalProposalInfo, int relevance, boolean updateReplacementLengthOnValidate) {
		super(replacementString, replacementOffset, replacementLength, cursorPosition, image, displayString, alternateMatch, contextInformation, additionalProposalInfo, relevance, updateReplacementLengthOnValidate);
	}

	public void apply(ITextViewer viewer, char trigger, int stateMask, int offset) {
		super.apply(viewer, trigger, stateMask, offset);

		getLinkedPositions(viewer);
	}

	/**
	 * Sets up linked positions and installs them on the viewer.
	 * 
	 */
	protected void getLinkedPositions(ITextViewer viewer) {
		final String replacement = getReplacementString();
		final IDocument document = viewer.getDocument();
		final int length = replacement.length();
		boolean inAttribute = false, hasGroup = false;
		int offset = 0;
		char attType = 0;
		int exitPosition = -1;
		LinkedModeModel model = new LinkedModeModel();

		try {
			for (int i = 0; i < length; i++) {
				final char c = replacement.charAt(i);
				switch (c) {
					case '=':
						break;
					case '\'':
					case '\"':
						if (!inAttribute) {
							offset = i;
							attType = c;
							inAttribute = true;
						}
						else {
							// Found matching quotes establishing an attribute value region
							if (attType == c && replacement.charAt(i - 1) != '\\') {
								inAttribute = false; // Record position length
								addPosition(model, document, getReplacementOffset() + offset + 1, i - offset - 1);
								hasGroup = true;
							}
						}
						break;
					case '>':
						if (!inAttribute && exitPosition == -1) {
							exitPosition = getReplacementOffset() + i + 1;
						}
						break;
				}
			}
			if (hasGroup) {
				model.forceInstall();
				final LinkedModeUI ui= new EditorLinkedModeUI(model, viewer);
				ui.setExitPosition(viewer, exitPosition < 0 ? getReplacementOffset() + getReplacementLength() + replacement.length() - 1 : exitPosition, 0, Integer.MAX_VALUE);
				ui.setCyclingMode(LinkedModeUI.CYCLE_WHEN_NO_PARENT);
				ui.setDoContextInfo(true);
				ui.enter();
				fSelectedRegion = ui.getSelectedRegion();
			}
		}
		catch (BadLocationException e) {
			Logger.logException(e);
		}
	}

	/**
	 * Adds a {@link LinkedPosition} to its own position group. This group is then added to the model
	 * @param model the linked model for this proposal
	 * @param document the document the content assist is operating upon
	 * @param offset the offset to establish the {@link LinkedPosition}
	 * @param length the length of the {@link LinkedPosition}
	 * @throws BadLocationException
	 */
	private void addPosition(LinkedModeModel model, IDocument document, int offset, int length) throws BadLocationException {
		final LinkedPositionGroup group = new LinkedPositionGroup();
		group.addPosition(new LinkedPosition(document, offset, length, LinkedPositionGroup.NO_STOP));
		model.addGroup(group);
	}

	public Point getSelection(IDocument document) {
		// Attempt to return the selection based on the selected region from the LinkedModeUI
		if (fSelectedRegion == null)
			return super.getSelection(document);
		return new Point(fSelectedRegion.getOffset(), fSelectedRegion.getLength());
	}
}