# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2007-2009  Douglas S. Blank <doug.blank@gmail.com>
# Copyright (C) 2009       Gary Burton
#
# This program 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 2 of the License, or
# (at your option) any later version.
#
# This program 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 this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

# $Id: PedigreeGramplet.py 15216 2010-04-18 13:44:33Z dsblank $

#------------------------------------------------------------------------
#
# Python modules
#
#------------------------------------------------------------------------
import cgi
import locale

#------------------------------------------------------------------------
#
# GRAMPS modules
#
#------------------------------------------------------------------------
from gen.plug import Gramplet
from gen.ggettext import sgettext as _
from gen.ggettext import ngettext
from gen.display.name import displayer as name_displayer
import DateHandler
import gen
from gen.utils import get_birth_or_fallback, get_death_or_fallback

#------------------------------------------------------------------------
#
# Gramplet class
#
#------------------------------------------------------------------------
class PedigreeGramplet(Gramplet):
    def init(self):
        self.set_text(_("No Family Tree loaded."))
        self.set_tooltip(_("Move mouse over links for options"))
        self.set_use_markup(True)
        self.max_generations = 100
        self.show_dates = 1
        self.box_mode = "UTF"

    def build_options(self):
        from gen.plug.menu import NumberOption, BooleanOption, EnumeratedListOption
        self.add_option(NumberOption(_("Max generations"), 
                                     self.max_generations, 1, 100))
        self.add_option(BooleanOption(_("Show dates"), bool(self.show_dates)))
        elist = EnumeratedListOption(_("Line type"), self.box_mode)
        elist.add_item("UTF", "UTF")
        elist.add_item("ASCII", "ASCII")
        self.add_option(elist)

    def save_options(self):
        self.max_generations = int(self.get_option(_("Max generations")).get_value())
        self.show_dates = int(self.get_option(_("Show dates")).get_value())
        self.box_mode = self.get_option(_("Line type")).get_value()

    def on_load(self):
        if len(self.gui.data) == 3:
            self.max_generations = int(self.gui.data[0])
            self.show_dates = int(self.gui.data[1])
            self.box_mode = self.gui.data[2] # "ASCII" or "UTF"

    def save_update_options(self, widget=None):
        self.max_generations = int(self.get_option(_("Max generations")).get_value())
        self.show_dates = int(self.get_option(_("Show dates")).get_value())
        self.box_mode = self.get_option(_("Line type")).get_value()
        self.gui.data = [self.max_generations, self.show_dates, self.box_mode]
        self.update()

    def db_changed(self):
        """
        If a person or family changes, the ancestors of active person might have
        changed.
        """
        self.dbstate.db.connect('person-add', self.update)
        self.dbstate.db.connect('person-delete', self.update)
        self.dbstate.db.connect('family-add', self.update)
        self.dbstate.db.connect('family-delete', self.update)
        self.dbstate.db.connect('person-rebuild', self.update)
        self.dbstate.db.connect('family-rebuild', self.update)

    def active_changed(self, handle):
        self.update()

    def get_boxes(self, generation, what):
        retval = u""
        if self.box_mode == "UTF":
            space = u"  "
        elif self.box_mode == "ASCII":
            space = u"    "
        space_len = len(space) + 2
        for i in range(generation+1):
            if self._boxes[i]:
                retval += space + u"|"
            else:
                retval += space + u" "
        if retval[-1] == u' ':
            if what == 'sf':
                retval = retval[:-space_len] + u"/"
            elif what == 'sm':
                retval = retval[:-space_len] + u"\\"
        elif retval.endswith(u"|" + space + u"|"):
            retval = retval[:-space_len] + u"+"
        if self.box_mode == "UTF":
            retval += u"-"
            retval = retval.replace(u"\\", u"\u2514")
            retval = retval.replace(u"-",  u"\u2500")
            retval = retval.replace(u"|",  u"\u2502")
            retval = retval.replace(u"/",  u"\u250c")
        elif self.box_mode == "ASCII":
            retval += u"--"
        return retval

    def set_box(self, pos, value):
        self._boxes[pos] = value

    def process_person(self, handle, generation, what):
        if generation > self.max_generations:
            return
        person = self.dbstate.db.get_person_from_handle(handle)
        family_list = person.get_parent_family_handle_list()
        if what == "f":
            if len(family_list) > 0:
                family = self.dbstate.db.get_family_from_handle(family_list[0])
                father = family.get_father_handle()
                mother = family.get_mother_handle()
                if father:
                    self.process_person(father, generation + 1, "f")
                    self.set_box(generation, 1)
                    self.process_person(father, generation + 1, "sf")
                    self.process_person(father, generation + 1, "m")
                elif mother:
                    self.set_box(generation, 1)
        elif what[0] == "s":
            boxes = self.get_boxes(generation, what)
            if what[-1] == 'f':
                if self.box_mode == "UTF":
                    boxes = boxes.replace("+", u"\u250c")
                else:
                    boxes = boxes.replace("+", u"/")
            else:
                if self.box_mode == "UTF":
                    boxes = boxes.replace("+", u"\u2514")
                else:
                    boxes = boxes.replace("+", u"\\")
            self.append_text(boxes)
            self.link(name_displayer.display_name(person.get_primary_name()),
                      'Person', person.handle, 
                      tooltip=_("Click to make active\n") + \
                          _("Right-click to edit"))
            if self.show_dates:
                self.append_text(" ")
                self.render_text(self.info_string(person))
            self.append_text("\n")
            if generation not in self._generations:
                self._generations[generation] = []
            self._generations[generation].append(handle)
        elif what == "a":
            if self.box_mode == "UTF":
                self.append_text(u"o" + (u"\u2500" * 3))
            elif self.box_mode == "ASCII":
                self.append_text(u"o---")
            self.append_text("%s  " % name_displayer.display_name(person.get_primary_name()))
            if self.show_dates:
                self.render_text(self.info_string(person))
            self.append_text("\n")
            if generation not in self._generations:
                self._generations[generation] = []
            self._generations[generation].append(handle)
        elif what == "m":
            if len(family_list) > 0:
                family = self.dbstate.db.get_family_from_handle(family_list[0])
                mother = family.get_mother_handle()
                if mother:
                    self.process_person(mother, generation + 1, "f")
                    self.process_person(mother, generation + 1, "sm")
                    self.set_box(generation, 0)
                    self.process_person(mother, generation + 1, "m")
            self.set_box(generation, 0) # regardless, turn off line if on

    def info_string(self, person):
        birth = get_birth_or_fallback(self.dbstate.db, person)
        if birth and birth.get_type() != gen.lib.EventType.BIRTH:
            sdate = DateHandler.get_date(birth)
            if sdate:
                bdate  = "<i>%s</i>" % cgi.escape(sdate)
            else:
                bdate = ""
        elif birth:
            bdate  = cgi.escape(DateHandler.get_date(birth))
        else:
            bdate = ""

        death = get_death_or_fallback(self.dbstate.db, person)
        if death and death.get_type() != gen.lib.EventType.DEATH:
            sdate = DateHandler.get_date(death)
            if sdate:
                ddate  = "<i>%s</i>" % cgi.escape(sdate)
            else:
                ddate = ""
        elif death:
            ddate  = cgi.escape(DateHandler.get_date(death))
        else:
            ddate = ""

        if bdate and ddate:
            value = _("(b. %(birthdate)s, d. %(deathdate)s)") % {
                'birthdate' : bdate, 
                'deathdate' : ddate
                }
        elif bdate:
            value = _("(b. %s)") % (bdate)
        elif ddate:
            value = _("(d. %s)") % (ddate)
        else:
            value = ""
        return value

    def main(self): # return false finishes
        """
        Generator which will be run in the background.
        """
        self._boxes = [0] * (self.max_generations + 1)
        self._generations = {}
        self.gui.buffer.set_text("")
        active_handle = self.get_active('Person')
        active_person = self.dbstate.db.get_person_from_handle(active_handle)
        if not active_person:
            yield False
        #no wrap in Gramplet
        self.no_wrap()
        self.process_person(active_person.handle, 1, "f") # father
        self.process_person(active_person.handle, 0, "a") # active #FIXME: should be 1?
        self.process_person(active_person.handle, 1, "m") # mother
        gens = sorted(self._generations)
        self.append_text(_("\nBreakdown by generation:\n"))
        all = [active_person.handle]
        percent_sign = _("percent sign or text string|%")
        for g in gens:
            yield True
            count = len(self._generations[g])
            handles = self._generations[g]
            self.append_text("     ")
            if g == 0:
                self.link(_("Generation 1"), 'PersonList', handles, 
                          tooltip=_("Double-click to see people in generation"))
                percent = locale.format( '%.2f', 100) + percent_sign
                self.append_text(_(" has 1 of 1 individual (%(percent)s complete)\n") %  {'percent': percent})
            else:
                all.extend(handles)
                self.link(_("Generation %d") % g, 'PersonList', handles,
                          tooltip=_("Double-click to see people in generation %d") % g)
                percent = locale.format('%.2f', float(count)/2**(g-1) * 100) + percent_sign
                self.append_text(ngettext(
                    " has %(count_person)d of %(max_count_person)d individuals (%(percent)s complete)\n",
                    " has %(count_person)d of %(max_count_person)d individuals (%(percent)s complete)\n",
                    2**(g-1)) %  {'count_person': count, 'max_count_person': 2**(g-1), 'percent': percent})
        self.link(_("All generations"), 'PersonList', all,
                  tooltip=_("Double-click to see all generations"))
        self.append_text(ngettext(
            " have %d individual\n",
            " have %d individuals\n",
            len(all)) % len(all))
        # Set to a fixed font
        if self.box_mode == "UTF":
            start, end = self.gui.buffer.get_bounds()
            self.gui.buffer.apply_tag_by_name("fixed", start, end)
        self.append_text("", scroll_to="begin")
