/*
 * Copyright 2013 Grzegorz Ligas <ligasgr@gmail.com> and other contributors (see the CONTRIBUTORS file).
 *
 * 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 org.intellij.xquery.folding;

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.util.TextRange;
import com.intellij.psi.PsiElement;
import org.intellij.xquery.psi.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

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

public class XQueryFoldingBuilder extends FoldingBuilderEx {

    @NotNull
    @Override
    public FoldingDescriptor[] buildFoldRegions(@NotNull PsiElement root, @NotNull Document document, boolean quick) {
        if (!(root instanceof XQueryFile)) return FoldingDescriptor.EMPTY;
        XQueryFile file = (XQueryFile) root;
        List<FoldingDescriptor> descriptorList = new ArrayList<FoldingDescriptor>();


        updateImportFoldingDescriptors(descriptorList, new ArrayList<XQueryElement>(file.getModuleImports()));
        updateImportFoldingDescriptors(descriptorList, new ArrayList<XQueryElement>(file.getNamespaceDeclarations()));

        for (XQueryFunctionDecl function : file.getFunctionDeclarations()) {
            final XQueryFunctionBody functionBody = function.getFunctionBody();
            if (functionBody != null) {
                descriptorList.add(new FoldingDescriptor(function, functionBody.getTextRange()));
            }
        }

        return descriptorList.toArray(new FoldingDescriptor[descriptorList.size()]);
    }

    private void updateImportFoldingDescriptors(List<FoldingDescriptor> descriptorList, Collection<XQueryElement> elementsToFold) {
        if (elementsToFold.size() < 2) {
            return;
        }

        FoldingDescriptor folding = null;
        int numberOfAdjacentElement = 0;
        for (Iterator<XQueryElement> i = elementsToFold.iterator(); i.hasNext(); ) {
            final XQueryElement element = i.next();
            final TextRange elementTextRange = element.getTextRange();
            if (folding == null) {
                 folding = buildNewFoldingDescriptor(element, elementTextRange);
                 numberOfAdjacentElement = 1;
                 continue;
             }

            final boolean isAdjacent = folding.getRange().getEndOffset() + 1 == elementTextRange.getStartOffset();
            if (isAdjacent) {
                final TextRange updatedRange = new TextRange(folding.getRange().getStartOffset(), elementTextRange.getEndOffset());
                folding = new FoldingDescriptor(folding.getElement(), updatedRange);
                numberOfAdjacentElement++;
            }

            if (isAdjacent && i.hasNext()) {
                continue;
            }

            if (numberOfAdjacentElement > 1) {
                descriptorList.add(folding);
                folding = null;
            }

            if (!isAdjacent) {
                folding = buildNewFoldingDescriptor(element, elementTextRange);
                numberOfAdjacentElement = 1;
            }
        }
    }

    private FoldingDescriptor buildNewFoldingDescriptor(XQueryElement element, TextRange elementTextRange) {
        FoldingDescriptor folding;
        folding = new FoldingDescriptor(element, elementTextRange);

        if (element instanceof XQueryModuleImport) {
            final TextRange updatedRange = new TextRange(folding.getRange().getStartOffset() + "import ".length(), folding.getRange().getEndOffset());
            folding = new FoldingDescriptor(folding.getElement(), updatedRange);
        }
        if (element instanceof XQueryNamespaceDecl) {
            final TextRange updatedRange = new TextRange(folding.getRange().getStartOffset() + "declare namespace ".length(), folding.getRange().getEndOffset());
            folding = new FoldingDescriptor(folding.getElement(), updatedRange);
        }
        return folding;
    }

    @Nullable
    @Override
    public String getPlaceholderText(@NotNull ASTNode astNode) {
        final PsiElement psiElement = astNode.getPsi();
        if (psiElement instanceof XQueryFunctionDecl) {
            return "{...}";
        } else if (psiElement instanceof XQueryModuleImport) {
            return "...";
        } else if (psiElement instanceof XQueryNamespaceDecl) {
            return "...";
        } else {
            return "....";
        }
    }

    @Override
    public boolean isCollapsedByDefault(@NotNull ASTNode astNode) {
        if (astNode.getElementType().equals(XQueryTypes.MODULE_IMPORT)) {
            return true;
        } else if (astNode.getElementType().equals(XQueryTypes.NAMESPACE_DECL)) {
            return true;
        }
        return false;
    }
}

@@ -2023,24 +2023,60 @@
 Body != null
+ && functionBody.getTextLength() %3E 2
 ) %7B%0A        

Fix edge case for folding.