/*
 * Copyright 2000-2017 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.codeInspection.htmlInspections;

import com.intellij.codeInsight.daemon.XmlErrorMessages;
import com.intellij.codeInsight.daemon.impl.analysis.XmlHighlightVisitor;
import com.intellij.codeInspection.LocalQuickFix;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiErrorElement;
import com.intellij.psi.PsiWhiteSpace;
import com.intellij.psi.XmlElementFactory;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.html.HtmlTag;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.XmlTag;
import com.intellij.psi.xml.XmlText;
import com.intellij.psi.xml.XmlToken;
import com.intellij.psi.xml.XmlTokenType;
import com.intellij.xml.util.HtmlUtil;
import com.intellij.xml.util.XmlTagUtil;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;

/**
 * @author Bas Leijdekkers
 */
public class HtmlMissingClosingTagInspection extends HtmlLocalInspectionTool {

  @Override
  protected void checkTag(@NotNull XmlTag tag, @NotNull ProblemsHolder holder, boolean isOnTheFly) {
    if (!(tag instanceof HtmlTag) || !XmlHighlightVisitor.shouldBeValidated(tag)) {
      return;
    }
    PsiElement child = tag.getLastChild();
    if (child instanceof PsiErrorElement) {
      return;
    }
    final XmlToken tagNameElement = XmlTagUtil.getStartTagNameElement(tag);
    if (tagNameElement == null) {
      return;
    }
    final String tagName = tagNameElement.getText();
    if (HtmlUtil.isSingleHtmlTag(tagName)) {
      return;
    }
    if (isToken(child, XmlTokenType.XML_TAG_END)) {
      child = child.getPrevSibling();
      if (isToken(child, XmlTokenType.XML_NAME) && tagName.equals(child.getText())) {
        child = child.getPrevSibling();
        if (isToken(child, XmlTokenType.XML_END_TAG_START)) {
          return;
        }
      }
    }

    holder.registerProblem(tagNameElement, XmlErrorMessages.message("element.missing.end.tag"),
                           new MissingClosingTagFix(tagName));
  }

  private static boolean isToken(PsiElement element, IElementType tokenType) {
    if (!(element instanceof XmlToken)) {
      return false;
    }
    final XmlToken token = (XmlToken)element;
    return token.getTokenType() == tokenType;
  }

  private static class MissingClosingTagFix implements LocalQuickFix {

    private final String myName;

    public MissingClosingTagFix(String name) {
      myName = name;
    }

    @Nls
    @NotNull
    @Override
    public String getName() {
      return XmlErrorMessages.message("add.named.closing.tag", myName);
    }

    @Nls
    @NotNull
    @Override
    public String getFamilyName() {
      return XmlErrorMessages.message("add.closing.tag");
    }

    @Override
    public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
      final PsiElement element = descriptor.getPsiElement();
      if (!(element instanceof XmlToken)) {
        return;
      }
      final PsiElement parent = element.getParent();
      if (!(parent instanceof XmlTag)) {
        return;
      }
      String text = parent.getText();
      if (text.contains("\n")) {
        int cutoff = -1;
        for (int i = text.length() - 1; i > 0; i--) {
          final char c = text.charAt(i);
          if (c == ' ' || c == '\t') continue;
          if (c == '\n') cutoff = i;
          else break;
        }
        if (cutoff > 0) {
          text = text.substring(0, cutoff);
        }
      }
      final String replacementText = text + "</" + element.getText() + ">";
      final XmlElementFactory factory = XmlElementFactory.getInstance(project);
      final XmlTag newTag = factory.createHTMLTagFromText(replacementText);
      final PsiElement child = parent.getLastChild().copy();
      CodeStyleManager.getInstance(project).performActionWithFormatterDisabled((Runnable)() -> {
        final PsiElement replacement = parent.replace(newTag);
        if (child instanceof XmlText) {
          final PsiElement grandChild = child.getLastChild();
          if (grandChild instanceof PsiWhiteSpace) {
            final XmlTag dummyTag = factory.createHTMLTagFromText("<dummy>" + grandChild.getText() + "</dummy>");
            final XmlText whitespace = PsiTreeUtil.getChildOfType(dummyTag, XmlText.class);
            assert whitespace != null;
            replacement.getParent().addAfter(whitespace, replacement);
          }
        }
      });
    }
  }
}

@@ -1232,51 +1232,8 @@
 ag;%0A
-import com.intellij.psi.tree.IElementType;%0A
 impo
@@ -1385,50 +1385,8 @@
 en;%0A
-import com.intellij.psi.xml.XmlTokenType;%0A
 impo
@@ -1878,16 +1878,22 @@
   %7D%0A    
+final 
 PsiEleme
@@ -2215,306 +2215,56 @@
 ame)
-) %7B%0A      return;%0A    %7D%0A    if (isToken(child, XmlTokenType.XML_TAG_END)) %7B%0A      child = child.getPrevSibling();%0A      if (isToken(child, XmlTokenType.XML_NAME) && tagName.equals(child.getText())) %7B%0A        child = child.getPrevSibling();%0A        if (isToken(child, XmlTokenType.XML_END_TAG_START)
+ %7C%7C XmlTagUtil.getEndTagNameElement(tag) != null
 ) %7B%0A
@@ -2265,36 +2265,32 @@
 ll) %7B%0A      
- 
-   
 return;%0A
         %7D%0A  
@@ -2277,34 +2277,16 @@
 return;%0A
-        %7D%0A      %7D%0A
     %7D%0A%0A 
@@ -2452,252 +2452,8 @@
  %7D%0A%0A
-  private static boolean isToken(PsiElement element, IElementType tokenType) %7B%0A    if (!(element instanceof XmlToken)) %7B%0A      return false;%0A    %7D%0A    final XmlToken token = (XmlToken)element;%0A    return token.getTokenType() == tokenType;%0A  %7D%0A%0A
   pr

use XmlTagUtil.getEndTagNameElement()