/*
 * Copyright 2000-2013 JetBrains s.r.o.
 *
 * 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.intellij.codeInsight.completion;

import com.intellij.codeInsight.TailType;
import com.intellij.codeInsight.lookup.*;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import com.intellij.psi.impl.source.xml.SchemaPrefixReference;
import com.intellij.psi.impl.source.xml.TagNameReference;
import com.intellij.psi.xml.XmlTag;
import com.intellij.util.Consumer;
import com.intellij.util.ProcessingContext;
import com.intellij.xml.XmlTagNameProvider;
import org.jetbrains.annotations.NotNull;

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

/**
 * @author yole
 */
public class TagNameReferenceCompletionProvider extends CompletionProvider<CompletionParameters> {
  public static LookupElement[] getTagNameVariants(final @NotNull XmlTag tag, final String prefix) {
    List<LookupElement> elements = new ArrayList<>();
    for (XmlTagNameProvider tagNameProvider : XmlTagNameProvider.EP_NAME.getExtensions()) {
      tagNameProvider.addTagNameVariants(elements, tag, prefix);
    }
    return elements.toArray(LookupElement.EMPTY_ARRAY);
  }

  @Override
  protected void addCompletions(@NotNull CompletionParameters parameters,
                                ProcessingContext context,
                                @NotNull final CompletionResultSet result) {
    LegacyCompletionContributor.processReferences(parameters, result, (reference, set) -> {
      if (reference instanceof TagNameReference) {
        collectCompletionVariants((TagNameReference)reference, set);
      }
      else if (reference instanceof SchemaPrefixReference) {
        TagNameReference tagNameReference = ((SchemaPrefixReference)reference).getTagNameReference();
        if (tagNameReference != null && !tagNameReference.isStartTagFlag()) {
          set.consume(createClosingTagLookupElement((XmlTag)tagNameReference.getElement(), true, tagNameReference.getNameElement()));
        }
      }
    });
  }

  public static void collectCompletionVariants(TagNameReference tagNameReference,
                                               Consumer<LookupElement> consumer) {
    PsiElement element = tagNameReference.getElement();
    if (element instanceof XmlTag) {
      if (!tagNameReference.isStartTagFlag()) {
        consumer.consume(createClosingTagLookupElement((XmlTag)element, false, tagNameReference.getNameElement()));
      }
      else {
        XmlTag tag = (XmlTag) element;
        for(LookupElement variant: getTagNameVariants(tag, tag.getNamespacePrefix())) {
          consumer.consume(variant);
        }
      }
    }
  }

  public static LookupElement createClosingTagLookupElement(XmlTag tag, boolean includePrefix, ASTNode nameElement) {
    LookupElementBuilder
      builder = LookupElementBuilder.create(includePrefix || !nameElement.getText().contains(":") ? tag.getName() : tag.getLocalName());
    return LookupElementDecorator.withInsertHandler(
      TailTypeDecorator.withTail(AutoCompletionPolicy.GIVE_CHANCE_TO_OVERWRITE.applyPolicy(builder),
                                 TailType.createSimpleTailType('>')),
      XmlClosingTagInsertHandler.INSTANCE);
  }

}

@@ -907,32 +907,74 @@
 gNameReference;%0A
+import com.intellij.psi.xml.XmlChildRole;%0A
 import com.intel
@@ -993,16 +993,16 @@
 XmlTag;%0A
-
 import c
@@ -2439,32 +2439,33 @@
 TagLookupElement
+s
 ((XmlTag)tagName
@@ -2520,32 +2520,39 @@
 etNameElement())
+.get(0)
 );%0A        %7D%0A   
@@ -2883,33 +2883,45 @@
         
-consumer.consume(
+for (LookupElement variant : 
 createCl
@@ -2941,16 +2941,17 @@
 pElement
+s
 ((XmlTag
@@ -3002,17 +3002,65 @@
 ment()))
-;
+ %7B%0A          consumer.consume(variant);%0A        %7D
 %0A      %7D
@@ -3274,32 +3274,37 @@
   public static 
+List%3C
 LookupElement cr
@@ -3300,16 +3300,17 @@
 pElement
+%3E
  createC
@@ -3331,16 +3331,17 @@
 pElement
+s
 (XmlTag 
@@ -3387,24 +3387,104 @@
 eElement) %7B%0A
+    List%3CLookupElement%3E result = new ArrayList%3C%3E();%0A    while (tag != null) %7B%0A  
     LookupEl
@@ -3496,16 +3496,18 @@
 Builder%0A
+  
       bu
@@ -3639,23 +3639,29 @@
 ));%0A    
-return 
+  result.add(
 LookupEl
@@ -3696,24 +3696,26 @@
 dler(%0A      
+ 
+ 
 TailTypeDeco
@@ -3822,32 +3822,34 @@
                 
+  
 TailType.createS
@@ -3875,16 +3875,18 @@
 ,%0A      
+  
 XmlClosi
@@ -3913,16 +3913,322 @@
 NSTANCE)
+);%0A      if (includePrefix) break;%0A      tag = tag.getParentTag();%0A      if (tag != null &&%0A          (XmlChildRole.CLOSING_TAG_NAME_FINDER.findChild(tag.getNode()) != null %7C%7C%0A           XmlChildRole.START_TAG_START_FINDER.findChild(tag.getNode()) == null)) %7B%0A        break;%0A      %7D%0A    %7D%0A    return result
 ;%0A  %7D%0A%0A%7D

add all closing tag variants (WEB-5082)