/*
 * Copyright 2013-2016 Grzegorz Ligas <[email protected]> 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.psi;

import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.search.FilenameIndex;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.util.containers.ContainerUtil;
import org.intellij.xquery.XQueryFileType;

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

import static java.util.Arrays.asList;

/**
 * User: ligasgr
 * Date: 08/06/13
 * Time: 21:40
 */
public class XQueryUtil {

    public static List<XQueryFile> findXQueryFileByName(Project project, final String filename,
                                                        PsiFile containingFile) {
        final String name = unifyNameFormatAndRemoveProtocol(filename);
        if (isNotAbsolutePath(name)) {
            PsiFile fileFoundRelatively = getFileByRelativePath(project, filename, containingFile);
            if (fileFoundRelatively != null) {
                List<XQueryFile> filteredList = getOnlyXQueryFiles(asList(fileFoundRelatively));
                if (filteredList.size() > 0) {
                    return filteredList;
                }
            }
        }
        List<String> splitFilename = StringUtil.split(name, "/");
        String lastPartOfTheFilename = ContainerUtil.iterateAndGetLastItem(splitFilename);
        PsiFile[] filesByName = getFilesByName(project, lastPartOfTheFilename);
        List<PsiFile> filesThatEndWithFullName = getOnlyFilesThatEndWithFullName(name, filesByName);

        return getOnlyXQueryFiles(filesThatEndWithFullName);
    }

    private static boolean isNotAbsolutePath(String name) {
        return !name.startsWith("/");
    }

    private static PsiFile getFileByRelativePath(Project project, String filename, PsiFile containingFile) {
        if (!containingFile.isPhysical()) return null;
        VirtualFile containingDirectory = containingFile.getParent().getVirtualFile();
        VirtualFile foundByRelativePath = containingDirectory.findFileByRelativePath(filename);
        if (foundByRelativePath != null) {
            PsiFile found = PsiManager.getInstance(project).findFile(foundByRelativePath);
            if (found != null) {
                return found;
            }
        }
        return null;
    }

    private static List<XQueryFile> getOnlyXQueryFiles(List<PsiFile> filesThatEndWithFullName) {
        List<XQueryFile> xqueryFiles = new ArrayList<XQueryFile>();
        for (PsiFile file : filesThatEndWithFullName) {
            if (file instanceof XQueryFile) {
                xqueryFiles.add((XQueryFile) file);
            }
        }
        return xqueryFiles;
    }

    private static List<PsiFile> getOnlyFilesThatEndWithFullName(final String name, PsiFile[] filesByName) {
        return ContainerUtil.filter(filesByName, new Condition<PsiFile>() {
            @Override
            public boolean value(PsiFile psiFile) {
                VirtualFile virtualFile = psiFile.getVirtualFile();
                if (virtualFile == null) return false;
                String canonicalPath = virtualFile.getCanonicalPath();
                if (canonicalPath == null) return false;
                return canonicalPath.endsWith(name);
            }
        });
    }

    private static PsiFile[] getFilesByName(Project project, String lastPartOfTheFilename) {
        return FilenameIndex.getFilesByName(project, lastPartOfTheFilename,
                GlobalSearchScope.getScopeRestrictedByFileTypes(GlobalSearchScope.allScope(project), XQueryFileType
                        .INSTANCE));
    }

    private static String unifyNameFormatAndRemoveProtocol(String filename) {
        return filename.replaceFirst("(.*):", "").replaceAll("\\\\", "/");
    }

    public static Collection<XQueryFile> getReferencesToExistingFilesInImport(XQueryModuleImport moduleImport) {
        Collection<XQueryFile> results = new LinkedList<XQueryFile>();
        XQueryModuleImportNamespace moduleImportNamespace = moduleImport.getModuleImportNamespace();
        if (moduleImportNamespace != null) {
            addFileIfReferencedAndExists(results, moduleImportNamespace.getModuleImportPath());
        }
        for (XQueryModuleImportPath path : moduleImport.getModuleImportPathList()) {
            addFileIfReferencedAndExists(results, path);
        }
        return results;
    }

    private static void addFileIfReferencedAndExists(Collection<XQueryFile> results, XQueryModuleImportPath path) {
        if (path.getReference() != null) {
            XQueryFile xQueryFile = (XQueryFile) path.getReference().resolve();
            if (xQueryFile != null && xQueryFile.getModuleNamespaceName() != null) {
                results.add(xQueryFile);
            }
        }
    }
}