package de.espend.idea.php.annotation.reference;

import com.intellij.openapi.util.TextRange;
import com.intellij.patterns.PlatformPatterns;
import com.intellij.psi.*;
import com.intellij.util.ProcessingContext;
import com.jetbrains.php.lang.documentation.phpdoc.psi.tags.PhpDocTag;
import com.jetbrains.php.lang.psi.elements.PhpClass;
import com.jetbrains.php.lang.psi.elements.PhpUse;
import de.espend.idea.php.annotation.util.AnnotationUtil;
import de.espend.idea.php.annotation.util.PluginUtil;
import org.jetbrains.annotations.NotNull;

public class DocTagNameAnnotationReferenceContributor extends PsiReferenceContributor {

    @Override
    public void registerReferenceProviders(PsiReferenceRegistrar psiReferenceRegistrar) {

        // "@Template()", "@ORM\PostPersist()"
        psiReferenceRegistrar.registerReferenceProvider(
            PlatformPatterns.psiElement(PhpDocTag.class),
            new PsiReferenceProvider() {
                @NotNull
                @Override
                public PsiReference[] getReferencesByElement(@NotNull PsiElement element, @NotNull ProcessingContext context) {

                    if(!PluginUtil.isEnabled(element)) {
                        return new PsiReference[0];
                    }

                    if(!(element instanceof PhpDocTag)) {
                        return new PsiReference[0];
                    }

                    return new PsiReference[] {
                        new PhpDocTagReference((PhpDocTag) element)
                    };
                }
            }

        );

    }

    public class PhpDocTagReference extends PsiPolyVariantReferenceBase<PhpDocTag> {

        public PhpDocTagReference(PhpDocTag psiElement) {
            super(psiElement);
        }


        @Override
        public boolean isReferenceTo(PsiElement element) {

            // eg for "Optimize Imports"

            // we only get calls on "use" alias
            // use Doctrine\ORM\Mapping as "ORM";
            if (element instanceof PhpUse) {
                String useName = ((PhpUse) element).getName();
                String docUseName = getDocBlockName();

                if(useName.equals(docUseName)) {
                    return true;
                }

                /* ResolveResult[] results = multiResolve(false);
                for (ResolveResult result : results) {
                    if(result.isValidResult() && result.getElement() == element) {
                        return true;
                    }
                }*/
            }

            // attach reference to @Template(), but its stripped anyways
            if (element instanceof PhpClass) {
                if(((PhpClass) element).getName().equals(getDocBlockName())) {
                    return true;
                }
            }

            return false;
        }

        public TextRange getRangeInElement() {
            String tagName = getElement().getName();
            int rangeStart = 0;
            int rangeEnd = tagName.length();

            // strip "@" should be always the case; just for secure
            if(tagName.startsWith("@")) {
                rangeStart = 1;
                tagName = tagName.substring(1);
            }

            // "@ORM\PostPersist()"
            // only on alias and namespace use main ns
            if(tagName.contains("\\")) {
                rangeEnd = tagName.indexOf("\\") + rangeStart;
            }

            return new TextRange(rangeStart, rangeEnd);
        }

        @NotNull
        @Override
        public ResolveResult[] multiResolve(boolean b) {

            PhpClass phpClass = AnnotationUtil.getAnnotationReference(getElement());
            if(phpClass == null) {
                return new ResolveResult[0];
            }

            return new ResolveResult[] { new PsiElementResolveResult(phpClass) };
        }

        @NotNull
        @Override
        public Object[] getVariants() {
            return new Object[0];
        }

        /**
         * Get the class alias
         *
         * "@Template()"
         * "@ORM\PostPersist()"
         */
        private String getDocBlockName() {
            String docBlockName = getElement().getName();

            if(docBlockName.startsWith("@")) {
                docBlockName = docBlockName.substring(1);
            }

            if(docBlockName.contains("\\")) {
                docBlockName = docBlockName.substring(0, docBlockName.indexOf("\\"));
            }

            return docBlockName;
        }
    }

}

@@ -738,17 +738,181 @@
        /
-/
+**%0A         * Our main reference provider to attach DocBlocTag to their use declaration%0A         * This one resolve the %22Optimize Usage%22 issues%0A         *%0A         *
  %22@Templ
@@ -932,32 +932,44 @@
 %5CPostPersist()%22%0A
+         */%0A
         psiRefer
@@ -1749,19 +1749,27 @@
 %7D%0A%0A    p
-ubl
+rivate stat
 ic class
@@ -1938,17 +1938,16 @@
      %7D%0A%0A
-%0A
         
@@ -2020,98 +2020,8 @@
  %7B%0A%0A
-            // eg for %22Optimize Imports%22%0A%0A            // we only get calls on %22use%22 alias%0A
     
@@ -2347,293 +2347,51 @@
     
-    /* ResolveResult%5B%5D results = multiResolve(false);%0A                for (ResolveResult result : results) %7B%0A                    if(result.isValidResult() && result.getElement() == element) %7B%0A                        return true;%0A                    %7D%0A                %7D*/%0A            %7D%0A
+%7D%0A%0A            // eg for %22Optimize Imports%22
 %0A   
@@ -2437,34 +2437,8 @@
 te()
-, but its stripped anyways
 %0A   
@@ -2655,32 +2655,187 @@
 lse;%0A        %7D%0A%0A
+        /**%0A         * We need to strip @ char before DocTag @Test, @Test%5CFoo%0A         *%0A         * @return TextRange of DocTag without @ char%0A         */%0A
         public T
@@ -3015,25 +3015,56 @@
  // 
-strip %22@%22
+remove DocTag %22@%22 char%0A            // it
  should 
 be a
@@ -3063,43 +3063,49 @@
 uld 
-be 
 always 
-the case; just for secure
+be true, check for security reason
 %0A   

remove redundant code and comments to our main references provider