package com.cedricziel.idea.fluid.viewHelpers;

import com.cedricziel.idea.fluid.extensionPoints.ViewHelperProvider;
import com.cedricziel.idea.fluid.viewHelpers.model.ViewHelper;
import com.cedricziel.idea.fluid.viewHelpers.model.ViewHelperArgument;
import com.intellij.lang.xml.XMLLanguage;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiFileFactory;
import com.intellij.psi.XmlRecursiveElementVisitor;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlFile;
import com.intellij.psi.xml.XmlTag;
import com.intellij.psi.xml.XmlText;
import gnu.trove.THashMap;
import org.apache.commons.io.IOUtils;
import org.jetbrains.annotations.NotNull;

import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.Map;

public class DefaultViewHelpersProvider implements ViewHelperProvider {
    private static final Map<String, Map<String, ViewHelper>> myCache = new THashMap<>();

    @NotNull
    @Override
    public Map<String, ViewHelper> provideForNamespace(@NotNull Project project, @NotNull String namespace) {
        if (!namespace.equals("TYPO3/Fluid/ViewHelpers") && !namespace.isEmpty()) {

            return new THashMap<>();
        }

        String schemaLocation = "/schemas/fluid/7.6.xsd";
        return getStringViewHelperMap(project, schemaLocation);
    }

    private synchronized Map<String, ViewHelper> getStringViewHelperMap(@NotNull Project project, String schemaLocation) {
        if (myCache.containsKey(schemaLocation)) {

            return myCache.get(schemaLocation);
        }

        String schema = readSchema(schemaLocation);

        XmlFile xmlLanguage = (XmlFile) PsiFileFactory.getInstance(project).createFileFromText(XMLLanguage.INSTANCE, schema);

        ViewHelperSchemaRecursiveElementVisitor visitor = new ViewHelperSchemaRecursiveElementVisitor();
        visitor.visitXmlFile(xmlLanguage);

        myCache.put(schemaLocation, visitor.viewHelpers);

        return myCache.get(schemaLocation);
    }

    private String readSchema(String schemaLocation) {
        InputStream resourceAsStream = DefaultViewHelpersProvider.class.getResourceAsStream(schemaLocation);
        StringWriter writer = new StringWriter();
        try {
            IOUtils.copy(resourceAsStream, writer);
        } catch (IOException e) {
            e.printStackTrace();
        }

        return writer.toString();
    }

    private class ViewHelperSchemaRecursiveElementVisitor extends XmlRecursiveElementVisitor {
        Map<String, ViewHelper> viewHelpers = new THashMap<>();

        @Override
        public void visitXmlTag(XmlTag tag) {
            if (!tag.getName().equals("xsd:element")) {
                super.visitXmlTag(tag);

                return;
            }

            XmlAttribute nameAttribute = tag.getAttribute("name");
            if (nameAttribute == null || nameAttribute.getValue() == null) {
                super.visitXmlTag(tag);

                return;
            }

            ViewHelper viewHelper = new ViewHelper(nameAttribute.getValue());
            viewHelper.setDocumentation(extractDocumentation(tag));

            XmlTag complexType = tag.findFirstSubTag("xsd:complexType");
            if (complexType != null) {
                XmlTag[] attributeTags = complexType.findSubTags("xsd:attribute");
                for (XmlTag attributeTag : attributeTags) {
                    String argumentName = attributeTag.getAttributeValue("name");
                    if (argumentName == null) {
                        continue;
                    }

                    ViewHelperArgument argument = new ViewHelperArgument(argumentName);

                    argument.setDocumentation(extractDocumentation(attributeTag));

                    String attributeType = attributeTag.getAttributeValue("php:type");
                    if (attributeType == null) {
                        argument.setType("mixed");
                    } else {
                        argument.setType(attributeType);
                    }

                    String requiredAttribute = attributeTag.getAttributeValue("use");
                    if (requiredAttribute != null && requiredAttribute.equals("required")) {
                        argument.setRequired(true);
                    }

                    viewHelper.addArgument(argumentName, argument);
                }
            }

            viewHelpers.put(nameAttribute.getValue(), viewHelper);

            super.visitXmlTag(tag);
        }

        private @NotNull
        String extractDocumentation(XmlTag attributeTag) {
            StringBuilder attributeDocumentation = new StringBuilder();

            XmlTag attributeAnnotation = attributeTag.findFirstSubTag("xsd:annotation");
            if (attributeAnnotation != null) {
                XmlTag attributeDoc = attributeAnnotation.findFirstSubTag("xsd:documentation");
                if (attributeDoc != null) {
                    for (XmlText textElement : attributeDoc.getValue().getTextElements()) {
                        attributeDocumentation.append(textElement.getValue());
                    }
                }
            }

            return attributeDocumentation.toString();
        }
    }
}

@@ -606,46 +606,8 @@
 ap;%0A
-import org.apache.commons.io.IOUtils;%0A
 impo
@@ -664,76 +664,9 @@
 .io.
-IOException;%0Aimport java.io.InputStream;%0Aimport java.io.StringWriter
+*
 ;%0Aim
@@ -2103,82 +2103,76 @@
 ring
-Writer writer = new StringWriter();%0A        try %7B%0A            IOUtils.copy
+ schema = %22%22;%0A        try %7B%0A            schema = readFromInputStream
 (res
@@ -2188,16 +2188,8 @@
 ream
-, writer
 );%0A 
@@ -2284,25 +2284,14 @@
 urn 
-writer.toString()
+schema
 ;%0A  
@@ -5135,11 +5135,717 @@
  %7D%0A    %7D
+%0A%0A    private String readFromInputStream(InputStream inputStream)%0A        throws IOException %7B%0A        StringBuilder resultStringBuilder = new StringBuilder();%0A        try (BufferedReader br = new BufferedReader(new InputStreamReader(inputStream))) %7B%0A            String line;%0A            while ((line = br.readLine()) != null) %7B%0A                resultStringBuilder.append(line).append(%22%5Cn%22);%0A            %7D%0A        %7D finally %7B%0A            if (inputStream != null) %7B%0A                try %7B%0A                    inputStream.close();%0A                %7D catch (IOException e) %7B%0A                    e.printStackTrace();%0A                %7D%0A            %7D%0A        %7D%0A%0A        return resultStringBuilder.toString();%0A    %7D
 %0A%7D%0A

Read files with standard methods