package fr.adrienbrault.idea.symfony2plugin.navigation;

import com.intellij.lang.ASTNode;
import com.intellij.lang.folding.FoldingBuilderEx;
import com.intellij.lang.folding.FoldingDescriptor;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.jetbrains.php.lang.psi.PhpFile;
import com.jetbrains.php.lang.psi.elements.StringLiteralExpression;
import fr.adrienbrault.idea.symfony2plugin.Settings;
import fr.adrienbrault.idea.symfony2plugin.Symfony2ProjectComponent;
import fr.adrienbrault.idea.symfony2plugin.config.SymfonyPhpReferenceContributor;
import fr.adrienbrault.idea.symfony2plugin.routing.PhpRouteReferenceContributor;
import fr.adrienbrault.idea.symfony2plugin.routing.Route;
import fr.adrienbrault.idea.symfony2plugin.routing.RouteHelper;
import fr.adrienbrault.idea.symfony2plugin.templating.util.TwigUtil;
import fr.adrienbrault.idea.symfony2plugin.util.MethodMatcher;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

/**
 * @author Daniel Espendiller <daniel@espendiller.net>
 */
public class PhpFoldingBuilder extends FoldingBuilderEx {

    @NotNull
    @Override
    public FoldingDescriptor[] buildFoldRegions(@NotNull PsiElement psiElement, @NotNull Document document, boolean b) {
        if (!(psiElement instanceof PhpFile)){
            return new FoldingDescriptor[0];
        }

        Project project = psiElement.getProject();
        if (!Symfony2ProjectComponent.isEnabled(project)){
            return new FoldingDescriptor[0];
        }

        boolean codeFoldingPhpRoute = Settings.getInstance(project).codeFoldingPhpRoute;
        boolean codeFoldingPhpModel = Settings.getInstance(project).codeFoldingPhpModel;
        boolean codeFoldingPhpTemplate = Settings.getInstance(project).codeFoldingPhpTemplate;

        // we dont need to do anything
        if(!codeFoldingPhpRoute && !codeFoldingPhpModel && !codeFoldingPhpTemplate) {
            return new FoldingDescriptor[0];
        }

        List<FoldingDescriptor> descriptors = new ArrayList<>();

        Collection<StringLiteralExpression> stringLiteralExpressiones = PsiTreeUtil.findChildrenOfType(psiElement, StringLiteralExpression.class);
        for(StringLiteralExpression stringLiteralExpression: stringLiteralExpressiones) {

            if(codeFoldingPhpModel) {
                attachModelShortcuts(descriptors, stringLiteralExpression);
            }

            if(codeFoldingPhpTemplate) {
                attachTemplateShortcuts(descriptors, stringLiteralExpression);
            }

        }

        // strip ".[php|html].twig"
        if(codeFoldingPhpRoute) {
            attachRouteShortcuts(project, descriptors, stringLiteralExpressiones);
        }

        return descriptors.toArray(new FoldingDescriptor[0]);
    }


    private void attachRouteShortcuts(@NotNull Project project, List<FoldingDescriptor> descriptors, Collection<StringLiteralExpression> stringLiteralExpressions) {

        Map<String,Route> routes = null;

        for(StringLiteralExpression stringLiteralExpression: stringLiteralExpressions) {

            if (MethodMatcher.getMatchedSignatureWithDepth(stringLiteralExpression, PhpRouteReferenceContributor.GENERATOR_SIGNATURES) != null) {

                // cache routes if we need them
                if(routes == null) {
                    routes = RouteHelper.getAllRoutes(project);
                }

                String contents = stringLiteralExpression.getContents();
                if(contents.length() > 0 && routes.containsKey(contents)) {
                    final Route route = routes.get(contents);

                    final String url = RouteHelper.getRouteUrl(route);
                    if(url != null) {
                        descriptors.add(new FoldingDescriptor(stringLiteralExpression.getNode(),
                            new TextRange(stringLiteralExpression.getTextRange().getStartOffset() + 1, stringLiteralExpression.getTextRange().getEndOffset() - 1)) {
                            @Override
                            public @NotNull String getPlaceholderText() {
                                return url;
                            }
                        });
                    }
                }
            }

        }

    }

    private void attachModelShortcuts(List<FoldingDescriptor> descriptors, final StringLiteralExpression stringLiteralExpression) {

        if (MethodMatcher.getMatchedSignatureWithDepth(stringLiteralExpression, SymfonyPhpReferenceContributor.REPOSITORY_SIGNATURES) == null) {
            return;
        }

        String content = stringLiteralExpression.getContents();

        for(String lastChar: new String[] {":", "\\"}) {
            if(content.contains(lastChar)) {
                final String replace = content.substring(content.lastIndexOf(lastChar) + 1);
                if(replace.length() > 0) {
                    descriptors.add(new FoldingDescriptor(stringLiteralExpression.getNode(),
                        new TextRange(stringLiteralExpression.getTextRange().getStartOffset() + 1, stringLiteralExpression.getTextRange().getEndOffset() - 1)) {
                        @Nullable
                        @Override
                        public String getPlaceholderText() {
                            return replace;
                        }
                    });
                }

                return;
            }
        }

    }

    private void attachTemplateShortcuts(List<FoldingDescriptor> descriptors, final StringLiteralExpression stringLiteralExpression) {

        if (MethodMatcher.getMatchedSignatureWithDepth(stringLiteralExpression, SymfonyPhpReferenceContributor.TEMPLATE_SIGNATURES) == null) {
            return;
        }

        String content = stringLiteralExpression.getContents();

        String templateShortcutName = TwigUtil.getFoldingTemplateName(content);
        if(templateShortcutName == null) {
            return;
        }

        final String finalTemplateShortcutName = templateShortcutName;
        descriptors.add(new FoldingDescriptor(stringLiteralExpression.getNode(),
            new TextRange(stringLiteralExpression.getTextRange().getStartOffset() + 1, stringLiteralExpression.getTextRange().getEndOffset() - 1)) {
            @Nullable
            @Override
            public String getPlaceholderText() {
                return finalTemplateShortcutName;
            }
        });

    }

    @Nullable
    @Override
    public String getPlaceholderText(@NotNull ASTNode astNode) {
        return "...";
    }

    @Override
    public boolean isCollapsedByDefault(@NotNull ASTNode astNode) {
        return true;
    }
}
