// Extracts the data from the given event, and creates a CIEvent instance for it and adds it to the list of events.
        // Does specific processing if appropriate to the event.
        // linkToOtherParty is an href link to the other party concerned in the event. Typically this is for fr events such as engagement, marriage etc where
        // the other party would be the partner.
        private void ProcessEvent(CEventStructure es, string linkToOtherParty )
        {
            LogFile.TheLogFile.WriteLine(LogFile.DT_HTML, LogFile.EDebugLevel.Note, String.Format("ProcessEvent( {0}, {1} )", es.Type != null ? es.Type : "null", es.Value != null ? es.Value : "null"));

              if (es.Type == null)
              {
            return;
              }
              string utype = es.Type.ToUpper();
              string subtype = es.m_sSubtype;

              // Strip trailing _ that FTM seems sometimes to include
              while (subtype.Length > 0 && subtype.Substring(subtype.Length - 1, 1) == "_")
              {
            subtype = subtype.Substring(0, subtype.Length - 1);
              }

              // Useful holder vars
              CPGDate date;
              string place;
              string escaped_description = "";
              string address = "";
              string url = "";
              string cause = "";

              bool important = false;
              date = null;
              place = "";
              string place_word = MainForm.s_config.m_sPlaceWord;
              string alternative_place_word = "and"; // For want of anything better...
              string alternative_place = "";
              if (es.m_eventDetail != null)
              {
            date = es.m_eventDetail.m_dateValue;
            if (es.m_eventDetail.m_placeStructure != null)
            {
              place = es.m_eventDetail.m_placeStructure.m_sPlaceName;
            }
            alternative_place = es.m_eventDetail.m_sAlternativePlace;

            if (es.m_eventDetail.m_addressStructure != null)
            {
              address = es.m_eventDetail.m_addressStructure.ToString();
              url = es.m_eventDetail.m_addressStructure.GetUrl();
            }

            cause = es.m_eventDetail.m_sCauseOfEvent;
              }

              string sourceRefs = "";
              if (es.m_eventDetail != null && es.Type != "MARR" && es.Type != "TITL") // Marriage handled separately later.
              {
            sourceRefs = AddSources(ref m_alReferenceList, es.m_eventDetail.m_alSourceCitations);
              }

              bool bNeedValue = false;
              bool bOnlyIncludeIfNotePresent = false;
              bool bIncludeOccupation = false;

              // First occurrence of an event in GEDCOM is the "preferred" one, where in real life there can be only one of the event (e.g. BIRT)
              bool bTypeIsAOneOff = false;

              // Fix for Family Tree Maker 2008 which exports occupation as generic EVEN events.
              // It also puts occupation in PLAC field, but this is already accommodated later.
              if (es.Type == "EVEN" && subtype.ToLower() == "occupation")
              {
            es.Type = "OCCU";
              }

              switch (utype)
              {
            case "BIRT":
              if (es is CIndividualEventStructure)
              {
            bTypeIsAOneOff = true;
            if (m_qdateInferredBirthday != null)
            {
              // Throw away lesser qualified birthday inferences.
              if (m_qdateInferredBirthday.m_eqQualification > CPGQualifiedDate.EQualification.Birth) // ">" here means "further from the truth".
              {
                m_qdateInferredBirthday = null;
              }
            }
            if (m_qdateInferredBirthday == null) // Take first BIRT we come across. In GEDCOM this means it is the preferred event.
            {
              m_qdateInferredBirthday = new CPGQualifiedDate(date, CPGQualifiedDate.EQualification.Birth);
            }
            m_sBirthdaySourceRefs = sourceRefs;
              }
              escaped_description = "born";
              important = true;
              break;

            case "CHR":
              if (es is CIndividualEventStructure)
              {
            if (m_qdateInferredBirthday != null)
            {
              // Throw away lesser qualified birthday inferences.
              if (m_qdateInferredBirthday.m_eqQualification > CPGQualifiedDate.EQualification.Christening) // ">" here means "further from the truth".
              {
                m_qdateInferredBirthday = null;
              }
            }
            if (m_qdateInferredBirthday == null) // In the absence of a BIRT event this will have to do.
            {
              m_qdateInferredBirthday = new CPGQualifiedDate(date, CPGQualifiedDate.EQualification.Christening);
              m_sBirthdaySourceRefs = sourceRefs;
            }
              }
              escaped_description = "christened";
              break;

            case "BAPM":
              if (es is CIndividualEventStructure)
              {
            if (m_qdateInferredBirthday != null)
            {
              // Throw away lesser qualified birthday inferences.
              if (m_qdateInferredBirthday.m_eqQualification > CPGQualifiedDate.EQualification.Baptism) // ">" here means "further from the truth".
              {
                m_qdateInferredBirthday = null;
              }
            }
            if (m_qdateInferredBirthday == null) // In the absence of a BIRT event this will have to do.
            {
              m_qdateInferredBirthday = new CPGQualifiedDate(date, CPGQualifiedDate.EQualification.Baptism);
              m_sBirthdaySourceRefs = sourceRefs;
            }
              }
              escaped_description = "baptised";
              break;

            case "DEAT":
              bTypeIsAOneOff = true;
              if (es is CIndividualEventStructure)
              {
            if (m_qdateInferredDeathday != null)
            {
              // Throw away lesser qualified birthday inferences.
              if (m_qdateInferredDeathday.m_eqQualification > CPGQualifiedDate.EQualification.Death) // ">" here means "further from the truth".
              {
                m_qdateInferredDeathday = null;
              }
            }
            if (m_qdateInferredDeathday == null) // Take first DEAT we come across. In GEDCOM this means it is the preferred event.
            {
              m_qdateInferredDeathday = new CPGQualifiedDate(date, CPGQualifiedDate.EQualification.Death);
            }
            m_sDeathdaySourceRefs = sourceRefs;
              }
              escaped_description = "died";
              important = true;
              break;

            case "BURI":
              bTypeIsAOneOff = true;
              if (es is CIndividualEventStructure)
              {
            if (m_qdateInferredDeathday != null)
            {
              // Throw away lesser qualified birthday inferences.
              if (m_qdateInferredDeathday.m_eqQualification > CPGQualifiedDate.EQualification.Burial) // ">" here means "further from the truth".
              {
                m_qdateInferredDeathday = null;
              }
            }
            if (m_qdateInferredDeathday == null) // In the absence of a DEAT event this will have to do.
            {
              m_qdateInferredDeathday = new CPGQualifiedDate(date, CPGQualifiedDate.EQualification.Burial);
              m_sDeathdaySourceRefs = sourceRefs;
            }
              }
              escaped_description = "buried";
              break;

            case "CREM":
              bTypeIsAOneOff = true;
              if (es is CIndividualEventStructure)
              {
            if (m_qdateInferredDeathday != null)
            {
              // Throw away lesser qualified birthday inferences.
              if (m_qdateInferredDeathday.m_eqQualification > CPGQualifiedDate.EQualification.Cremation) // ">" here means "further from the truth".
              {
                m_qdateInferredDeathday = null;
              }
            }
            if (m_qdateInferredDeathday == null) // In the absence of a DEAT event this will have to do.
            {
              m_qdateInferredDeathday = new CPGQualifiedDate(date, CPGQualifiedDate.EQualification.Cremation);
              m_sDeathdaySourceRefs = sourceRefs;
            }
              }
              escaped_description = "cremated";
              break;

            case "ADOP":
              escaped_description = "adopted";
              if (es.m_eventDetail != null)
              {
            CFamilyRecord adopFam = m_gedcom.GetFamilyRecord(es.m_eventDetail.m_xrefFam);
            CIndividualRecord adopHusb = null;
            CIndividualRecord adopWife = null;
            if (adopFam != null && es.m_eventDetail != null)
            {
              if (es.m_eventDetail.m_bAdoptedByHusband)
              {
                adopHusb = m_gedcom.GetIndividualRecord(adopFam.m_xrefHusband);
              }

              if (es.m_eventDetail.m_bAdoptedByWife)
              {
                adopWife = m_gedcom.GetIndividualRecord(adopFam.m_xrefWife);
              }

              if (adopHusb != null || adopWife != null)
              {
                escaped_description += " by ";
                if (adopHusb != null && adopHusb.Visibility() != CIndividualRecord.EVisibility.Invisible)
                {
                  escaped_description += MakeLink(adopHusb);
                  if (adopWife != null)
                  {
                    escaped_description += " and ";
                  }
                }
                if (adopWife != null && adopWife.Visibility() != CIndividualRecord.EVisibility.Invisible)
                {
                  escaped_description += MakeLink(adopWife);
                }
              }
            }
              }

              break;

            case "BARM":
              escaped_description = "bar mitzvah";
              break;

            case "BASM":
              escaped_description = "bat mitzvah";
              break;

            case "BLES":
              escaped_description = "blessing";
              break;

            case "CHRA":
              escaped_description = "christened (as adult)";
              break;

            case "CONF":
              escaped_description = "confirmed";
              break;

            case "FCOM":
              escaped_description = "first communion";
              break;

            case "ORDN":
              escaped_description = "ordained";
              break;

            case "NATU":
              escaped_description = "naturalized";
              break;

            case "EMIG":
              escaped_description = "emigrated";
              place_word = "from";
              alternative_place_word = "to";
              break;

            case "IMMI":
              escaped_description = "immigrated";
              place_word = "to";
              alternative_place_word = "from";
              break;
            /*  handled as fr event below
                case "CENS":
                  escaped_description = "recorded in census";
                  break;*/

            case "PROB":
              escaped_description = "probate";
              break;

            case "WILL":
              escaped_description = "wrote will";
              break;

            case "GRAD":
              escaped_description = "graduated";
              break;

            case "RETI":
              escaped_description = "retired";
              break;

            case "EVEN":
              if (subtype != null && subtype != "")
              {
            escaped_description = EscapeHTML(subtype, false);
              }
              else
              {
            escaped_description = "other event";
              }
              if (es.Value != null && es.Value != "")
              {
              escaped_description += ": " + es.Value;
              }
              break;

            case "CAST":
              escaped_description = "caste";
              if (es.Value != null && es.Value != "")
            escaped_description = String.Concat(escaped_description, " ", EscapeHTML(es.Value, false));
              else
            bNeedValue = true;
              break;

            case "DSCR":
              escaped_description = "physical description";
              if (es.Value != null && es.Value != "")
            escaped_description = String.Concat(escaped_description, " ", EscapeHTML(es.Value, false));
              else
            bNeedValue = true;
              break;

            case "EDUC":
              escaped_description = "educated";
              if (es.Value != null && es.Value != "")
            escaped_description = String.Concat(escaped_description, " ", EscapeHTML(es.Value, false));
              else
            bNeedValue = true;
              break;

            case "IDNO":
              escaped_description = "ID number";
              if (es.Value != null && es.Value != "")
            escaped_description = String.Concat(escaped_description, " ", EscapeHTML(es.Value, false));
              else
            bNeedValue = true;
              break;

            case "NATI":
              escaped_description = "nationality";
              if (es.Value != null && es.Value != "")
            escaped_description = String.Concat(escaped_description, " ", EscapeHTML(es.Value, false));
              else
            bNeedValue = true;
              break;

            case "NCHI":
              bTypeIsAOneOff = true;
              escaped_description = "number of children";
              if (es.Value != null && es.Value != "")
            escaped_description = String.Concat(escaped_description, " ", EscapeHTML(es.Value, false));
              else
            bNeedValue = true;
              break;

            case "NMR":
              bTypeIsAOneOff = true;
              escaped_description = "number of marriages";
              if (es.Value != null && es.Value != "")
            escaped_description = String.Concat(escaped_description, " ", EscapeHTML(es.Value, false));
              else
            bNeedValue = true;

              break;

            case "OCCU":
              escaped_description = "occupation";
              if (es.Value != null && es.Value != "")
              {
            COccupationCounter oc = new COccupationCounter(EscapeHTML(es.Value, false) + sourceRefs, date);
            m_alOccupations.Add(oc);
            escaped_description = String.Concat(escaped_description, " ", EscapeHTML(es.Value, false));
            bIncludeOccupation = true;
              }
              else
            bNeedValue = true;
              break;

            case "PROP":
              escaped_description = "property";
              if (es.Value != null && es.Value != "")
            escaped_description = String.Concat(escaped_description, " ", EscapeHTML(es.Value, false));
              else
            bNeedValue = true;
              break;

            case "RELI":
              escaped_description = "religion";
              if (es.Value != null && es.Value != "")
            escaped_description = String.Concat(escaped_description, " ", EscapeHTML(es.Value, false));
              else
            bNeedValue = true;
              break;

            case "RESI":
              escaped_description = "resident";
              if (es.Value != null && es.Value != "")
            escaped_description = String.Concat(escaped_description, " ", EscapeHTML(es.Value, false));
              else
            bNeedValue = false; // Special case, we need the "at" word left in for this.
              break;

            case "SSN":
              escaped_description = "Social Security number";
              if (es.Value != null && es.Value != "")
            escaped_description = String.Concat(escaped_description, " ", EscapeHTML(es.Value, false));
              else
            bNeedValue = true;
              break;

            case "TITL":
              /* This is handled as a special case outside of event processing*/
              place = ""; // Clear place to avoid creating spurious event entry
              break;

            case "FACT":
              escaped_description = "other fact";
              if (es.Value != null && es.Value != "")
            escaped_description = String.Concat(escaped_description, " ", EscapeHTML(es.Value, false));
              else
            bNeedValue = true;
              break;

            case "_NMR": // _NMR Brother's Keeper
              bTypeIsAOneOff = true;
              escaped_description = "never married";
              if (es.Value != null && es.Value != "")
            escaped_description = String.Concat(escaped_description, " ", EscapeHTML(es.Value, false));
              else
            bNeedValue = true;
              break;

            case "_AKA": // _AKA Brother's Keeper
            case "_AKAN": // _AKAN Brother's Keeper
              escaped_description = "also known as";
              if (es.Value != null && es.Value != "")
            escaped_description = String.Concat(escaped_description, " ", EscapeHTML(es.Value, false));
              else
            bNeedValue = true;
              break;

            // Now the fr events:
            case "ANUL":
              escaped_description = "annulment of marriage";
              if (es.Value != null && es.Value != "")
            escaped_description = String.Concat(escaped_description, " ", EscapeHTML(es.Value, false));
              else
            bNeedValue = true;
              if (linkToOtherParty != null && linkToOtherParty != "")
              {
            escaped_description = String.Concat(escaped_description, " to ", linkToOtherParty);
              }

              break;

            case "CENS":
              escaped_description = "recorded in census";
              break;

            case "DIV":
              if (es.Value != null && (es.Value == "N" || es.Value == "n"))
              {
            place = ""; // Clear place to prevent this event being shown
              }
              else
              {
            escaped_description = "divorced";
            if (linkToOtherParty != null && linkToOtherParty != "")
            {
              escaped_description = String.Concat(escaped_description, " from ", linkToOtherParty);
            }
              }

              break;

            case "DIVF":
              escaped_description = "filing of divorce";
              if (linkToOtherParty != null && linkToOtherParty != "")
              {
            escaped_description = String.Concat(escaped_description, " from ", linkToOtherParty);
              }
              break;

            case "ENGA":
              escaped_description = "engagement";
              if (linkToOtherParty != null && linkToOtherParty != "")
              {
            escaped_description = String.Concat(escaped_description, " to ", linkToOtherParty);
              }
              break;

            case "MARB":
              escaped_description = "publication of banns of marriage";
              if (linkToOtherParty != null && linkToOtherParty != "")
              {
            escaped_description = String.Concat(escaped_description, " to ", linkToOtherParty);
              }

              break;

            case "MARC":
              escaped_description = "contract of marriage";
              if (linkToOtherParty != null && linkToOtherParty != "")
              {
            escaped_description = String.Concat(escaped_description, " to ", linkToOtherParty);
              }

              break;

            case "MARR":
              /* This is handled as a special case outside of event processing*/
              place = ""; // Clear place to avoid creating spurious event entry
              break;

            case "MARL":
              escaped_description = "licence obtained for marriage";
              if (linkToOtherParty != null && linkToOtherParty != "")
              {
            escaped_description = String.Concat(escaped_description, " to ", linkToOtherParty);
              }

              break;

            case "MARS":
              escaped_description = "settlement of marriage";
              if (linkToOtherParty != null && linkToOtherParty != "")
              {
            escaped_description = String.Concat(escaped_description, " to ", linkToOtherParty);
              }

              break;

            case "GEDMILL_ADOPTION_OF_CHILD":
              if (es.m_eventDetail != null)
              {
            CIndividualRecord adoptedChild = m_gedcom.GetIndividualRecord(es.m_eventDetail.m_xrefAdoptedChild);
            if (adoptedChild != null)
            {
              escaped_description = String.Concat("adopted ", MakeLink(adoptedChild));
            }
              }
              break;

            default:
              escaped_description = "unknown event";
              if (es.Value != null && es.Value != "")
            escaped_description = String.Concat(escaped_description, " ", EscapeHTML(es.Value, false));
              else
            bNeedValue = true;
              break;
              }

              if (MainForm.s_config.m_bCapitaliseEventDescriptions)
              {
            Capitalise(ref escaped_description);
              }

              if (place != "")
              {
            // It seems some earlier GEDCOM has PLAC value filled with the event value, and the event value blank. Accomodate this:
            if ((es.Value == null || es.Value == "") && bNeedValue)
            {
              escaped_description += " " + EscapeHTML(place, false);
              if (utype == "OCCU")
              {
            COccupationCounter oc = new COccupationCounter(place,date);
            m_alOccupations.Add(oc);
              }
              place = "";
              bIncludeOccupation = true; // Needed to include occupation event, (without date or place), in page.
            }
            else
            {
              escaped_description += String.Concat(" ", place_word, " ", EscapeHTML(place, false));
              if (alternative_place != null && alternative_place.Length > 0)
              {
            escaped_description += String.Concat(" ", alternative_place_word, " ", EscapeHTML(alternative_place, false));
              }
            }
              }

              if (address != "")
              {
            if (escaped_description.Length > 0)
            {
              escaped_description += " (" + EscapeHTML(address, false) + ")";
            }
            else
            {
              escaped_description = EscapeHTML(address, false);
            }
              }

              if (url != "")
              {
            if (escaped_description.Length > 0)
            {
              escaped_description += " (<a href=\"" + (url) + "\">" + (url) + "</a>)";
            }
            else
            {
              escaped_description = "<a href=\"" + (url) + "\">" + (url) + "</a>";
            }
              }

              string overview = "";
              if (es.m_eventDetail != null && es.m_eventDetail.m_sOverview != null && es.m_eventDetail.m_sOverview != "")
              {
            overview = es.m_eventDetail.m_sOverview;
              }

              if (escaped_description == "")
              {
            return; // In the case of MARR and TITL and DIV N we don't want to add anything here.
              }

              escaped_description += ".";
              escaped_description += sourceRefs;

              string eventNote = "";

              if (cause != "")
              {
            cause = EscapeHTML(cause, false);
            if (MainForm.s_config.m_bCapitaliseEventDescriptions)
            {
              Capitalise(ref cause);
            }
            if (eventNote.Length > 0)
            {
              eventNote += "\n";
            }
            if (MainForm.s_config.m_bObfuscateEmails)
            {
              eventNote += ObfuscateEmail(cause);
            }
            else
            {
              eventNote += cause;
            }
              }

              if (es.m_eventDetail != null)
              {
            if (es.m_eventDetail.m_alNoteStructures != null)
            {
              foreach (CNoteStructure ns in es.m_eventDetail.m_alNoteStructures)
              {
            if (ns.Text != null && ns.Text.Length > 0)
            {
              if (eventNote != "")
              {
                eventNote += "\n";
              }
              if (MainForm.s_config.m_bObfuscateEmails)
              {
                eventNote += ObfuscateEmail(ns.Text);
              }
              else
              {
                eventNote += ns.Text;
              }
            }
              }
            }
              }

              CIEvent iEvent = null;

              if (!bOnlyIncludeIfNotePresent || eventNote != "")
              {
            if (date != null)
            {
              iEvent = new CIEvent(date, utype, escaped_description, overview, eventNote, important, MainForm.s_config.m_bCapitaliseEventDescriptions);
              m_alEventList.Add(iEvent);
            }
            // else its an attribute.
            else
            {
              // Don't include plain "Died" and nothing else. Roots Magic seems to use this just to signify that person died. But it appears on every single page and looks silly.
              // GSP Family Tree puts lots of blank tags (OCCU, CHR, SEX, NOTE, etc.etc). Don't display those without meaning
              // Note CHR is contentious, as other s/w may use a CHR with no other info to mean that they were christened. GSP it appears puts a CHR for everyone?
              if ((utype != "DEAT" && utype != "BIRT" && utype != "CHR" && utype != "OCCU") || place != "" || eventNote != "" || bIncludeOccupation)
              {
            iEvent = new CIEvent(null, utype, escaped_description, overview, eventNote, important, MainForm.s_config.m_bCapitaliseEventDescriptions);
            m_alAttributeList.Add(iEvent);
              }
            }
              }

              if (iEvent != null && bTypeIsAOneOff)
              {
            if (m_htFirstFoundEvent.ContainsKey(utype))
            {
              // We have multiple occurences of this event. Mark the one we saw first as 'preferred'.
              CIEvent firstEvent = (CIEvent)m_htFirstFoundEvent[utype];
              if (firstEvent != null)
              {
            firstEvent.Preference = CIEvent.EPreference.First;
            iEvent.Preference = CIEvent.EPreference.Subsequent;
              }
            }
            else
            {
              m_htFirstFoundEvent[utype] = iEvent;
            }
              }
        }
 // Constructor
 public CCreatorRecordIndividual( CGedcom gedcom, IProgressCallback progress, string sW3cfile, CIndividualRecord ir, CCreatorIndexIndividuals indiIndexCreator, CPaintbox paintbox )
     : base(gedcom, progress, sW3cfile)
 {
     m_ir = ir;
       m_indiIndexCreator = indiIndexCreator;
       m_paintbox = paintbox;
       m_htFirstFoundEvent = new Hashtable();
       m_sBirthdaySourceRefs = "";
       m_sDeathdaySourceRefs = "";
       m_sNameTitle = "";
       m_bUnknownName = false;
       m_sName = m_ir.Name;
       m_sNameSuffix = m_ir.NameSuffix;
       m_sFirstName = "";
       m_sSurname = "";
       m_sOccupation = "";
       m_bConcealed = m_ir.Visibility() == CIndividualRecord.EVisibility.Restricted;
       m_alEventList = new ArrayList();
       m_alAttributeList = new ArrayList();
       m_alReferenceList = new ArrayList();
       m_alOccupations = new ArrayList();
       m_sPreviousChildLink = "";
       m_sNextChildLink = "";
       m_alOtherNames = new ArrayList();
       m_qdateInferredBirthday = null;
       m_dateActualBirthday = null;
       m_qdateInferredDeathday = null;
       m_dateActualDeathday = null;
       m_alParents = new ArrayList();
 }