import os
import vim
from glob import glob
from itertools import chain
from subprocess import check_output

bib_extensions = ["bib",
                    "bibtex",
                    "ris",
                    "json",
                    "enl",
                    "wos",
                    "medline",
                    "copac",
                    "xml"]


class SourceCollator():
    def __init__(self, fname=None, query=None, sources="bcg", extra_sources=([], []), **extra_args):
        # nvim's python host doesn't change the directory the same way vim does
        if vim.eval('has("nvim")') == '1':
            os.chdir(vim.eval('expand("%:p:h")'))
        self.fname = fname
        self.query = query
        self.sources = sources
        self.extra_sources = extra_sources
        self.extra_args = extra_args

    def find_bibfiles(self):
        def curdir_by_name_search():
            """
            Search for bibiographies with the same name as the current file in the
            current dir.
            """

            if self.fname in (None, ""): return []

            file_name_prefix = os.path.splitext(self.fname)[0]
            search_paths = [file_name_prefix + "." + f for f in bib_extensions]

            bibfiles = [os.path.abspath(f) for f in search_paths if os.path.exists(f)]
            return bibfiles

        def curdir_all_search():
            """
            Search for any other bibliographies in the current dir.
            Note: This does not stop bibliographies picked up in b_search() from being found.
            """

            relative_bibfiles = []
            for ext in bib_extensions:
                relative_bibfiles.extend(glob("*."+ext))
            bibfiles = [os.path.abspath(f) for f in relative_bibfiles]
            return bibfiles

        def git_search():
            """
            Search for any bibliographies in the git repository.
            """
            try:
                root_dir = check_output(['git', 'rev-parse', '--show-toplevel']).decode().strip()
            except:
                return []
            git_files = check_output(['git', 'ls-tree',
                                      '-r', '--full-tree', '--name-only',
                                      'HEAD']).decode().split('\n')
            relpaths = []
            for f in git_files:
                ext = os.path.splitext(f)[1]
                if ext != '' and ext[1:] in bib_extensions:
                    relpaths.append(f)
            bibfiles = [os.path.join(root_dir, f) for f in relpaths]
            return bibfiles

        def pandoc_local_search():
            """
            Search for bibliographies in the pandoc data dirs.
            """

            if os.path.exists(os.path.expandvars("$HOME/.pandoc/")):
                b = os.path.expandvars("$HOME/.pandoc/")
            elif os.path.exists(os.path.expandvars("%APPDATA%/pandoc/")):
                b = os.path.expandvars("%APPDATA%/pandoc/")
            else:
                return []

            search_paths = [b + "default." + f for f in bib_extensions]
            bibfiles = [os.path.abspath(f) for f in search_paths if os.path.exists(f)]
            return bibfiles

        def texmf_search():
            """
            Search for bibliographies in the texmf data dirs.
            """

            texmf = check_output(["kpsewhich", "-var-value", "TEXMFHOME"]).rstrip()

            if os.path.exists(texmf):
                search_paths = (texmf.decode() + "/**/*." + f for f in bib_extensions)
                relative_bibfiles = (glob(f, recursive=True) for f in search_paths)
                bibfiles = [os.path.abspath(f) for f in chain.from_iterable(relative_bibfiles)]
                return bibfiles

            return []

        def explicit_global_search():
            """
            Add bibliographies defined in pandoc#biblio#bibs,
            passed to the collator through the extra_sources argument
            """

            return self.extra_sources[0]

        search_methods = {"b": curdir_by_name_search,
                          "c": curdir_all_search,
                          "l": pandoc_local_search,
                          "t": texmf_search,
                          "g": explicit_global_search,
                          "G": git_search}


        bibfiles = []
        for f in self.sources:
            bibfiles.extend(search_methods.get(f, list)())
        # add buffer-local bibliographies, defined in b:pandoc_biblio_bibs,
        # passed to the collator through the extra_sources argument
        bibfiles.extend(self.extra_sources[1])

        # check if the items in bibfiles are readable and not directories
        # also, ensure items are unique
        if bibfiles != []:
            bibfiles = list(set(filter(lambda f : os.access(f, os.R_OK) and not os.path.isdir(f), bibfiles)))

        return bibfiles

    def collate(self):
        """
        Retrieves the data from the sources.
        Should be overriden by sub-classes.
        """
        pass

