/*
 * Copyright 2005 Sascha Weinreuter
 *
 * 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 org.intellij.lang.xpath.xslt.validation.inspections;

import com.intellij.codeInspection.LocalInspectionTool;
import com.intellij.codeInspection.SuppressIntentionAction;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiWhiteSpace;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.*;
import com.intellij.xml.util.XmlUtil;
import org.intellij.lang.xpath.xslt.util.XsltCodeInsightUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class InspectionUtil {
    static final Pattern SUPPRESSION_PATTERN = Pattern.compile("[ \t]*(?:noinspection|suppress)[ \t]+(\\w+(,[ \t]*\\w+)*)[ \t]*");

    @NonNls
    private static final String ALL_ID = "ALL";

    private InspectionUtil() {
    }

    public static boolean isSuppressed(LocalInspectionTool tool, PsiElement element) {
        final XmlTag tag = PsiTreeUtil.getContextOfType(element, XmlTag.class, true);
        if (isSuppressedAt(tag, tool)) {
            return true;
        }
        final XmlTag tmpl = XsltCodeInsightUtil.getTemplateTag(element, true);
        if (isSuppressedAt(tmpl, tool)) {
            return true;
        }
        final XmlDocument document = PsiTreeUtil.getContextOfType(element, XmlDocument.class, true);
        if (document != null) {
            final XmlTag sheet = document.getRootTag();
            if (isSuppressedAt(sheet, tool)) {
                return true;
            }
        }
        return false;
    }

    private static boolean isSuppressedAt(PsiElement anchor, LocalInspectionTool tool) {
        if (anchor == null) return false;

        PsiElement prevSibling;
        if (!(anchor instanceof XmlComment)) {
            prevSibling = anchor.getPrevSibling();
            while (prevSibling instanceof PsiWhiteSpace || prevSibling instanceof XmlText) {
                prevSibling = prevSibling.getPrevSibling();
            }
        } else {
            prevSibling = anchor;
        }

        if (prevSibling instanceof XmlProlog) {
            if (prevSibling.getTextLength() > 0 && !"\n".equals(prevSibling.getText())) {
                return isSuppressedAt(prevSibling.getLastChild(), tool);
            } else {
                return isSuppressedAt(prevSibling, tool);                
            }
        }
        if (prevSibling instanceof XmlComment) {
            final XmlComment comment = (XmlComment)prevSibling;
            final String text = XmlUtil.getCommentText(comment);
            if (text != null) {
                final Matcher matcher = SUPPRESSION_PATTERN.matcher(text);
                if (matcher.matches()) {
                    final String[] strings = matcher.group(1).split(",");
                    final String toolId = tool.getID();
                    for (String s : strings) {
                        if (s.trim().equals(toolId) || ALL_ID.equals(s.trim())) return true;
                    }
                }
            }
        }
        return false;
    }

    public static List<SuppressIntentionAction> getSuppressActions(LocalInspectionTool inspection, final boolean isXPath) {
        final List<SuppressIntentionAction> actions = new ArrayList<SuppressIntentionAction>(4);

        actions.add(new SuppressInspectionAction(inspection.getID(), "Suppress for Instruction") {
            @Override
            protected XmlTag getAnchor(@NotNull PsiElement element) {
                return PsiTreeUtil.getContextOfType(element, XmlTag.class, isXPath);
            }
        });

        actions.add(new SuppressInspectionAction(inspection.getID(), "Suppress for Template") {
            @Override
            protected XmlTag getAnchor(@NotNull PsiElement element) {
                return XsltCodeInsightUtil.getTemplateTag(element, isXPath);
            }
        });

        actions.add(new SuppressInspectionAction(inspection.getID(), "Suppress for Stylesheet") {
            @Override
            protected XmlTag getAnchor(@NotNull PsiElement element) {
                final XmlDocument document = PsiTreeUtil.getContextOfType(element, XmlDocument.class, isXPath);
                return document != null ? document.getRootTag() : null;
            }
        });

        actions.add(new SuppressInspectionAction(ALL_ID, "Suppress all for Stylesheet") {
            @Override
            protected XmlTag getAnchor(@NotNull PsiElement element) {
                final XmlDocument document = PsiTreeUtil.getContextOfType(element, XmlDocument.class, isXPath);
                return document != null ? document.getRootTag() : null;
            }
        });

        return actions;
    }
}
