/*
 * Copyright 2013 Palantir Technologies, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.palantir.typescript.text;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

import java.util.List;

import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.contentassist.CompletionProposal;
import org.eclipse.jface.text.contentassist.ContentAssistEvent;
import org.eclipse.jface.text.contentassist.ICompletionListener;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
import org.eclipse.jface.text.contentassist.IContextInformation;
import org.eclipse.jface.text.contentassist.IContextInformationValidator;
import org.eclipse.swt.graphics.Image;
import org.eclipse.ui.IPathEditorInput;

import com.google.common.base.CharMatcher;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.palantir.typescript.Images;
import com.palantir.typescript.bridge.language.CompletionEntryDetails;
import com.palantir.typescript.bridge.language.CompletionInfo;
import com.palantir.typescript.bridge.language.ScriptElementKind;

/**
 * This class deals with making auto completions.
 *
 * @author tyleradams
 */
public final class ContentAssistProcessor implements ICompletionListener, IContentAssistProcessor {

    private static final CharMatcher NON_IDENTIFIER = CharMatcher.WHITESPACE.or(CharMatcher.anyOf("(){}[]+-/*=%!<>?&|,;"));

    private final TypeScriptEditor editor;

    private CompletionInfo currentCompletionInfo;
    private int currentOffset;

    public ContentAssistProcessor(TypeScriptEditor editor) {
        checkNotNull(editor);

        this.editor = editor;
    }

    @Override
    public void assistSessionStarted(ContentAssistEvent event) {
    }

    @Override
    public void assistSessionEnded(ContentAssistEvent event) {
        this.currentCompletionInfo = null;
    }

    @Override
    public void selectionChanged(ICompletionProposal proposal, boolean smartToggle) {
    }

    @Override
    public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) {
        checkNotNull(viewer);
        checkArgument(offset >= 0);

        // get the completion info
        if (this.currentCompletionInfo == null || offset < this.currentOffset) {
            IPathEditorInput editorInput = (IPathEditorInput) this.editor.getEditorInput();
            String fileName = editorInput.getPath().toOSString();

            this.currentCompletionInfo = this.editor.getLanguageService().getCompletionsAtPosition(fileName, offset);
            this.currentOffset = this.getOffset(offset);
        }

        // create the completion proposals from the completion entries
        List<CompletionProposal> proposals = Lists.newArrayList();
        if (this.currentCompletionInfo != null) {
            ImmutableList<CompletionEntryDetails> entries = this.currentCompletionInfo.getEntries();

            // get the current prefix
            String prefix;
            try {
                prefix = this.editor.getDocument().get(this.currentOffset, offset - this.currentOffset);
            } catch (BadLocationException e) {
                throw new RuntimeException(e);
            }

            for (CompletionEntryDetails entry : entries) {
                String replacementString = entry.getName();

                // filter the entries to only include the ones matching the current prefix
                if (replacementString.startsWith(prefix)) {
                    int replacementOffset = this.currentOffset;
                    int replacementLength = offset - this.currentOffset;
                    int cursorPosition = replacementString.length();
                    Image image = Images.getImage(entry.getKind(), entry.getKindModifiers());
                    String displayString = getDisplayString(entry);
                    IContextInformation contextInformation = null;
                    String additionalProposalInfo = entry.getDocComment();
                    CompletionProposal proposal = new CompletionProposal(replacementString, replacementOffset, replacementLength,
                        cursorPosition,
                        image, displayString, contextInformation, additionalProposalInfo);

                    proposals.add(proposal);
                }
            }
        }

        return proposals.toArray(new ICompletionProposal[proposals.size()]);
    }

    @Override
    public IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) {
        return new IContextInformation[] {};
    }

    @Override
    public char[] getCompletionProposalAutoActivationCharacters() {
        return new char[] { '.' };
    }

    @Override
    public char[] getContextInformationAutoActivationCharacters() {
        return new char[] {};
    }

    @Override
    public IContextInformationValidator getContextInformationValidator() {
        return null;
    }

    @Override
    public String getErrorMessage() {
        return null;
    }

    private int getOffset(int offset) {
        boolean memberCompletion = this.currentCompletionInfo.isMemberCompletion();

        try {
            for (int i = offset - 1; i >= 0; i--) {
                char character = this.editor.getDocument().getChar(i);

                if ((memberCompletion && character == '.') ||
                        (!memberCompletion && NON_IDENTIFIER.matches(character))) { // TODO
                    return i + 1;
                }
            }
        } catch (BadLocationException e) {
            throw new RuntimeException(e);
        }

        return 0;
    }

    private static String getDisplayString(CompletionEntryDetails completion) {
        String displayString = completion.getName();
        String type = completion.getType();

        if (type != null) {
            ScriptElementKind kind = completion.getKind();

            if (kind == ScriptElementKind.LOCAL_FUNCTION_ELEMENT
                    || kind == ScriptElementKind.MEMBER_FUNCTION_ELEMENT
                    || kind == ScriptElementKind.FUNCTION_ELEMENT) {
                displayString += type;
            } else if (kind == ScriptElementKind.LOCAL_VARIABLE_ELEMENT
                    || kind == ScriptElementKind.MEMBER_VARIABLE_ELEMENT
                    || kind == ScriptElementKind.VARIABLE_ELEMENT) {
                displayString += ": " + type;
            }
        }

        return displayString;
    }
}

@@ -5765,32 +5765,86 @@
 t(int offset) %7B%0A
+        if (this.currentCompletionInfo != null) %7B%0A    
         boolean 
@@ -5912,38 +5912,46 @@
 ion();%0A%0A        
+    
 try %7B%0A
+    
             for 
@@ -5982,24 +5982,28 @@
 = 0; i--) %7B%0A
+    
             
@@ -6074,24 +6074,28 @@
             
+  
+  
 if ((memberC
@@ -6128,16 +6128,20 @@
 '.') %7C%7C%0A
+    
         
@@ -6240,24 +6240,28 @@
             
+    
 return i + 1
@@ -6270,34 +6270,42 @@
                 
+   
+ 
 %7D%0A
+    
             %7D%0A  
@@ -6302,32 +6302,36 @@
       %7D%0A        
+  
+  
 %7D catch (BadLoca
@@ -6353,32 +6353,36 @@
 ) %7B%0A            
+    
 throw new Runtim
@@ -6388,32 +6388,46 @@
 meException(e);%0A
+            %7D%0A
         %7D%0A%0A     

Fix NPE