# Here we create BibBase supporting infrastructure:
#  1. An Arpeggio Grammar is declared so we can parse a BibTeX file
#  2. An Arpeggio Visitor class is declared so the AST from the BibTeX file can be conveniently interpreted
#  3. BibTeX translators are declared so each kind of BibTeX file (with the specifics of Web of Science, Scopus,
#     ACM Digital Library and IEEE Xplore) can be translated into a set of BibRef instances

# Declaring Arpeggio grammar for BibTeX files

from arpeggio import *
from arpeggio import RegExMatch as _


def bibFile():  return ZeroOrMore(bibEntry), EOF
def bibEntry(): return bibType, "{", bibKey, ",", bibFields, "}"

def bibType():   return _(r'@\w+')
def bibKey():    return _(r'[^,]+')
def bibFields(): return [(field, ",", bibFields), (field, Optional(","))]

def field():     return fieldName, "=", fieldValue
def fieldName(): return OneOrMore(fieldNamePart)
def fieldNamePart(): return _(r'[-\w]+')
def fieldValue(): return [complexValue, stringValue, simpleValue]

def complexValue(): return "{", complexContent, "}"
def simpleValue():  return _(r'[^,]*')
def stringValue():  return '"', stringContent, '"'

def complexContent(): return ZeroOrMore([complexPiece, textContent])
def complexPiece():   return "{", complexContent, "}"
def stringContent():  return _(r'((\\")|[^"])*')
def textContent():    return _(r'((\\")|[^{}])+')

# Declaring a BibTeXVisitor, that is able to navigate along all the AST generated by the parser
# to turn a BibTeX file into a Python list, each entry being a Python dictionary describing
# all the fields present in a single BiBteX reference


class BibTeXVisitor(PTNodeVisitor):
    def visit_bibFile(self, node, children):
        return [x for x in children if type(x) is dict]
    
    def visit_bibEntry(self, node, children):
        bibRef = {
            'type': children[0],
            'id': children[1].strip(),
            'field': {}
        }
        fields = {}
        for field in children[2]:
            fields[field[0]] = field[1]
        bibRef['field'] = fields
        return bibRef
    
    def visit_bibFields(self, node, children):
        bibFields = [children[0]]
        for fieldList in [x for x in children if type(x) is list]:
            for field in fieldList:
                bibFields.append(field)
        return bibFields
    
    def visit_field(self, node, children):
        if len(children) < 2:
            print(children)
        field = (children[0], children[1])
        return field
    
    def visit_fieldName(self, node, children):
        name = ""
        return name.join(children).strip()
    
    def visit_fieldValue(self, node, children):
        if len(children) == 0:
            return ''
        return children[0]
    
    def visit_complexContent(self, node, children):
        complexContent = self.asString(children)
        return complexContent
    
    def visit_complexPiece(self, node, children):
        complexPiece = "{" + self.asString(children) + "}"
        return complexPiece
    
    def asString(self, pieces):
        if type(pieces) is str:
            text = pieces
        else:
            text = ""
        for piece in pieces:
            if type(piece) is list:
                part = self.asString(piece)
            elif type(piece) is str:
                part = piece
            else:
                part = "{" + piece.toString() + "}"
            text = text + part
        return text

