// Parser
        public static CIndividualRecord Parse( CGedcom gedcom, int nLevel )
        {
            CGedcomLine gedcomLine;
            bool bParsingFinished;

            // Temporary holders for class members.
            CPersonalNameStructure personalNameStructure;
            CIndividualEventStructure individualEventStructure;
            CLdsOrdinance ldsIndividualOrdinance;
            CChildToFamilyLink childToFamilyLink;
            CSpouseToFamilyLink spouseToFamilyLink;
            CAssociationStructure associationStructure;
            CNoteStructure noteStructure;
            CSourceCitation sourceCitation;
            CMultimediaLink multimediaLink;
            ArrayList alAliases = new ArrayList();

            // Without an xref header, we can't continue
            if ((gedcomLine = gedcom.GetLine(nLevel, "INDI")) == null)
            {
                // Not one of us
                return null;
            }

            CIndividualRecord ir = new CIndividualRecord( gedcom );

            ir.m_xref = gedcomLine.XrefID;
            gedcom.IncrementLineIndex(1);

            do
            {
                bParsingFinished = true;

                // Family Historian tag _FLGS
                if( (gedcomLine = gedcom.GetLine(nLevel+1, "_FLGS")) != null )
                {
                    gedcom.IncrementLineIndex(1);
                    if( (gedcomLine = gedcom.GetLine(nLevel+2, "__LIVING")) != null )
                    {
                        ir.m_sStillLiving = gedcomLine.LineItem;
                        gedcom.IncrementLineIndex(1);
                    }
                    bParsingFinished = false;
                }
                // Let Record have a go at parsing the rest
                else if( ir.ParseRecord( gedcom, nLevel ) )
                {
                    bParsingFinished = false;
                    continue;
                }
                else if( (gedcomLine = gedcom.GetLine( nLevel+1, "RESN" )) != null )
                {
                    ir.m_sRestrictionNotice = gedcomLine.LineItem;
                    gedcom.IncrementLineIndex(1);
                    bParsingFinished = false;
                }
                else if( (personalNameStructure = CPersonalNameStructure.Parse( gedcom, nLevel+1 )) != null )
                {
                    ir.m_alPersonalNameStructures.Add( personalNameStructure );
                    bParsingFinished = false;
                }
                else if( (gedcomLine = gedcom.GetLine(nLevel+1, "SEX")) != null )
                {
                    ir.m_sSexValue = gedcomLine.LineItem;
                    gedcom.IncrementLineIndex(1);
                    bParsingFinished = false;
                }
                else if( (individualEventStructure = CIndividualEventStructure.Parse( gedcom, nLevel+1 )) != null )
                {
                    ir.m_alIndividualEventStructures.Add( individualEventStructure );
                    bParsingFinished = false;

                    if( individualEventStructure.Type == "ADOP" )
                    {
                        gedcom.m_alAdoptedIndividuals.Add( ir );
                    }
                }
                else if( (ldsIndividualOrdinance = CLdsOrdinance.Parse( gedcom, nLevel+1 )) != null )
                {
                    ir.m_alLdsIndividualOrdinances.Add( ldsIndividualOrdinance );
                    bParsingFinished = false;
                }
                else if( (childToFamilyLink = CChildToFamilyLink.Parse( gedcom, nLevel+1 )) != null )
                {
                    ir.m_alChildToFamilyLinks.Add( childToFamilyLink );
                    bParsingFinished = false;
                }
                else if( (spouseToFamilyLink = CSpouseToFamilyLink.Parse( gedcom, nLevel+1 )) != null )
                {
                    ir.m_alSpouseToFamilyLinks.Add( spouseToFamilyLink );
                    bParsingFinished = false;
                }
                else if( (gedcomLine = gedcom.GetLine(nLevel+1, "SUBM")) != null )
                {
                    ir.m_alXrefSubms.Add( gedcomLine.LinePointer );
                    gedcom.IncrementLineIndex(1);
                    bParsingFinished = false;
                }
                else if( (associationStructure = CAssociationStructure.Parse( gedcom, nLevel+1 )) != null )
                {
                    ir.m_alAssociationStructures.Add( associationStructure );
                    bParsingFinished = false;
                }
                else if( (gedcomLine = gedcom.GetLine(nLevel+1, "ALIA")) != null )
                {
                    if( gedcomLine.LinePointer != null )
                    {
                        ir.m_alXrefAlias.Add( gedcomLine.LinePointer );
                        gedcom.IncrementLineIndex(1);
                        bParsingFinished = false;
                    }
                    else if( gedcomLine.LineItem != null && gedcomLine.LineItem.Length > 0 )
                    {
                        alAliases.Add( gedcomLine.LineItem );
                        gedcom.IncrementLineIndex(1);
                        bParsingFinished = false;
                    }
                }
                else if( (gedcomLine = gedcom.GetLine(nLevel+1, "ANCI")) != null )
                {
                    ir.m_alXrefAncis.Add( gedcomLine.LinePointer );
                    gedcom.IncrementLineIndex(1);
                    bParsingFinished = false;
                }
                else if( (gedcomLine = gedcom.GetLine(nLevel+1, "DESI")) != null )
                {
                    ir.m_alXrefDesis.Add( gedcomLine.LinePointer );
                    gedcom.IncrementLineIndex(1);
                    bParsingFinished = false;
                }
                else if( (gedcomLine = gedcom.GetLine(nLevel+1, "RFN")) != null )
                {
                    ir.m_sPermanentRecordFileNumber = gedcomLine.LineItem;
                    gedcom.IncrementLineIndex(1);
                    bParsingFinished = false;
                }
                else if( (gedcomLine = gedcom.GetLine(nLevel+1, "AFN")) != null )
                {
                    ir.m_sAncestralFileNumber = gedcomLine.LineItem;
                    gedcom.IncrementLineIndex(1);
                    bParsingFinished = false;
                }
                else if( (sourceCitation = CSourceCitation.Parse( gedcom, nLevel+1 )) != null )
                {
                    ir.m_alSourceCitations.Add( sourceCitation );
                    bParsingFinished = false;
                }
                else if( (multimediaLink = CMultimediaLink.Parse( gedcom, nLevel+1 )) != null )
                {
                    ir.m_alMultimediaLinks.Add( multimediaLink );
                    bParsingFinished = false;
                }
                else if( (noteStructure = CNoteStructure.Parse( gedcom, nLevel+1 )) != null )
                {
                    ir.m_alNoteStructures.Add( noteStructure );
                    bParsingFinished = false;
                }
                else if( ( gedcomLine = gedcom.GetLine()).Level > nLevel )
                {
                    LogFile.TheLogFile.WriteLine( LogFile.DT_GEDCOM, LogFile.EDebugLevel.Warning, "Unknown tag :" );
                    LogFile.TheLogFile.WriteLine( LogFile.DT_GEDCOM, LogFile.EDebugLevel.Warning, gedcomLine.ToString() );
                    gedcom.IncrementLineIndex(1);
                    bParsingFinished = false;
                }
            }
            while( !bParsingFinished );

            // Workaround for GEDCOM that incorrectly contains this: ALIA Fred /Bloggs/ (instead of ALIA @I12@)
            if( alAliases.Count > 0 )
            {
                foreach( string sAlias in alAliases )
                {
                    sAlias.Trim();
                    if( sAlias.Length > 0 )
                    {
                        CPersonalNameStructure pns = new CPersonalNameStructure(gedcom);
                        pns.m_sNamePersonal = sAlias;
                        ir.m_alPersonalNameStructures.Add( pns );
                    }
                }
            }

            return ir;
        }
        // Parser
        public static CPersonalNameStructure Parse( CGedcom gedcom, int nLevel )
        {
            CGedcomLine gedcomLine;

            // Temporary holders for class members. Saves constructing a class early.
            string sNamePersonal;
            string sNameTempSuffix="";
            string sNameType="";
            string sUsedName = "";
            CPersonalNamePieces pnp, personalNamePiece=null;
            ArrayList alNamePhoneticVariations = new ArrayList();
            ArrayList alNameRomanizedVariations = new ArrayList();

            CNamePhoneticVariation namePhoneticVariation;
            CNameRomanizedVariation nameRomanizedVariation;

            // There must be one of these, it defines the object.
            if ((gedcomLine = gedcom.GetLine(nLevel, "NAME")) == null)
            {
                // Not one of us
                return null;
            }
            sNamePersonal = gedcomLine.LineItem;

            // Some sw outputs name suffixes in the personal name, e.g. Fred /Bloggs/ Snr
            // Store the suffix, and let caller subsequently move the suffix to a proper class.
            int i = sNamePersonal.LastIndexOf('/') + 1;
            int l = sNamePersonal.Length;
            if (i != l)
            {
                int j = i;
                while ( j<l && (Char.IsWhiteSpace(sNamePersonal[j]) || sNamePersonal[j] == ','))
                {
                    ++j;
                }
                if (j < l)
                {
                    sNameTempSuffix = sNamePersonal.Substring(j);

                    personalNamePiece = new CPersonalNamePieces(gedcom);
                    personalNamePiece.m_sNamePieceSuffix = sNameTempSuffix;
                    sNamePersonal = sNamePersonal.Substring(0, i);
                }
            }
            gedcom.IncrementLineIndex(1);

            bool bParsingFinished;
            do
            {
                bParsingFinished = true;

                // Test for underscore items first so that parser doesn't skip them later
                if( (gedcomLine = gedcom.GetLine(nLevel+1, "_USED")) != null )
                {
                    sUsedName = gedcomLine.LineItem;
                    gedcom.IncrementLineIndex(1);
                    bParsingFinished = false;
                }
                else if( (gedcomLine = gedcom.GetLine(nLevel+1, "TYPE")) != null )
                {
                    sNameType = gedcomLine.LineItem;
                    gedcom.IncrementLineIndex(1);
                    bParsingFinished = false;
                }
                else if( (pnp = CPersonalNamePieces.Parse( gedcom, nLevel+1 ) ) != null )
                {
                    personalNamePiece = pnp;
                    bParsingFinished = false;
                }
                else if( (namePhoneticVariation = CNamePhoneticVariation.Parse( gedcom, nLevel+1 ) ) != null )
                {
                    alNamePhoneticVariations.Add( namePhoneticVariation );
                    bParsingFinished = false;
                }
                else if( (nameRomanizedVariation = CNameRomanizedVariation.Parse( gedcom, nLevel+1 ) ) != null )
                {
                    alNameRomanizedVariations.Add( nameRomanizedVariation );
                    bParsingFinished = false;
                }
                else if( ( gedcomLine = gedcom.GetLine()).Level > nLevel )
                {
                    LogFile.TheLogFile.WriteLine( LogFile.DT_GEDCOM, LogFile.EDebugLevel.Warning, "Unknown tag :" );
                    LogFile.TheLogFile.WriteLine( LogFile.DT_GEDCOM, LogFile.EDebugLevel.Warning, gedcomLine.ToString() );
                    gedcom.IncrementLineIndex(1);
                    bParsingFinished = false;
                }
            }
            while( !bParsingFinished );

            // Parsing went ok. Construct a new object and return it.
            CPersonalNameStructure pns = new CPersonalNameStructure( gedcom );
            pns.m_sNamePersonal = sNamePersonal;
            pns.m_sNameType = sNameType;
            pns.m_sUsedName = sUsedName;
            pns.m_personalNamePieces = personalNamePiece;
            pns.m_alNamePhoneticVariations = alNamePhoneticVariations;
            pns.m_alNameRomanizedVariations = alNameRomanizedVariations;
            return pns;
        }