/*******************************************************************************
 * Copyright (c) 2010, 2012 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.propertiesfileeditor;

import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;

import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.custom.VerifyKeyListener;
import org.eclipse.swt.events.VerifyEvent;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;

import org.eclipse.core.resources.IFile;

import org.eclipse.jface.text.DocumentCommand;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IAutoEditStrategy;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentListener;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.jface.text.quickassist.IQuickAssistAssistant;
import org.eclipse.jface.text.quickassist.IQuickAssistProcessor;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.text.source.ISourceViewerExtension3;

import org.eclipse.ui.progress.WorkbenchJob;

/**
 * Auto edit strategy that escapes a character if it cannot be encoded in the .properties file's
 * encoding.
 *
 * <p>
 * A quick assist to escape backslashes is offered iff the pasted text is not perfectly correct for
 * the .properties file, i.e. if the text contains
 * <ul>
 * <li>an invalid escape sequence as defined by
 * {@link PropertiesFileEscapes#containsInvalidEscapeSequence(String)}</li>
 * <li>a character which requires Unicode escapes</li>
 * </ul>
 * </p>
 *
 * @since 3.7
 */
public class PropertiesFileAutoEditStrategy implements IAutoEditStrategy {

	private final IFile fFile;

	private String fCharsetName;

	private CharsetEncoder fCharsetEncoder;

	private final ISourceViewer fSourceViewer;

	public PropertiesFileAutoEditStrategy(IFile file, ISourceViewer sourceViewer) {
		fFile= file;
		fSourceViewer= sourceViewer;
	}

	@Override
	public void customizeDocumentCommand(IDocument document, DocumentCommand command) {
		showProposal(escape(command), document);
	}

	private ICompletionProposal escape(DocumentCommand command) {
		try {
			String charsetName= fFile.getCharset();
			if (!charsetName.equals(fCharsetName)) {
				fCharsetName= charsetName;
				fCharsetEncoder= Charset.forName(fCharsetName).newEncoder();
			}
		} catch (CoreException e) {
			return null;
		}

		String text= command.text;
		boolean escapeUnicodeChars= !fCharsetEncoder.canEncode(text);
		boolean escapeBackslash= (text.length() > 1) && ((escapeUnicodeChars && PropertiesFileEscapes.containsUnescapedBackslash(text)) || PropertiesFileEscapes.containsInvalidEscapeSequence(text));

		if (!escapeUnicodeChars && !escapeBackslash)
			return null;

		command.text= PropertiesFileEscapes.escape(text, false, false, escapeUnicodeChars);
		if (escapeBackslash) {
			String proposalText= PropertiesFileEscapes.escape(text, false, true, escapeUnicodeChars);
			return new EscapeBackslashCompletionProposal(proposalText, command.offset, command.text.length(),
					PropertiesFileEditorMessages.EscapeBackslashCompletionProposal_escapeBackslashesInOriginalString);
		}
		return null;
	}

	private void showProposal(final ICompletionProposal proposal, final IDocument document) {
		if (proposal != null && fSourceViewer instanceof ISourceViewerExtension3) {
			final WorkbenchJob job= new WorkbenchJob(PropertiesFileEditorMessages.PropertiesFileAutoEditStrategy_showQuickAssist) {
				@Override
				public IStatus runInUIThread(IProgressMonitor monitor) {
					IQuickAssistAssistant assistant= ((ISourceViewerExtension3)fSourceViewer).getQuickAssistAssistant();
					IQuickAssistProcessor processor= assistant.getQuickAssistProcessor();
					if (processor instanceof PropertiesCorrectionProcessor) {
						((PropertiesCorrectionProcessor)processor).setProposals(new ICompletionProposal[] { proposal });
						assistant.showPossibleQuickAssists();
					}
					return Status.OK_STATUS;
				}
			};
			job.setSystem(true);
			job.schedule(500);
			final StyledText textWidget= fSourceViewer.getTextWidget();
			textWidget.addVerifyKeyListener(new VerifyKeyListener() {
				@Override
				public void verifyKey(VerifyEvent event) {
					job.cancel();
					textWidget.removeVerifyKeyListener(this);
				}
			});

			final IDocumentListener documentListener= new IDocumentListener() {
				private boolean pasteComplete= false;
				@Override
				public void documentAboutToBeChanged(DocumentEvent event) {
				}
				@Override
				public void documentChanged(DocumentEvent event) {
					if (pasteComplete) {
						job.cancel();
						document.removeDocumentListener(this);
					}
					pasteComplete= true;
				}
			};
			document.addDocumentListener(documentListener);
		}
	}
}
