/*******************************************************************************
 * Copyright (c) 2016-2017 Red Hat Inc. and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Red Hat Inc. - initial API and implementation
 *******************************************************************************/
package org.eclipse.jdt.ls.core.internal.handlers;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.ProgressMonitorWrapper;
import org.eclipse.jdt.core.CompletionProposal;
import org.eclipse.jdt.core.IBuffer;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.ls.core.internal.JDTUtils;
import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin;
import org.eclipse.jdt.ls.core.internal.contentassist.CompletionProposalRequestor;
import org.eclipse.lsp4j.CompletionItem;
import org.eclipse.lsp4j.CompletionList;
import org.eclipse.lsp4j.TextDocumentPositionParams;
import org.eclipse.lsp4j.jsonrpc.messages.Either;

public class CompletionHandler{

	Either<List<CompletionItem>, CompletionList> completion(TextDocumentPositionParams position,
			IProgressMonitor monitor) {
		List<CompletionItem> completionItems = null;
		try {
			ICompilationUnit unit = JDTUtils.resolveCompilationUnit(position.getTextDocument().getUri());
			completionItems = this.computeContentAssist(unit,
					position.getPosition().getLine(),
					position.getPosition().getCharacter(), monitor);
		} catch (OperationCanceledException ignorable) {
			// No need to pollute logs when query is cancelled
			monitor.setCanceled(true);
		} catch (Exception e) {
			JavaLanguageServerPlugin.logException("Problem with codeComplete for " +  position.getTextDocument().getUri(), e);
			monitor.setCanceled(true);
		}
		CompletionList $ = new CompletionList();
		if (monitor.isCanceled()) {
			$.setIsIncomplete(true);
			completionItems = null;
			JavaLanguageServerPlugin.logInfo("Completion request cancelled");
		} else {
			JavaLanguageServerPlugin.logInfo("Completion request completed");
		}
		$.setItems(completionItems == null ? Collections.emptyList() : completionItems);
		return Either.forRight($);
	}

	private List<CompletionItem> computeContentAssist(ICompilationUnit unit, int line, int column, IProgressMonitor monitor) throws JavaModelException {
		CompletionResponses.clear();
		if (unit == null) {
			return Collections.emptyList();
		}
		List<CompletionItem> proposals = new ArrayList<>();

		final int offset = JsonRpcHelpers.toOffset(unit.getBuffer(), line, column);
		CompletionProposalRequestor collector = new CompletionProposalRequestor(unit, offset);
		// Allow completions for unresolved types - since 3.3
		collector.setAllowsRequiredProposals(CompletionProposal.FIELD_REF, CompletionProposal.TYPE_REF, true);
		collector.setAllowsRequiredProposals(CompletionProposal.FIELD_REF, CompletionProposal.TYPE_IMPORT, true);
		collector.setAllowsRequiredProposals(CompletionProposal.FIELD_REF, CompletionProposal.FIELD_IMPORT, true);

		collector.setAllowsRequiredProposals(CompletionProposal.METHOD_REF, CompletionProposal.TYPE_REF, true);
		collector.setAllowsRequiredProposals(CompletionProposal.METHOD_REF, CompletionProposal.TYPE_IMPORT, true);
		collector.setAllowsRequiredProposals(CompletionProposal.METHOD_REF, CompletionProposal.METHOD_IMPORT, true);

		collector.setAllowsRequiredProposals(CompletionProposal.CONSTRUCTOR_INVOCATION, CompletionProposal.TYPE_REF, true);

		collector.setAllowsRequiredProposals(CompletionProposal.ANONYMOUS_CLASS_CONSTRUCTOR_INVOCATION, CompletionProposal.TYPE_REF, true);
		collector.setAllowsRequiredProposals(CompletionProposal.ANONYMOUS_CLASS_DECLARATION, CompletionProposal.TYPE_REF, true);

		collector.setAllowsRequiredProposals(CompletionProposal.TYPE_REF, CompletionProposal.TYPE_REF, true);

		if (offset >-1 && !monitor.isCanceled()) {
			IBuffer buffer = unit.getBuffer();
			if (buffer != null && buffer.getLength() >= offset) {
				IProgressMonitor subMonitor = new ProgressMonitorWrapper(monitor) {
					private final static int TIMEOUT = 1000;
					private long timeLimit;

					@Override
					public void beginTask(String name, int totalWork) {
						timeLimit = System.currentTimeMillis() + TIMEOUT;
					}

					@Override
					public boolean isCanceled() {
						return super.isCanceled() || timeLimit <= System.currentTimeMillis();
					}

				};
				unit.codeComplete(offset, collector, subMonitor);
				if (!subMonitor.isCanceled()) {
					proposals.addAll(collector.getCompletionItems());
				} else {
					monitor.setCanceled(true);
				}
			}
		}

		return proposals;
	}
}

@@ -4438,21 +4438,44 @@
 ate 
-final
+long timeLimit;%0A%09%09%09%09%09private
  static 
 int 
@@ -4470,19 +4470,26 @@
  static 
+f
 in
-t
+al long
  TIMEOUT
@@ -4495,42 +4495,13 @@
 T = 
-1
+5
 000;
-%0A%09%09%09%09%09private long timeLimit;
 %0A%0A%09%09
@@ -4775,16 +4775,27 @@
 %0A%09%09%09%09%7D;%0A
+%09%09%09%09try %7B%0A%09
 %09%09%09%09unit
@@ -4844,44 +4844,8 @@
 r);%0A
-%09%09%09%09if (!subMonitor.isCanceled()) %7B%0A
 %09%09%09%09
@@ -4897,28 +4897,60 @@
 s());%0A%09%09%09%09%7D 
-else
+catch (OperationCanceledException e)
  %7B%0A%09%09%09%09%09moni
@@ -4983,21 +4983,20 @@
 %09%7D%0A%09%09%09%7D%0A
+
 %09%09%7D%0A
-%0A
 %09%09return

Increase Code Assist Timeout