import subprocess, urllib.parse, collections, re, os, time
from weakref import proxy
from config import config

_brace_re = re.compile(r'(?<=[^\\])[\{\}]')

def validate_bibtex_value(txt):
    '''
    we only perform rudimentary validation - just check whether braces are
    balanced. Otherwise, we assume the user knows what they are doing.

    Since the regex doesn't find leading braces, we add a leading space
    before running it.
    '''
    txt = txt or ''
    braces = _brace_re.findall(' ' + txt)

    counter = 0

    for brace in braces:
        counter += 1 if brace == '{' else -1
        if counter < 0:
            return False

    return counter == 0


class Link(object):
    '''
    auxiliary class for OrderedSet
    '''
    __slots__ = 'prev', 'next', 'key', '__weakref__'


class OrderedSet(collections.MutableSet):
    '''
    from https://raw.githubusercontent.com/ActiveState/code/3b27230f418b714bc9a0f897cb8ea189c3515e99/recipes/Python/576696_OrderedSet_with_Weakrefs/recipe-576696.py
    '''
    def __init__(self, iterable=None):
        self.__root = root = Link()         # sentinel node for doubly linked list
        root.prev = root.next = root
        self.__map = {}                     # key --> link
        if iterable is not None:
            self |= iterable

    def __len__(self):
        return len(self.__map)

    def __contains__(self, key):
        return key in self.__map

    def add(self, key):
        # Store new key in a new link at the end of the linked list
        if key not in self.__map:
            self.__map[key] = link = Link()
            root = self.__root
            last = root.prev
            link.prev, link.next, link.key = last, root, key
            last.next = root.prev = proxy(link)

    def discard(self, key):
        # Remove an existing item using self.__map to find the link which is
        # then removed by updating the links in the predecessor and successors.
        if key in self.__map:
            link = self.__map.pop(key)
            link.prev.next = link.next
            link.next.prev = link.prev

    def __iter__(self):
        # Traverse the linked list in order.
        root = self.__root
        curr = root.next
        while curr is not root:
            yield curr.key
            curr = curr.next

    def __reversed__(self):
        # Traverse the linked list in reverse order.
        root = self.__root
        curr = root.prev
        while curr is not root:
            yield curr.key
            curr = curr.prev

    def pop(self, last=True):
        if not self:
            raise KeyError('set is empty')
        key = next(reversed(self)) if last else next(iter(self))
        self.discard(key)
        return key

    def __repr__(self):
        if not self:
            return '%s()' % (self.__class__.__name__,)
        return '%s(%r)' % (self.__class__.__name__, list(self))

    def __eq__(self, other):
        if isinstance(other, OrderedSet):
            return len(self) == len(other) and list(self) == list(other)
        return not self.isdisjoint(other)


def locate(fn, case_sensitive=False, as_regex=True):
    '''
    use 'locate' to find a file
    '''
    args = ['locate']

    if not case_sensitive:
        args.append('-i')

    if as_regex:
        args.append('--regex')

    args.append(fn)

    try:
        p = subprocess.Popen(args, \
                                stdout=subprocess.PIPE, \
                                stderr=subprocess.STDOUT)
        blurb = p.communicate()[0].decode("utf-8")
        return blurb.strip().split()

    except FileNotFoundError: # expected if locate is not installed
        return []


def pubmed_search_url(data):
    '''
    create url from existing data fields to search for missing pmid
    '''
    fields = dict(
               author = 'au',
               journal = 'so',
               year = 'dp',
               volume = 'vol',
               pages = 'pg'
             )

    formatted = []

    for field, abbrev in fields.items():
        value = data.get(field)

        if value is not None:
            if field == 'author':
                first = value.split('and')[0]
                value = first.replace(',','')

            elif field == 'pages':
                value = value.split('-')[0]

            formatted.append("%s[%s]" % (value, abbrev))

    base_url = "https://www.ncbi.nlm.nih.gov/pubmed/?term="
    encoded = urllib.parse.quote(" ".join(formatted))

    return base_url + encoded


def xsel(text, isfile=False, timeout=2, max_size=128):
    """
    Copy text or file contents into X clipboard. Requires xsel to be installed.
    """
    if isfile:
        text = open(text, 'r').read().strip()

    if len(text) > max_size * 1024:
        raise ValueError("String exceeded %s kb, not copied to clipboard" % max_size)

    p = subprocess.Popen("xsel -i",
                         shell=True,
                         stdin=subprocess.PIPE,
                         stdout=subprocess.PIPE,
                         stderr=subprocess.PIPE
                        )
    try:
        weg, error = p.communicate(str.encode(text), timeout=timeout)
    except:
        raise IOError("Copying to clipboard failed")


def writefile(file_name, text):
    '''
    if file_name is empty, output is sent to clipboard.

    All other file names and paths are interpreted relative to the user's
    home directory. Howabout using makedirs?
    '''
    if not file_name:
        rv = 'clipboard'
        xsel(text)
    else:
        open(file_name,'w').write(text)
        rv = file_name.replace(os.path.expanduser('~'), '~')

    return rv


class Null(object):
    '''
    An object that doesn't complain about anything we ask of it.
    Essentially from Python cookbook p.278.
    '''
    def __init__(self, *a, **kw):
        pass

    def __iter__(self):
        return iter(())

    def __repr__(self):
        return 'Null()'

    def __nonzero__(self):
        return False

    __getitem__ = __setitem__ = __delitem__ = \
    __setattr__ = __getattr__ = __delattr__ = \
    __call__ = lambda self, *a, **kw: self



if __name__ == '__main__':

    data = dict(
             author = "<NAME> and <NAME>",
             year = "2012",
             journal = "Biochimica et Biophysica Acta",
             volume = "1818",
             pages = "1642-1647"
           )

    # print(pubmed_search_url(data))

    n = Null()
    n.harakiri()
    for i in n: # empty iterator
        1/0


import subprocess, urllib.parse, collections, re, os, time
from weakref import proxy
from config import config

_brace_re = re.compile(r'(?<=[^\\])[\{\}]')

def validate_bibtex_value(txt):
    '''
    we only perform rudimentary validation - just check whether braces are
    balanced. Otherwise, we assume the user knows what they are doing.

    Since the regex doesn't find leading braces, we add a leading space
    before running it.
    '''
    txt = txt or ''
    braces = _brace_re.findall(' ' + txt)

    counter = 0

    for brace in braces:
        counter += 1 if brace == '{' else -1
        if counter < 0:
            return False

    return counter == 0


class Link(object):
    '''
    auxiliary class for OrderedSet
    '''
    __slots__ = 'prev', 'next', 'key', '__weakref__'


class OrderedSet(collections.MutableSet):
    '''
    from https://raw.githubusercontent.com/ActiveState/code/3b27230f418b714bc9a0f897cb8ea189c3515e99/recipes/Python/576696_OrderedSet_with_Weakrefs/recipe-576696.py
    '''
    def __init__(self, iterable=None):
        self.__root = root = Link()         # sentinel node for doubly linked list
        root.prev = root.next = root
        self.__map = {}                     # key --> link
        if iterable is not None:
            self |= iterable

    def __len__(self):
        return len(self.__map)

    def __contains__(self, key):
        return key in self.__map

    def add(self, key):
        # Store new key in a new link at the end of the linked list
        if key not in self.__map:
            self.__map[key] = link = Link()
            root = self.__root
            last = root.prev
            link.prev, link.next, link.key = last, root, key
            last.next = root.prev = proxy(link)

    def discard(self, key):
        # Remove an existing item using self.__map to find the link which is
        # then removed by updating the links in the predecessor and successors.
        if key in self.__map:
            link = self.__map.pop(key)
            link.prev.next = link.next
            link.next.prev = link.prev

    def __iter__(self):
        # Traverse the linked list in order.
        root = self.__root
        curr = root.next
        while curr is not root:
            yield curr.key
            curr = curr.next

    def __reversed__(self):
        # Traverse the linked list in reverse order.
        root = self.__root
        curr = root.prev
        while curr is not root:
            yield curr.key
            curr = curr.prev

    def pop(self, last=True):
        if not self:
            raise KeyError('set is empty')
        key = next(reversed(self)) if last else next(iter(self))
        self.discard(key)
        return key

    def __repr__(self):
        if not self:
            return '%s()' % (self.__class__.__name__,)
        return '%s(%r)' % (self.__class__.__name__, list(self))

    def __eq__(self, other):
        if isinstance(other, OrderedSet):
            return len(self) == len(other) and list(self) == list(other)
        return not self.isdisjoint(other)


def locate(fn, case_sensitive=False, as_regex=True):
    '''
    use 'locate' to find a file
    '''
    args = ['locate']

    if not case_sensitive:
        args.append('-i')

    if as_regex:
        args.append('--regex')

    args.append(fn)

    try:
        p = subprocess.Popen(args, \
                                stdout=subprocess.PIPE, \
                                stderr=subprocess.STDOUT)
        blurb = p.communicate()[0].decode("utf-8")
        return blurb.strip().split()

    except FileNotFoundError: # expected if locate is not installed
        return []


def pubmed_search_url(data):
    '''
    create url from existing data fields to search for missing pmid
    '''
    fields = dict(
               author = 'au',
               journal = 'so',
               year = 'dp',
               volume = 'vol',
               pages = 'pg'
             )

    formatted = []

    for field, abbrev in fields.items():
        value = data.get(field)

        if value is not None:
            if field == 'author':
                first = value.split('and')[0]
                value = first.replace(',','')

            elif field == 'pages':
                value = value.split('-')[0]

            formatted.append("%s[%s]" % (value, abbrev))

    base_url = "https://www.ncbi.nlm.nih.gov/pubmed/?term="
    encoded = urllib.parse.quote(" ".join(formatted))

    return base_url + encoded


def xsel(text, isfile=False, timeout=2, max_size=128):
    """
    Copy text or file contents into X clipboard. Requires xsel to be installed.
    """
    if isfile:
        text = open(text, 'r').read().strip()

    if len(text) > max_size * 1024:
        raise ValueError("String exceeded %s kb, not copied to clipboard" % max_size)

    p = subprocess.Popen("xsel -i",
                         shell=True,
                         stdin=subprocess.PIPE,
                         stdout=subprocess.PIPE,
                         stderr=subprocess.PIPE
                        )
    try:
        weg, error = p.communicate(str.encode(text), timeout=timeout)
    except:
        raise IOError("Copying to clipboard failed")


def writefile(file_name, text):
    '''
    if file_name is empty, output is sent to clipboard.

    All other file names and paths are interpreted relative to the user's
    home directory. Howabout using makedirs?
    '''
    if not file_name:
        rv = 'clipboard'
        xsel(text)
    else:
        open(file_name,'w').write(text)
        rv = file_name.replace(os.path.expanduser('~'), '~')

    return rv


class Null(object):
    '''
    An object that doesn't complain about anything we ask of it.
    Essentially from Python cookbook p.278.
    '''
    def __init__(self, *a, **kw):
        pass

    def __iter__(self):
        return iter(())

    def __repr__(self):
        return 'Null()'

    def __nonzero__(self):
        return False

    __getitem__ = __setitem__ = __delitem__ = \
    __setattr__ = __getattr__ = __delattr__ = \
    __call__ = lambda self, *a, **kw: self



if __name__ == '__main__':

    data = dict(
             author = "<NAME> and <NAME>",
             year = "2012",
             journal = "Biochimica et Biophysica Acta",
             volume = "1818",
             pages = "1642-1647"
           )

    # print(pubmed_search_url(data))

    n = Null()
    n.harakiri()
    for i in n: # empty iterator
        1/0


we only perform rudimentary validation - just check whether braces are     balanced. Otherwise, we assume the user knows what they are doing.      Since the regex doesn't find leading braces, we add a leading space     before running it. auxiliary class for OrderedSet from https://raw.githubusercontent.com/ActiveState/code/3b27230f418b714bc9a0f897cb8ea189c3515e99/recipes/Python/576696_OrderedSet_with_Weakrefs/recipe-576696.py # sentinel node for doubly linked list # key --> link # Store new key in a new link at the end of the linked list # Remove an existing item using self.__map to find the link which is # then removed by updating the links in the predecessor and successors. # Traverse the linked list in order. # Traverse the linked list in reverse order. use 'locate' to find a file # expected if locate is not installed create url from existing data fields to search for missing pmid Copy text or file contents into X clipboard. Requires xsel to be installed. if file_name is empty, output is sent to clipboard.      All other file names and paths are interpreted relative to the user's     home directory. Howabout using makedirs? An object that doesn't complain about anything we ask of it.     Essentially from Python cookbook p.278. # print(pubmed_search_url(data)) # empty iterator