import re, sys, string
from karpathos import Person, FamilyGraph


"""
Defines some routines that load and save GEDCOM files.  They return a standard
FamilyGraph.  These are the only public data from this module.  Specifically,
they are called:

FamilyGraph loadGEDCOMFile (file)
saveGEDCOMFile (file, FamilyGraph)

They do add some extra data added to the various Person etc classes.  This
should be ignored by other applications.

Also, a number of smaller support classes are created here.  Each
represents a GEDCOM entity, and as such it has an id which is its tag
from the gedcom file.

There is a routine getGDRef() which returns a GEDCOM style id tag for
any object it is called on (like @p122@).  Also there are several
routines dumpGDXXXRec(obj,indent=0) which dump the record to a file,
starting at the supplied indent value.
"""

### --------------------------------

def saveGEDCOMFile(file, graph):
    """
    Saving a GEDCOM file occurs in three steps.

    Step 1: build the family objects that contain the interactions
    Step 2: output the families, then the individuals
    Step 3: we're done
    """
    def buildFamilies (graph):
        """
        Returns a list of family objects culled from the family graph
        Basically looks for every spouse pair it can find and creates
        a family for them, adding any kids.
        """
        fams = []
        def addFamilies (p, recurse, fams=fams):
            """
            Starting with p adds any unmarked spouses etc to the fams
            list
            """
            if not p.mark:
                p.mark = 1
                for spouse in p.spouses:
                    if not spouse.mark:
                        # Neither p nor their spouse has been visited
                        # yet.  Create a new family object, and set the
                        # husband and wife.
                        f = Family(len(fams))
                        fams.append(f)
                        
                        f.setSpouse (p)
                        f.setSpouse (spouse)
                        
                        # Check for same sex marriages or messed up data
                        if not f.husb or not f.wife:
                            sys.stderr.write(
                                "Warning: missing husb or "
                                "wife from family %s\n" 
                                % str(f))
                            pass
                        
                        # Look through kids for kids that came from this
                        # family.
                        for kid in p.kids:
                            if spouse in kid.parents: f.addChild (kid)
                            pass
                        pass
                    pass
                for kid in p.kids:
                    recurse (kid, recurse)
                    pass
                return

        for p in graph.people:
            p.mark = 0
            p.famc = []
            p.fams = []
            pass
        for p in graph.people:
            if p.isPrimal(): addFamilies (p, addFamilies)
            pass
        return fams
    
    families = buildFamilies(graph)
    print >> file, """0 HEAD
1 SOUR unregistered
2 NAME papa_format.py
2 VERS 1.0
1 DEST ANSTFILE
1 SUBM @S0@
1 GEDC
2 VERS 5.5
2 FORM LINEAGE_LINKED
1 LANG English
1 CHAR ASCII
2 VERS MacOS Roman
0 @S0@ SUBM
1 NAME The Matsakis Family"""
    for f in families: dumpGDFamRec(file, f)
    for p in graph.people: dumpGDIndiRec(file, p)
    print >> file, "0 TRLR"
    return

def loadGEDCOMFile(file):
    raise BadFormatError ("GEDCOM File loading not yet enabled.")

### --------------------------------

def dumpGDFamRec(file,family,indent=0):
    return family.dumpGDFamRec(file,indent)

def getGDRef (object):
    return "@%s%s@" % (object.typecode, object.id)

class Family:
    """
    Families are used when loading and outputting GEDCOM files, as GEDCOM
    files store all the connections between People in a Family Graph in
    Family records.  When inputting from a GEDCOM file, the references
    to husband and wife can be links that will be resolved later, or
    actual pointers.  Otherwise they must be pointers.
    
    Has the following fields:
    
    id - some unique string for this family.
    
    husb - a male Person
    
    wife - a female Person
    
    kids - a list of Persons
    """
    def __init__(self, id):
        self.typecode = 'f'
        self.id = id
        self.husb = None
        self.wife = None
        self.kids = []
        return
    
    def setSpouse (self, p):
        if p.sex == 'M': self.husb = p
        elif p.sex == 'F': self.wife = p
        else: raise BadFormatError('Spouse of unknown sex: %s'%str(p))
        p.fams.append (self)
        return
    
    def addChild (self, p):
        self.kids.append (p)
        p.famc.append (self)
        return
    
    def getLastChild (self):
        return self.kids[-1]
    
    def dumpGDFamRec (self, file, indent=0):
        print >> file, "%d %s FAM" % (indent, getGDRef(self))
        indent = indent + 1
        if self.husb: print >> file, "%d HUSB %s" % (indent,
                                                     getGDRef(self.husb))
        if self.wife: print >> file, "%d WIFE %s" % (indent,
                                                     getGDRef(self.wife))
        if self.kids:
            print >> file, "%d NCHI %d" % (indent, len(self.kids))
            for kid in self.kids:
                print >> file, "%d CHIL %s" % (indent, getGDRef(kid))
                pass
            pass
        return
    
    __str__ = getGDRef
    pass # end class Family

class GEDCOMPerson(Person):
    """
    A GEDCOMPerson is essentially a normal Person except that the
    constructor is defined to parse in a GEDCOM Person from a file.
    """

    #
    # GEDCOM Input Format
    #
    
    def __init__(self, clone=None):
        Person.__init__(self, clone)
        
        # Used during gedcom output, set by Family
        self.famc = []
        self.fams = []

        return

    #
    # GEDCOM Output Format
    #

    def getGDRef (self):
        return "@p%s@"%str(self.id)

    pass # end class GEDCOMPerson

def dumpGDIndiRec (file, person, indent=0):
    print >> file, "%d %s INDI" % (indent, getGDRef(person))
    indent = indent + 1

    def _dumpGDBirthDeathStats (indent, str, stats, person=person, file=file):
        if stats['occurred']:
            print >> file, "%d %s" % (indent, str)
            indent = indent + 1
            if stats['date']: print >> file, "%d DATE %s" % (indent,
                                                             stats['date'])
            if stats['age'] != '?': print >> file, "%d AGE %s" % (indent,
                                                                  stats['age'])
            pass
        return

    # Print >> File, name, highlighting surname with // like
    # GEDCOM wants.  Assumes that first / last names
    # are separated by spaces, and that the last one
    # is the surname.  If only one name, assume it is
    # not a surname.
    print >> file, ("%d NAME" % indent),
    names = person.name.split()
    if len(names) == 1:
        print >> file, person.name
    else:
        for name in names[:-1]:
            print >> file, name,
            pass
        print >> file, "/%s/" % names[-1]
        pass
    
    if person.sex != '?': print >> file, "%d SEX %s" % (indent, person.sex)
    
    # Print >> File, birth and information, if any
    _dumpGDBirthDeathStats (indent, 'BIRT', person.birthstats)
    _dumpGDBirthDeathStats (indent, 'DEAT Y', person.deathstats)
    
    # Phone number.
    if person.phonenum: print >> file, "%d PHON %s" % (indent, person.phonenum)
    
    # Print >> File, comments etc.
    for c in person.comments: print >> file, "%d NOTE %s" % (indent, c)
    for c in person.seekrits: print >> file, "%d NOTE SEEKRIT: %s" % (indent, c)
    
    # Print >> File, any families they belong to.
    # NOTE: this is extra data added by buildFamilies.  Should prob
    # move it to an external hashtable or something.
    for fam in person.famc:
        print >> file, "%d FAMC %s" % (indent, getGDRef(fam))
        pass
    for fam in person.fams:
        print >> file, "%d FAMS %s" % (indent, getGDRef(fam))
        pass
    return
