#!/usr/bib/env python
from __future__ import print_function
import sys
import bibtexparser
from bibtexparser.bwriter import BibTexWriter
from bibtexparser.bibdatabase import BibDatabase
import argparse
import operator
import textwrap
from itertools import groupby
from multiprocessing import Pool
from bibcure.in_db import update_bibs_in
from bibcure.out_db import update_bibs_out
from bibcure.arxiv import update_bibs_arxiv
from bibcure.title import update_bibs_get_doi
from bibcure.doi import update_bibs_from_doi
from bibcure.database import Db_abbrev

db_abbrev = Db_abbrev()


def get_status(bib):
    text = ""
    state = "are_not_journal"

    if "journal" in bib:
        if "arxiv" in bib["journal"].lower():
            text = ""
            state = "are_arxiv"
        else:
            state = "are_out_db"
            expanded_index = db_abbrev.get_index(bib["journal"], key="name")
            abbrev_index = db_abbrev.get_index(bib["journal"], key="abbrev")
            if expanded_index != -1:
                text = db_abbrev.db[expanded_index]["abbrev"]
                state = "can_be_abbreviated"
            elif abbrev_index != -1:
                text = db_abbrev.db[abbrev_index]["name"]
                state = "can_be_expanded"
    bib["_type"] = state
    bib["_text"] = text

    return bib


def save_output_bib(updated_bibs, output_file):
    writer = BibTexWriter()
    new_bibtex = BibDatabase()
    new_bibtex.entries = updated_bibs
    with open(output_file, 'w') as bibfile:
        bibfile.write(writer.write(new_bibtex))


def main():
    parser = argparse.ArgumentParser(
        prog="bibcure",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        description=textwrap.dedent('''\
        Bibcure helps in boring tasks by keeping
        your bibfile up to date and normalized.

        $ bibcure -i input.bib -o output.bib
        Given a bib file...

            . check sure the Arxiv items have been published, then update
                                    them(requires internet connection)
            . complete all fields(url, journal, etc) of all bib items using DOI
                                    number(requires internet connection)
            . find and create DOI number associated with each bib item which
                                    has not DOI field(requires internet
                                    connection)
            . abbreviate jorunals names

       $ doitobib 10.1038/s41524-017-0032-0
       Given a DOI number...

            . get bib item given a doi(requires internet connection)

       $ titletobib An useful paper
       Given a title...

            . search papers related and return a bib for the selected
                                    paper(requires internet connection)

       $ arxivcheck 1601.02785
       Given a arxiv id...

            . given an arixiv id, check if has been published, and then returns
                                    the updated bib (requires internet
                                    connection)
        -----------------------------------------------------
            @author: Bruno Messias
            @email: messias.physics@gmail.com
            @telegram: @brunomessias
            @github: https://github.com/bibcure/bibcure
        ''')
    )
    parser.add_argument(
        "--input", "-i",
        required=True,
        type=argparse.FileType("r"),
        help="bibtex input file"
    )
    parser.add_argument(
        "--output", "-o",
        required=True,
        help="bibtex output file")

    args = parser.parse_args()
    dict_parser = {
        'keywords': 'keyword',
        'keyw': 'keyword',
        'subjects': 'subject',
        'urls': 'url',
        'link': 'url',
        'links': 'url',
        'editors': 'editor',
        'authors': 'author'}
    parser = bibtexparser.bparser.BibTexParser()
    parser.alt_dict = dict_parser
    bibtex = bibtexparser.loads(args.input.read(), parser=parser)
    bibs = bibtex.entries
    if len(bibs) == 0:
        print("Input File is empty or corrupted.")
        sys.exit(1)

    bibs = update_bibs_from_doi(bibs)
    bibs = update_bibs_get_doi(bibs)
    bibs = update_bibs_arxiv(bibs)

    pool = Pool()
    bibs = pool.map(get_status, bibs)
    pool.close()
    pool.join()

    bibs.sort(key=operator.itemgetter('_type'))

    grouped_bibs_by_type = {}
    for key, items in groupby(bibs, lambda i: i["_type"]):
        grouped_bibs_by_type[key] = list(items)

    updated_bibs = []
    for key, bibs_items in grouped_bibs_by_type.items():
        if len(bibs_items) > 0:
            print("{:d} bibs >> {}".format(
                len(bibs_items),
                bibs_items[0]["_type"].replace("_", " ")
            ))

    for key, bibs_items in grouped_bibs_by_type.items():
        if len(bibs_items) > 0:
            if "can_be_abbreviated" in bibs_items[0]["_type"]:
                updated_bibs += update_bibs_in(bibs_items, db_abbrev)
            elif "out_db" in bibs_items[0]["_type"]:
                updated_bibs += update_bibs_out(bibs_items, db_abbrev)
            else:
                updated_bibs += bibs_items

    for bib in updated_bibs:
        if set(("_text", "_type")).issubset(bib):
            bib.pop("_text", None)
            bib.pop("_type", None)
    save_output_bib(updated_bibs, args.output)
    db_abbrev.close()


if __name__ == "__main__":
    main()
