package fr.adrienbrault.idea.symfony2plugin.asset;

import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileVisitor;
import com.intellij.psi.PsiDirectory;
import fr.adrienbrault.idea.symfony2plugin.Settings;
import fr.adrienbrault.idea.symfony2plugin.util.ProjectUtil;
import fr.adrienbrault.idea.symfony2plugin.util.SymfonyBundleUtil;
import fr.adrienbrault.idea.symfony2plugin.util.dict.SymfonyBundle;
import org.apache.commons.lang.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author Daniel Espendiller <[email protected]>
 */
public class AssetDirectoryReader {

    public static AssetDirectoryReader INSTANCE = new  AssetDirectoryReader();

    final private boolean includeBundleDir;

    @NotNull
    final private Collection<String> filterExtension = new HashSet<>();

    public AssetDirectoryReader() {
        includeBundleDir = false;
    }

    public AssetDirectoryReader(@NotNull String[] filterExtension, boolean includeBundleDir) {
        this.includeBundleDir = includeBundleDir;
        this.filterExtension.addAll(Arrays.asList(filterExtension));
    }

    @Nullable
    private static VirtualFile getProjectAssetRoot(@NotNull Project project) {
        String webDirectoryName = Settings.getInstance(project).directoryToWeb;
        return VfsUtil.findRelativeFile(ProjectUtil.getProjectDir(project), webDirectoryName.split("/"));
    }

    @NotNull
    public Collection<AssetFile> getAssetFiles(@NotNull Project project) {
        Collection<AssetFile> files = new ArrayList<>();

        VirtualFile webDirectory = getProjectAssetRoot(project);
        if (null == webDirectory) {
            return files;
        }

        VfsUtil.visitChildrenRecursively(webDirectory, new VirtualFileVisitor() {
            @Override
            public boolean visitFile(@NotNull VirtualFile virtualFile) {
                if(isValidFile(virtualFile)) {
                    files.add(new AssetFile(virtualFile, AssetEnum.Position.Web, webDirectory));
                }
                return super.visitFile(virtualFile);
            }
        });

        if(!this.includeBundleDir) {
            return files;
        }

        SymfonyBundleUtil symfonyBundleUtil = new SymfonyBundleUtil(project);
        for(SymfonyBundle bundle : symfonyBundleUtil.getBundles()) {
            PsiDirectory bundleDirectory = bundle.getDirectory();
            if(null == bundleDirectory) {
                continue;
            }

            VirtualFile bundleDirectoryVirtual = bundleDirectory.getVirtualFile();
            VirtualFile resourceDirectory = VfsUtil.findRelativeFile(bundleDirectoryVirtual, "Resources");

            if (null != resourceDirectory) {
                VfsUtil.visitChildrenRecursively(resourceDirectory, new VirtualFileVisitor() {
                    @Override
                    public boolean visitFile(@NotNull VirtualFile virtualFile) {
                        if(isValidFile(virtualFile)) {
                            files.add(new AssetFile(virtualFile, AssetEnum.Position.Bundle, bundleDirectoryVirtual, '@' + bundle.getName() + "/"));
                        }
                        return super.visitFile(virtualFile);
                    }
                });
            }
        }

        return files;
    }

    /**
     * '@SampleBundle/Resources/public/js/*'
     * 'assets/js/*'
     * 'assets/js/*.js'
     */
    @NotNull
    public Collection<VirtualFile> resolveAssetFile(@NotNull Project project, @NotNull String filename) {
        String assetName = StringUtils.stripStart(filename.replace("\\", "/").replaceAll("/+", "/"), "/");

        // '@SampleBundle/Resources/public/js/foo,js'
        // TODO: '@SampleBundle/Resources/public/js/*'
        // TODO: '@SampleBundle/Resources/public/js/*.js'
        if(filename.startsWith("@")) {
            Collection<VirtualFile> files = new ArrayList<>();

            int i = filename.indexOf("/");
            if(i > 0) {
                String relativeFilename = filename.substring(1, i);
                for (SymfonyBundle bundle : new SymfonyBundleUtil(project).getBundle(relativeFilename)) {
                    String assetPath = filename.substring(i + 1);

                    Matcher matcher = Pattern.compile("^(.*[/\\\\])\\*([.\\w+]*)$").matcher(assetPath);
                    if (!matcher.find()) {
                        VirtualFile relative = bundle.getRelative(assetPath);
                        if(relative != null) {
                            files.add(relative);
                        }
                    } else {
                        // "/*"
                        // "/*.js"
                        PsiDirectory directory = bundle.getDirectory();
                        if(directory != null) {
                            files.addAll(collectWildcardDirectories(matcher, directory.getVirtualFile()));
                        }
                    }
                }
            }

            return files;
        }

        Collection<VirtualFile> files = new ArrayList<>();

        VirtualFile webDirectory = getProjectAssetRoot(project);
        if (null == webDirectory) {
            return files;
        }

        Matcher matcher = Pattern.compile("^(.*[/\\\\])\\*([.\\w+]*)$").matcher(assetName);
        if (!matcher.find()) {
            VirtualFile assetFile = VfsUtil.findRelativeFile(webDirectory, assetName.split("/"));
            if(assetFile != null) {
                files.add(assetFile);
            }
        } else {
            // "/*"
            // "/*.js"
            files.addAll(collectWildcardDirectories(matcher, webDirectory));
        }

        return files;
    }

    private Collection<VirtualFile> collectWildcardDirectories(@NotNull Matcher matcher, @NotNull VirtualFile directory) {
        Collection<VirtualFile> files = new HashSet<>();

        String pathName = matcher.group(1);
        String fileExtension = matcher.group(2).length() > 0 ? matcher.group(2) : null;

        pathName = StringUtils.stripEnd(pathName, "/");

        if(fileExtension == null) {
            // @TODO: filter files
            // 'assets/js/*'
            VirtualFile assetFile = VfsUtil.findRelativeFile(directory, pathName.split("/"));
            if(assetFile != null) {
                files.add(assetFile);
            }
        } else {
            // @TODO: filter files
            // 'assets/js/*.js'
            VirtualFile assetFile = VfsUtil.findRelativeFile(directory, pathName.split("/"));
            if(assetFile != null) {
                files.add(assetFile);
            }
        }

        return files;
    }

    private boolean isValidFile(@NotNull VirtualFile virtualFile) {
        if (virtualFile.isDirectory()) {
            return false;
        }

        if(filterExtension.size() == 0) {
            return true;
        }

        String extension = virtualFile.getExtension();
        return extension != null && this.filterExtension.contains(extension);
    }
}