# coding=utf-8
'''
Created on 18-12-2011

@author: lennyn
'''

import os
import subprocess
import logging
import re

from django.utils.encoding import smart_unicode, smart_str

from pybtex.database.input import bibtex as bibtex_input
from pybtex.database.output import bibtex as bibtex_output
from pybtex.database import BibliographyData
from StringIO import StringIO

from bibmate.models import Person, Publication, Authorship, Editorship, Keyword
from bibmate import settings
import pdf_utils, bibtex_utils
import exceptions

def check_bibtex(bibtex_text):
    p = subprocess.Popen(
                         settings.CHECK_BIBTEX_EXEC, 
                         shell=True,
                         stdin=subprocess.PIPE, 
                         stdout=subprocess.PIPE,
                         stderr=subprocess.PIPE)
    _, errStr = p.communicate(input=smart_str(bibtex_text))
    if p.returncode != 0:
        raise exceptions.BibtexException(smart_unicode(errStr))

def import_bibtex(bibtex_text):
    assert type(bibtex_text) == unicode
    check_bibtex(bibtex_text)
    bib_data = bibtex_utils.get_bib_data(bibtex_text)
    _add_publications(bib_data)

#XXX - troche bezsensowna nazwa funkcji
def update_bibtex_and_html_fields(pub_id):
    pub = Publication.objects.get(id=pub_id)
    parser = bibtex_input.Parser()
    bib_data = parser.parse_stream(StringIO(pub.bibtex))
    
    assert len(bib_data.entries) == 1
        
    entry = bib_data.entries[pub.id]
    #~ if pdf_utils.uploaded_file_exists(pub_id):
        #~ entry.fields['url'] = pdf_utils.pubid2url(pub.id)
    #~ elif entry.fields.get('url', '') == pdf_utils.pubid2url(pub_id):
        #~ del entry.fields['url']
        
    pub.bibtex = bibtex_utils.get_bibtex_text(pub.id, entry)
    pub.update_html()
    pub.save()

def _get_full_name(p):
    res = u''
    for part in ['first', 'middle', 'prelast', 'last', 'lineage']:
        part_text = p.get_part_as_text(part)
        if part_text:
            res += part_text
            res += u' '
    return _detex_string(res.strip())
    #~ return u''.join([p.get_part_as_text(part) for part in ['first', 'middle', 'prelast', 'last', 'lineage']])

def _get_and_add_people(entry, person_type):
    assert person_type in ('author', 'editor')
    if person_type in entry.persons:
        for a in entry.persons[person_type]:
            full_name = _get_full_name(a)
            person = Person.objects.filter(name__exact=full_name)
            if person:
                yield person[0]
            else:
                yield Person.objects.create(name=full_name)
    elif 'crossref' in entry.fields:
        if person_type == 'author':
            people = Publication.objects.get(id=entry.fields['crossref']).authors.all()
        else:
            people = Publication.objects.get(id=entry.fields['crossref']).editors.all()
        for person in people:
            yield person

def _get_and_add_keywords(entry):
    keywordsStr = entry.fields.get('keywords', '')
    for keywordName in [k.strip() for k in keywordsStr.split(',') if k.strip()]:
        keyword = Keyword.objects.filter(name=keywordName)
        if keyword:
            yield keyword[0]
        else:
            yield Keyword.objects.create(name=keywordName)
        

def _add_publications(bib_data):
    for i, (key, entry) in enumerate(bib_data.entries.items()):
        logging.warn('add %d %s' %(i, entry.fields['title']))
        _add_and_get_one_publication(key, entry, bib_data)
        update_bibtex_and_html_fields(key)

#XXX - it is a little bit ugly as each crossrefed publication may be added two times
def _add_and_get_one_publication(key, entry, bib_data):
    bibtex = bibtex_utils.get_bibtex_text(key, entry)
    title = _detex_string(entry.fields['title'])
    
    crossrefKey = entry.fields.get('crossref', None)
    if crossrefKey and Publication.objects.filter(id=crossrefKey).exists():
        crossref = Publication.objects.get(id=crossrefKey)
    elif crossrefKey:
        crossref = _add_and_get_one_publication(crossrefKey, bib_data.entries[crossrefKey], bib_data)
    else:
        crossref = None
    
    year = _get_year(entry)
    if year is None:
        year = crossref.year
    
    if Publication.objects.filter(id=key).exists():
        pub = Publication.objects.get(id=key)
        pub.crossref = crossref
        pub.title = title
        pub.year = year
        pub.bibtex = bibtex
        pub.authors.clear()
        pub.editors.clear()
        pub.keywords.clear()
    else:
        pub = Publication.objects.create(id=key, crossref=crossref, title=title, year=year, bibtex=bibtex)
    
    if 'abstract' in entry.fields:
        pub.abstract = _detex_string(entry.fields['abstract'])
    
    authors = list(_get_and_add_people(entry, 'author'))
    editors = list(_get_and_add_people(entry, 'editor'))
    for idx, author in enumerate(authors):
        Authorship(publication=pub, person=author, idx=idx).save()
    for idx, editor in enumerate(editors, start=len(authors)):
        Editorship(publication=pub, person=editor, idx=idx).save()
    
    keywords = _get_and_add_keywords(entry)
    for keyword in keywords:
        pub.keywords.add(keyword)
    
    pub.save()
    update_bibtex_and_html_fields(pub.id)
    return pub

def _get_year(entry):
    year_str = entry.fields.get('year', None)
    if year_str:
        return int(year_str)
    else:
        return None
#        print entry

def _detex_string(string):
    string = _ogonkify_string(string)
    p = subprocess.Popen(
                    [settings.DETEX_PATH],
                    shell=True,
                    stdin=subprocess.PIPE,
                    stdout=subprocess.PIPE)
    (res_out, _) = p.communicate(string.encode('utf8'))
    return res_out

def _ogonkify_string(string):
    res = string
    res = re.sub(r'\\L\b', u'Ł', res)
    res = re.sub(r'\\l\b', u'ł', res)
    return u''+res

