package fr.adrienbrault.idea.symfony2plugin.dic.container.util;

import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.impl.source.tree.LeafPsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.XmlDocument;
import com.intellij.psi.xml.XmlFile;
import com.intellij.psi.xml.XmlTag;
import com.intellij.util.Consumer;
import fr.adrienbrault.idea.symfony2plugin.dic.attribute.value.AttributeValueInterface;
import fr.adrienbrault.idea.symfony2plugin.dic.attribute.value.XmlTagAttributeValue;
import fr.adrienbrault.idea.symfony2plugin.dic.attribute.value.YamlKeyValueAttributeValue;
import fr.adrienbrault.idea.symfony2plugin.dic.container.ServiceInterface;
import fr.adrienbrault.idea.symfony2plugin.dic.container.SerializableService;
import fr.adrienbrault.idea.symfony2plugin.dic.container.visitor.ServiceConsumer;
import fr.adrienbrault.idea.symfony2plugin.util.yaml.YamlHelper;
import org.apache.commons.lang.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.yaml.psi.YAMLDocument;
import org.jetbrains.yaml.psi.YAMLFile;
import org.jetbrains.yaml.psi.YAMLKeyValue;

import java.util.ArrayList;
import java.util.Collection;

/**
 * @author Daniel Espendiller <daniel@espendiller.net>
 */
public class ServiceContainerUtil {

    @NotNull
    public static Collection<ServiceInterface> getServicesInFile(@NotNull PsiFile psiFile) {
        final Collection<ServiceInterface> services = new ArrayList<ServiceInterface>();

        if(psiFile instanceof XmlFile) {
            visitFile((XmlFile) psiFile, new Consumer<ServiceConsumer>() {
                @Override
                public void consume(ServiceConsumer serviceConsumer) {
                    SerializableService serializableService = createService(serviceConsumer);
                    serializableService.setDecorationInnerName(serviceConsumer.attributes().getString("decoration-inner-name"));
                    serializableService.setIsDeprecated(serviceConsumer.attributes().getBoolean("deprecated"));

                    services.add(serializableService);
                }
            });
        }

        if(psiFile instanceof YAMLFile) {
            visitFile((YAMLFile) psiFile, new Consumer<ServiceConsumer>() {
                @Override
                public void consume(ServiceConsumer serviceConsumer) {

                    // alias inline "foo: @bar"
                    PsiElement yamlKeyValue = serviceConsumer.attributes().getPsiElement();
                    if(yamlKeyValue instanceof YAMLKeyValue) {
                        PsiElement value = ((YAMLKeyValue) yamlKeyValue).getValue();
                        if(value instanceof LeafPsiElement) {
                            String valueText = ((YAMLKeyValue) yamlKeyValue).getValueText();
                            if(StringUtils.isNotBlank(valueText) && valueText.startsWith("@")) {
                                services.add(new SerializableService(serviceConsumer.getServiceId()).setAlias(valueText.substring(1)));
                                return;
                            }
                        }
                    }

                    SerializableService serializableService = createService(serviceConsumer);
                    serializableService.setDecorationInnerName(serviceConsumer.attributes().getString("decoration_inner_name"));

                    // catch: deprecated: ~
                    String string = serviceConsumer.attributes().getString("deprecated");
                    if("~".equals(string)) {
                        serializableService.setIsDeprecated(true);
                    } else {
                        serializableService.setIsDeprecated(serviceConsumer.attributes().getBoolean("deprecated"));
                    }

                    services.add(serializableService);
                }
            });
        }

        return services;
    }

    @NotNull
    private static SerializableService createService(@NotNull ServiceConsumer serviceConsumer) {
        AttributeValueInterface attributes = serviceConsumer.attributes();

        return new SerializableService(serviceConsumer.getServiceId())
            .setAlias(attributes.getString("alias"))
            .setClassName(attributes.getString("class"))
            .setDecorates(attributes.getString("decorates"))
            .setParent(attributes.getString("parent"))
            .setIsAbstract(attributes.getBoolean("abstract"))
            .setIsAutowire(attributes.getBoolean("autowrite"))
            .setIsLazy(attributes.getBoolean("lazy"))
            .setIsPublic(attributes.getBoolean("public"));
    }

    public static void visitFile(@NotNull YAMLFile psiFile, @NotNull Consumer<ServiceConsumer> consumer) {
        for (YAMLKeyValue keyValue : YamlHelper.getQualifiedKeyValuesInFile(psiFile, "services")) {
            String serviceId = keyValue.getKeyText();
            if(StringUtils.isBlank(serviceId)) {
                continue;
            }

            consumer.consume(new ServiceConsumer(keyValue, serviceId, new YamlKeyValueAttributeValue(keyValue)));
        }
    }

    public static void visitFile(@NotNull XmlFile psiFile, @NotNull Consumer<ServiceConsumer> consumer) {
        if(!(psiFile.getFirstChild() instanceof XmlDocument)) {
            return;
        }

        XmlTag xmlTags[] = PsiTreeUtil.getChildrenOfType(psiFile.getFirstChild(), XmlTag.class);
        if(xmlTags == null) {
            return;
        }

        for(XmlTag xmlTag: xmlTags) {
            if(xmlTag.getName().equals("container")) {
                for(XmlTag servicesTag: xmlTag.getSubTags()) {
                    if(servicesTag.getName().equals("services")) {
                        for(XmlTag serviceTag: servicesTag.getSubTags()) {
                            String serviceId = serviceTag.getAttributeValue("id");
                            if(StringUtils.isBlank(serviceId)) {
                                continue;
                            }

                            consumer.consume(new ServiceConsumer(serviceTag, serviceId, new XmlTagAttributeValue(serviceTag)));
                       }
                    }
                }
            }
        }
    }
}

@@ -131,65 +131,8 @@
 le;%0A
-import com.intellij.psi.impl.source.tree.LeafPsiElement;%0A
 impo
@@ -586,83 +586,8 @@
 ue;%0A
-import fr.adrienbrault.idea.symfony2plugin.dic.container.ServiceInterface;%0A
 impo
@@ -656,24 +656,99 @@
 bleService;%0A
+import fr.adrienbrault.idea.symfony2plugin.dic.container.ServiceInterface;%0A
 import fr.ad
@@ -972,52 +972,8 @@
 ll;%0A
-import org.jetbrains.yaml.psi.YAMLDocument;%0A
 impo
@@ -1051,16 +1051,58 @@
 eyValue;
+%0Aimport org.jetbrains.yaml.psi.YAMLScalar;
 %0A%0Aimport
@@ -2650,22 +2650,18 @@
 eof 
-LeafPsiElement
+YAMLScalar
 ) %7B%0A
@@ -2713,34 +2713,25 @@
 = ((YAML
-KeyValue) yamlKeyV
+Scalar) v
 alue).ge
@@ -2735,17 +2735,17 @@
 .get
+Text
 Value
-Text
 ();%0A

migrate yaml alias indexing to new yaml scalar element