#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2000-2007  Donald N. Allingham
# Copyright (C) 2007-2008  Brian G. Matherly
#
# 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
#

"""
Run a query on the tables
"""

from Simple import SimpleAccess, SimpleDoc, SimpleTable
from gettext import gettext as _
from gen.plug import PluginManager
import Utils
from ReportBase import CATEGORY_QR_MISC
import DateHandler
import gen.lib
import Config

def cleanup_column_name(column):
    """ Handle column aliases for CSV spreadsheet import and SQL """
    retval = column
    # Title case:
    if retval in ["Lastname", 
                  "Surname", _("Surname")]:
        return "surname"
    elif retval in ["Firstname", 
                    "Given name", _("Given name"), 
                    "Given", _("Given")]:
        return "firstname"
    elif retval in ["Callname", 
                    "Call name", _("Call name"),
                    "Call", _("Call")]:
        return "callname"
    elif retval in ["Title", _("Title")]:
        return "title"
    elif retval in ["Prefix", _("Prefix")]:
        return "prefix"
    elif retval in ["Suffix", _("Suffix")]:
        return "suffix"
    elif retval in ["Gender", _("Gender")]:
        return "gender"
    elif retval in ["Source", _("Source")]:
        return "source"
    elif retval in ["Note", _("Note")]:
        return "note"
    elif retval in ["Birthplace", 
                    "Birth place", _("Birth place")]:
        return "birthplace"
    elif retval in ["Birthdate", 
                    "Birth date", _("Birth date")]:
        return "birthdate"
    elif retval in ["Birthsource", 
                    "Birth source", _("Birth source")]:
        return "birthsource"
    elif retval in ["Deathplace", 
                    "Death place", _("Death place")]:
        return "deathplace"
    elif retval in ["Deathdate", 
                    "Death date", _("Death date")]:
        return "deathdate"
    elif retval in ["Deathsource", 
                    "Death source", _("Death source")]:
        return "deathsource"
    elif retval in ["Deathcause", 
                    "Death cause", _("Death cause")]:
        return "deathcause"
    elif retval in ["Grampsid", "ID",
                    "Gramps id", _("Gramps id")]:
        return "grampsid"
    elif retval in ["Person", _("Person")]:
        return "person"
    # ----------------------------------
    elif retval in ["Child", _("Child")]:
        return "child"
    elif retval in ["Source", _("Source")]:
        return "source"
    elif retval in ["Family", _("Family")]:
        return "family"
    # ----------------------------------
    elif retval in ["Mother", _("Mother"), 
                    "Wife", _("Wife"),
                    "Parent2", _("Parent2")]:
        return "wife"
    elif retval in ["Father", _("Father"), 
                    "Husband", _("Husband"),
                    "Parent1", _("Parent1")]:
        return "husband"
    elif retval in ["Marriage", _("Marriage")]:
        return "marriage"
    elif retval in ["Date", _("Date")]:
        return "date"
    elif retval in ["Place", _("Place")]:
        return "place"
    # lowercase
    elif retval in ["lastname", "last_name", 
                  "surname", _("surname")]:
        return "surname"
    elif retval in ["firstname", "first_name", "given_name",
                    "given name", _("given name"), 
                    "given", _("given")]:
        return "firstname"
    elif retval in ["callname", "call_name",
                    "call name", 
                    "call", _("call")]:
        return "callname"
    elif retval in ["title", _("title")]:
        return "title"
    elif retval in ["prefix", _("prefix")]:
        return "prefix"
    elif retval in ["suffix", _("suffix")]:
        return "suffix"
    elif retval in ["gender", _("gender")]:
        return "gender"
    elif retval in ["source", _("source")]:
        return "source"
    elif retval in ["note", _("note")]:
        return "note"
    elif retval in ["birthplace", "birth_place",
                    "birth place", _("birth place")]:
        return "birthplace"
    elif retval in ["birthdate", "birth_date",
                    "birth date", _("birth date")]:
        return "birthdate"
    elif retval in ["birthsource", "birth_source",
                    "birth source", _("birth source")]:
        return "birthsource"
    elif retval in ["deathplace", "death_place",
                    "death place", _("death place")]:
        return "deathplace"
    elif retval in ["deathdate", "death_date",
                    "death date", _("death date")]:
        return "deathdate"
    elif retval in ["deathsource", "death_source",
                    "death source", _("death source")]:
        return "deathsource"
    elif retval in ["deathcause", "death_cause",
                    "death cause", _("death cause")]:
        return "deathcause"
    elif retval in ["grampsid", "id", "gramps_id", 
                    "gramps id", _("gramps id")]:
        return "grampsid"
    elif retval in ["person", _("person")]:
        return "person"
    # ----------------------------------
    elif retval in ["child", _("child")]:
        return "child"
    elif retval in ["source", _("source")]:
        return "source"
    elif retval in ["family", _("family")]:
        return "family"
    # ----------------------------------
    elif retval in ["mother", _("mother"), 
                    "wife", _("wife"),
                    "parent2", _("parent2")]:
        return "wife"
    elif retval in ["father", _("father"), 
                    "husband", _("husband"),
                    "parent1", _("parent1")]:
        return "husband"
    elif retval in ["marriage", _("marriage")]:
        return "marriage"
    elif retval in ["date", _("date")]:
        return "date"
    elif retval in ["place", _("place")]:
        return "place"
    #----------------------------------------------------
    return retval


class DBI(object):
    def __init__(self, database, document):
        self.database = database
        self.document = document

    def parse(self, query):
        # select col1, col2 from table where exp;
        # select * from table where exp;
        # delete from table where exp;
        self.query = query
        state = "START"
        data = None
        i = 0
        self.columns = []
        self.command = None
        self.where = None
        self.table = None
        while i < len(query):
            c = query[i]
            #print "STATE:", state, c
            if state == "START":
                if c in [' ', '\n', '\t']: # pre white space
                    pass # skip it
                else:
                    state = "COMMAND"
                    data = c
            elif state == "COMMAND":
                if c in [' ', '\n', '\t']: # ending white space
                    self.command = data.lower()
                    data = ''
                    state = "AFTER-COMMAND"
                else:
                    data += c
            elif state == "AFTER-COMMAND":
                if c in [' ', '\n', '\t']: # pre white space
                    pass
                else:
                    state = "COL_OR_FROM"
                    i -= 1
            elif state == "COL_OR_FROM":
                if c in [' ', '\n', '\t',  ',']: # end white space or comma
                    if data.upper() == "FROM":
                        data = ''
                        state = "PRE-GET-TABLE"
                    else:
                        self.columns.append(data.lower())
                        data = ''
                        state = "AFTER-COMMAND"
                else:
                    data += c
            elif state == "PRE-GET-TABLE":
                if c in [' ', '\n', '\t']: # pre white space
                    pass
                else:
                    state = "GET-TABLE"
                    i -= 1
            elif state == "GET-TABLE":
                if c in [' ', '\n', '\t', ';']: # end white space or colon
                    self.table = data.lower()
                    data = ''
                    state = "PRE-GET-WHERE"
                else:
                    data += c
            elif state == "PRE-GET-WHERE":
                if c in [' ', '\n', '\t']: # pre white space
                    pass
                else:
                    state = "GET-WHERE"
                    i -= 1
            elif state == "GET-WHERE":
                if c in [' ', '\n', '\t']: # end white space
                    if data.upper() != "WHERE":
                        raise AttributeError("expecting WHERE got '%s'" % data)
                    else:
                        data = ''
                        state = "GET-EXP"
                else:
                    data += c
            elif state == "GET-EXP":
                self.where = query[i:]
                self.where = self.where.strip()
                if self.where.endswith(";"):
                    self.where = self.where[:-1]
                i = len(query)
            else:
                raise AttributeError("unknown state: '%s'" % state)
            i += 1
        if self.table is None:
            raise AttributeError("malformed query: no table in '%s'\n" % self.query)

    def close(self):
        try:
            self.progress.close()
        except:
            pass

    def eval(self):
        self.sdb = SimpleAccess(self.database)
        self.stab = SimpleTable(self.sdb)
        self.select = 0
        self.progress = Utils.ProgressMeter(_('Processing Query'))
        self.process_table()
        if self.select > 0:
            self.sdoc = SimpleDoc(self.document)
            self.sdoc.title(self.query)
            self.sdoc.paragraph("\n")
            self.sdoc.paragraph("%d rows processed.\n" % self.select)
            self.stab.write(self.sdoc)
            self.sdoc.paragraph("")
        return _("[%d rows processed]") % self.select

    def get_columns(self, table):
        if table == "people":
            return ("name", "grampsid", "gender", 
                    "birth_date", "birth_place", 
                    "death_date", "death_place",
                    "change", "marker", "private")
        elif table == "events":
            return ("grampsid", "type", "date", 
                    "description", "place", 
                    "change", "marker", "private")
        # families = grampsid, father, mother, relationship, date
        # sources = 
        # places =
        # media =
        # repos = 
        # notes = 
        else:
            raise AttributeError("unknown table: '%s'" % table)
    
    def process_table(self):
        for col_name in self.columns[:]: # copy
            if col_name == "*":
                self.columns.remove('*')
                self.columns.extend( self.get_columns(self.table))
        self.stab.columns(*map(lambda s: s.replace("_", "__"),
                               self.columns))
        if self.table == "people":
            all_people = [person for person in self.sdb.all_people()]
            if len(all_people) > 100:
                self.progress.set_pass(_('Matching people...'), len(all_people)/50)
            count = 0
            for person in all_people:
                if len(all_people) > 100:
                    if count % 50 == 0:
                        self.progress.step()
                count += 1
                row = []
                sorts = []
                env = {_("Date"): gen.lib.date.Date} # env for py.eval
                for col_name in self.columns:
                    col = cleanup_column_name(col_name)
                    if col == "name":
                        env[col_name] = str(person)
                        row.append(person)
                    elif col == "firstname":
                        env[col_name] = person.get_primary_name().get_first_name()
                        row.append(env[col_name])
                    elif col == "surname":
                        env[col_name] = person.get_primary_name().get_surname()
                        row.append(env[col_name])
                    elif col == "suffix":
                        env[col_name] = person.get_primary_name().get_suffix()
                        row.append(env[col_name])
                    elif col == "title":
                        env[col_name] = person.get_primary_name().get_title()
                        row.append(env[col_name])
                    elif col == "birthdate":
                        env[col_name] = self.sdb.birth_date_obj(person)
                        row.append(env[col_name])
                    elif col == "deathdate":
                        env[col_name] = self.sdb.death_date_obj(person)
                        row.append(env[col_name])
                    elif col == "gender":
                        env[col_name] = self.sdb.gender(person)
                        row.append(env[col_name])
                    elif col == "birthplace":
                        env[col_name] = self.sdb.birth_place(person)
                        row.append(env[col_name])
                    elif col == "deathplace":
                        env[col_name] = self.sdb.death_place(person)
                        row.append(env[col_name])
                    elif col == "change":
                        env[col_name] = person.get_change_display()
                        row.append(env[col_name])
                    elif col == "marker":
                        env[col_name] = person.marker.string
                        row.append(env[col_name])
                    elif col == "private":
                        env[col_name] = {True: "private", False: ""}[person.private]
                        row.append(env[col_name])
                    elif col == "grampsid":
                        env[col_name] = person.gramps_id
                        row.append(env[col_name])
                    else:
                        raise AttributeError("unknown column: '%s'" % col_name)
                #sorts.append((len(row)-1,sortval))
                # should we add it?:
                if self.where:
                    try:
                        result = eval(self.where, env)
                    except:
                        raise AttributeError("malformed where clause: '%s'" % self.where)
                        result = False
                else:
                    result = True
                if result:
                    self.select += 1
                    if self.command == "select":
                        self.stab.row(*row)
                        for (col, value) in sorts:
                            self.stab.row_sort_val(col, value)
                    elif self.command == "delete":
                        self.database.active = person
                        trans = self.database.transaction_begin()
                        active_name = _("Delete Person (%s)") % self.sdb.name(person)
                        gen.utils.delete_person_from_database(self.database, person, trans)
                        # FIXME: delete familes, events, notes, resources, etc, if possible
                        self.database.transaction_commit(trans, active_name)
                    else:
                        raise AttributeError("unknown command: '%s'", self.command)

def run(database, document, query):
    """
    """
    retval = ""
    dbi = DBI(database, document)
    try:
        q = dbi.parse(query)
    except AttributeError, msg:
        return msg
    try:
        retval = dbi.eval()
    except AttributeError, msg:
        # dialog?
        retval = msg
    dbi.close()
    return retval

#------------------------------------------------------------------------
#
# Register the report
#
#------------------------------------------------------------------------
pmgr = PluginManager.get_instance()
pmgr.register_quick_report(
    name = 'query',
    category = CATEGORY_QR_MISC,
    run_func = run,
    translated_name = _("Query"),
    status = _("Stable"),
    description= _("Display data that matches a query"),
    author_name="Douglas Blank",
    author_email="dblank@cs.brynmawr.edu"
    )

