/*
 * Copyright 2000-2014 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.daemon.impl.analysis;

import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction;
import com.intellij.openapi.application.Result;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.InputValidator;
import com.intellij.openapi.ui.Messages;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReference;
import com.intellij.psi.XmlRecursiveElementVisitor;
import com.intellij.psi.impl.source.resolve.reference.impl.providers.TypeOrElementOrAttributeReference;
import com.intellij.psi.impl.source.xml.SchemaPrefixReference;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlAttributeValue;
import com.intellij.psi.xml.XmlElement;
import com.intellij.psi.xml.XmlTag;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

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

/**
 * @author Konstantin Bulenkov
 */
public class AddSchemaPrefixIntention extends PsiElementBaseIntentionAction {
  public static final String NAME = "Insert Namespace Prefix";

  public AddSchemaPrefixIntention() {
    setText(NAME);
  }

  @NotNull
  @Override
  public String getFamilyName() {
    return NAME;
  }

  @Override
  public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement element) throws IncorrectOperationException {
    final XmlAttribute xmlns = getXmlnsDeclaration(element);
    if (xmlns == null) return;
    final String namespace = xmlns.getValue();
    final XmlTag tag = xmlns.getParent();

    if (tag != null) {
      final Set<String> ns = tag.getLocalNamespaceDeclarations().keySet();
      final String nsPrefix = Messages.showInputDialog(project, "Namespace Prefix:", NAME, Messages.getInformationIcon(), "",
                               new InputValidator() {
                                 @Override
                                 public boolean checkInput(String inputString) {
                                   return !ns.contains(inputString);
                                 }

                                 @Override
                                 public boolean canClose(String inputString) {
                                   return checkInput(inputString);
                                 }
                               });
      if (nsPrefix == null) return;
      final List<XmlTag> tags = new ArrayList<XmlTag>();
      final List<XmlAttributeValue> values = new ArrayList<XmlAttributeValue>();
      new WriteCommandAction(project, NAME, tag.getContainingFile()) {
        @Override
        protected void run(@NotNull Result result) throws Throwable {
          tag.accept(new XmlRecursiveElementVisitor() {
            @Override
            public void visitXmlTag(XmlTag tag) {
              if (tag.getNamespace().equals(namespace) && tag.getNamespacePrefix().isEmpty()) {
                tags.add(tag);
              }
              super.visitXmlTag(tag);
            }

            @Override
            public void visitXmlAttributeValue(XmlAttributeValue value) {
              PsiReference ref = null;
              boolean skip = false;
              for (PsiReference reference : value.getReferences()) {
                if (reference instanceof TypeOrElementOrAttributeReference) {
                  ref = reference;
                } else if (reference instanceof SchemaPrefixReference) {
                  skip = true;
                  break;
                }
              }
              if (!skip && ref != null) {
                final PsiElement xmlElement = ref.resolve();
                if (xmlElement instanceof XmlElement) {
                  final XmlTag tag = PsiTreeUtil.getParentOfType(xmlElement, XmlTag.class, false);
                  if (tag != null) {
                    if (tag.getNamespace().equals(namespace)) {
                      if (ref.getRangeInElement().getLength() == value.getValue().length()) { //no ns prefix
                        values.add(value);
                      }
                    }
                  }
                }
              }
            }
          });
          for (XmlAttributeValue value : values) {
            ((XmlAttribute)value.getParent()).setValue(nsPrefix + ":" + value.getValue());
          }
          for (XmlTag xmlTag : tags) {
            xmlTag.setName(nsPrefix + ":" + xmlTag.getLocalName());
          }
          xmlns.setName("xmlns:" + nsPrefix);
        }
      }.execute();
    }
  }

  @Override
  public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement element) {
    return getXmlnsDeclaration(element) != null;
  }

  @Nullable
  private static XmlAttribute getXmlnsDeclaration(PsiElement element) {
    final PsiElement parent = element.getParent();
    if (parent instanceof XmlTag) {
      XmlTag tag = (XmlTag)parent;
      if (tag.getNamespacePrefix().isEmpty()) {
        while (tag != null) {
          final XmlAttribute attr = tag.getAttribute("xmlns");
          if (attr != null) return attr;
          tag = tag.getParentTag();
        }
      }
    } else if (parent instanceof XmlAttribute && ((XmlAttribute)parent).getName().equals("xmlns")) {
      return (XmlAttribute)parent;
    }
    return null;
  }
}

@@ -1003,16 +1003,66 @@
 ssages;%0A
+import com.intellij.openapi.util.text.StringUtil;%0A
 import c
@@ -1928,17 +1928,17 @@
 %22Insert 
-N
+n
 amespace
@@ -1934,25 +1934,25 @@
 t namespace 
-P
+p
 refix%22;%0A%0A  p
@@ -2594,20 +2594,43 @@
 efix:%22, 
+StringUtil.capitalize(
 NAME
+)
 , Messag

sentence capitalization