context: /* 
   * Copyright 2009 Alexander Curtis <alex@logicmill.com>
   * This file is part of GEDmill - A family history website creator
   * 
   * GEDmill is free software: you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation, either version 3 of the License, or
   * (at your option) any later version.
   *
   * GEDmill is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with GEDmill.  If not, see <http://www.gnu.org/licenses/>.
   */
  
  using System;
  using System.Collections.Generic;
  using System.Drawing;
  using System.Drawing.Imaging;
  using System.IO;
  using GDModel;
  using GEDmill.MiniTree;
  using GEDmill.Model;
  using GKCore.Logging;
  using GKCore.Types;
  using GDModel.Providers.GEDCOM;
  
  namespace GEDmill.HTML
  {
      public class CreatorRecordIndividual : CreatorRecord
      {
          private static readonly ILogger fLogger = LogManager.GetLogger(CConfig.LOG_FILE, CConfig.LOG_LEVEL, typeof(CreatorRecordIndividual).Name);
  
          // The individual record that we are creating the page for.
          private GDMIndividualRecord fIndiRec;
  
          // Indicates that this individual should have most of the record data excluded from the website for privacy.
          private bool fConcealed;
  
          // List of the events in the individual's life history.
          private List<Event> fEventList;
  
          // List of other facts known about the individual.
          private List<Event> fAttributeList;
  
          // List of sources referenced by this page.
          private List<GDMSourceCitation> fReferenceList;
  
          // List of occupations known for the individual
          private List<OccupationCounter> fOccupations;
  
          // An HTML link to the previous sibling in this fr.
          private string fPreviousChildLink;
  
          // An HTML link to the next sibling in this fr.
          private string fNextChildLink;
  
          // List of aliases and other names for the individual
          private List<NameAndSource> fOtherNames;
  
          // A date inferred for the individual's birthday, with associated quality so that it can be rejected in favour of more certain information.
          private QualifiedDate fInferredBirthday;
  
          // The individual's date of birth
          private GDMDateValue fActualBirthday;
  
          // A date inferred for the individual's date of death, with associated quality so that it can be rejected in favour of more certain information.
          private QualifiedDate fInferredDeathday;
  
          // The individual's date of death
          private GDMDateValue fActualDeathday;
  
          // Records first occurrence of one-off events, so that they may be marked as "preferred"
          private Dictionary<string, Event> fFirstFoundEvent;
  
          // The sources giving the individual's birth date
          private string fBirthdaySourceRefs;
  
          // The sources giving the individual's death date
          private string fDeathdaySourceRefs;
  
          // The individual's title (GEDCOM:TITL)
          private string fNameTitle;
  
          // Indicates that a name for this individual is not available
          private bool fUnknownName;
  
          // The individual's main name
          private string fName;
  
          // The suffix on the individual's name (e.g. "Snr")
          private string fNameSuffix;
  
          // The individual's first name
          private string fFirstName;
  
          // The individual's surname
          private string fSurname;
  
          // The individual's fully expanded name
          private string fFullName;
  
          // The individual's nickname
          private string fNickName;
  
          // The individual's commonly used name
          private string fUsedName;
  
          // The sources giving the individual's name
          private string fNameSources;
  
          // The individual's occupation, for display at the page head
          private string fOccupation;
  
          // All the frParents of the individual. May be more than one pair if individual was associated with more than one fr.
          private List<HusbandAndWife> fParents;
  
          // A reference to the index creator, so that individual pages can be added to the index as they are created.
          private CreatorIndexIndividuals fIndiIndexCreator;
  
          // The paintbox with which to draw the mini tree.
          private Paintbox fPaintbox;
  
  
          public CreatorRecordIndividual(GDMTree tree, IProgressCallback progress, string w3cfile, GDMIndividualRecord ir, CreatorIndexIndividuals indiIndexCreator, Paintbox paintbox) : base(tree, progress, w3cfile)
          {
              fIndiRec = ir;
              fIndiIndexCreator = indiIndexCreator;
              fPaintbox = paintbox;
              fFirstFoundEvent = new Dictionary<string, Event>();
              fBirthdaySourceRefs = "";
              fDeathdaySourceRefs = "";
              fNameTitle = "";
              fUnknownName = false;
              fName = fIndiRec.GetPrimaryFullName();
              fNameSuffix = ""/*fIndiRec.NameSuffix*/; // TODO
              fFirstName = "";
              fSurname = "";
              fOccupation = "";
              fConcealed = !fIndiRec.GetVisibility();
              fEventList = new List<Event>();
              fAttributeList = new List<Event>();
              fReferenceList = new List<GDMSourceCitation>();
              fOccupations = new List<OccupationCounter>();
              fPreviousChildLink = "";
              fNextChildLink = "";
              fOtherNames = new List<NameAndSource>();
              fInferredBirthday = null;
              fActualBirthday = null;
              fInferredDeathday = null;
              fActualDeathday = null;
              fParents = new List<HusbandAndWife>();
          }
  
          // The main method that causes the page to be created.
          public bool Create(Stats stats)
          {
              fLogger.WriteInfo("CCreatorRecordIndividual.Create()");
  
              if (fIndiRec == null) {
                  return false;
              }
  
              if (!fIndiRec.GetVisibility()) {
                  return false;
              }
  
              // Collect together multimedia links
              if (CConfig.Instance.AllowMultimedia && !fConcealed) {
                  AddMultimedia(fIndiRec.MultimediaLinks, string.Concat(fIndiRec.XRef, "mm"), string.Concat(fIndiRec.XRef, "mo"), CConfig.Instance.MaxImageWidth, CConfig.Instance.MaxImageHeight, stats);
              }
  
              AddEvents();
  
              RemoveLoneOccupation();
  
              var lifeDatesX = fIndiRec.GetLifeDates();
              fActualBirthday = (lifeDatesX.BirthEvent == null) ? null : lifeDatesX.BirthEvent.Date;
              fActualDeathday = (lifeDatesX.DeathEvent == null) ? null : lifeDatesX.DeathEvent.Date;
  
              ConstructName();
  
              GDMDateValue age30;
              if (fInferredBirthday != null) {
                  age30 = new GDMDateValue(null);
                  age30.Assign(fInferredBirthday.Date);
              } else {
                  age30 = new GDMDateValue(null);
                  age30.SetDateTime(DateTime.Now);
              }
              try {
                  ((GDMDate)age30.Value).Year += (short)CConfig.Instance.AgeForOccupation;
              } catch { }
  
              // We should have birthday and deathday by now, so find longest occupation
              if (!fConcealed) {
                  fOccupation = BestOccupation(fOccupations, age30, (fInferredBirthday != null) ? fInferredBirthday.Date : null, (fInferredDeathday != null) ? fInferredDeathday.Date : null); // Picks occupation with longest time span
              }
  
              // Go through all families this person was a irSubject to
              if (!fConcealed) {
                  foreach (GDMSpouseToFamilyLink sfl in fIndiRec.SpouseToFamilyLinks) {
                      GDMFamilyRecord fr = (sfl.Value as GDMFamilyRecord);
                      if (fr != null) {
                          // Find the irSubject's name
                          GDMIndividualRecord spouse = null;
                          string spouseLink = "";
                          spouse = fr.GetSpouseBy(fIndiRec);
                          if (spouse != null && spouse.GetVisibility()) {
                              spouseLink = MakeLink(spouse);
                          }
  
                          // Add fr events as events connected to this individual
                          foreach (GDMFamilyEvent fes in fr.Events) {
                              ProcessEvent(fes, spouseLink);
                          }
  
                          AddChildrensEvents(fr);
  
                          AddMarriage(spouse, spouseLink, fr);
                      }
                  }
                  AddParentsAndSiblings();
              }
  
              string birthyear = "";
              string deathyear = "";
              if (!fConcealed) {
                  if (fInferredBirthday != null && fInferredBirthday.Date != null) {
                      birthyear = fInferredBirthday.Date.GetDisplayStringExt(DateFormat.dfYYYY, false, false);
                  }
                  if (fInferredDeathday != null && fInferredDeathday.Date != null) {
                      deathyear = fInferredDeathday.Date.GetDisplayStringExt(DateFormat.dfYYYY, false, false);
                  }
              }
  
              string title = fName; //"Fred Bloggs 1871-1921"
              string lifeDates = "";
              if (!fConcealed) {
                  if (birthyear != "" || deathyear != "") {
                      lifeDates = string.Concat(birthyear, "-", deathyear);
                      title = string.Concat(fName, " ", lifeDates);
                  }
              }
  
              AddIndividualIndexEntry(lifeDates);
  
              OutputHTML(title);
  
              return true;
          }
  
          // Adds the marriage associated with the fr record to the list of events. Also adds irSubject death if within this person's lifetime.
          private void AddMarriage(GDMIndividualRecord spouse, string spouseLink, GDMFamilyRecord fr)
          {
              // Find wedding date
              if (spouse != null) {
                  string sourceRefs = AddSpouseDeath(spouse, spouseLink);
  
                  GDMDateValue marriageDate;
                  string marriageNote;
                  string marriagePlace;
                  sourceRefs = AddMarriageEvent(fr, sourceRefs, out marriageDate, out marriageNote, out marriagePlace);
  
                  marriageNote = BuildMaritalStatusNote(fr, marriageNote);
  
                  // Add fr record notes to marriage event
                  foreach (GDMNotes ns in fr.Notes) {
                      if (marriageNote != "") {
                          marriageNote += "\n";
                      }
                      if (CConfig.Instance.ObfuscateEmails) {
                          marriageNote += ObfuscateEmail(ns.Lines.Text);
                      } else {
                          marriageNote += ns.Lines.Text;
                      }
                  }
  
                  string marriedString = "married ";
                  if (fr.Status == GDMMarriageStatus.MarrNotRegistered) {
                      marriedString = "partner of ";
                  }
  
                  if (marriageDate != null) {
                      Event iEvent = new Event(marriageDate, "_MARRIAGE", string.Concat(marriedString, spouseLink, marriagePlace, ".", sourceRefs), "", marriageNote, true, CConfig.Instance.CapitaliseEventDescriptions);
                      fEventList.Add(iEvent);
                  } else {
                      Event iEvent = new Event(marriageDate, "_MARRIAGE", string.Concat(marriedString, spouseLink, marriagePlace, ".", sourceRefs), "", marriageNote, true, CConfig.Instance.CapitaliseEventDescriptions);
                      // Marriages go at the front of the list so that they appear first in "Other facts"
                      fAttributeList.Insert(0, iEvent);
                  }
              }
          }
  
          // Goes through all families this person was a irSibling in and finds their frParents and siblings.
          private void AddParentsAndSiblings()
          {
              // Set a limit for date comparisons
              DateTime dtNow = DateTime.Now;
  
              // Go through all families this person was a irSibling in
              foreach (GDMChildToFamilyLink cfl in fIndiRec.ChildToFamilyLinks) {
                  GDMFamilyRecord fr = (cfl.Value as GDMFamilyRecord);
                  if (fr != null) {
                      GDMIndividualRecord husband = fr.Husband.Individual;
                      GDMIndividualRecord wife = fr.Wife.Individual;
  
                      if (husband != null || wife != null) {
                          fParents.Add(new HusbandAndWife(husband, wife));
                      }
  
                      // Get all the children in order, to find previous and next irSibling
                      GDMDateValue testBirthday = (fInferredBirthday != null) ? fInferredBirthday.Date : null;
  
                      if (testBirthday == null) {
                          testBirthday = new GDMDateValue(null);
                          testBirthday.SetDateTime(dtNow);
                      }
  
                      int previousDifference = -100 * 365; // 100 years should be enough
                      int nextDifference = 100 * 365;
  
                      foreach (var ch in fr.Children) {
                          if (ch.XRef == fIndiRec.XRef)
                              continue;
  
                          GDMIndividualRecord child = ch.Value as GDMIndividualRecord;
                          if (child != null) {
                              if (!child.GetVisibility())
                                  continue;
  
                              GDMCustomEvent childBirthday = child.FindEvent("BIRT");
                              if (childBirthday == null) {
                                  childBirthday = child.FindEvent("CHR");
                              }
                              if (childBirthday == null) {
                                  childBirthday = child.FindEvent("BAPM");
                              }
  
                              GDMDateValue childBirthdate = null;
                              if (childBirthday != null)
                                  childBirthdate = childBirthday.Date;
                              if (childBirthdate == null) {
                                  childBirthdate = new GDMDateValue(null);
                                  childBirthdate.SetDateTime(dtNow);
                              }
  
                              int difference = Extensions.GetEventsYearsDiff(testBirthday, childBirthdate);
                              if (difference < 0) {
                                  if (difference > previousDifference) {
                                      previousDifference = difference;
                                      fPreviousChildLink = MakeLink(child, "previous child");
                                  }
                              } else if (difference > 0) {
                                  if (difference < nextDifference) {
                                      nextDifference = difference;
                                      fNextChildLink = MakeLink(child, "next child");
                                  }
                              } else {
                                  fNextChildLink = MakeLink(child, "next child");
                              }
                          }
                      }
                  }
              }
          }
  
          // Adds this individual page to the index of pages.
          private void AddIndividualIndexEntry(string lifeDates)
          {
  
gt:             string relativeFilename = GetIndividualHTMLFilename(fIndiRec);