#!/usr/bin/env python
# -*- coding: iso-8859-2 -*-
#
# Svan 3.0
# (c) 2006 Jiri Benc <benc@upir.cz>
#

import os
import sys
import string
import getopt
import datetime
import time
import calendar
import shlex
try:
    from dateutil import easter     # http://labix.org/python-dateutil
    is_easter = True
except ImportError:
    is_easter = False

# defaultni hodnoty
FILE_SVATKY = "~/.svan/svatky"
FILE_DATABASE = "~/.svan/database"
FILE_COLORS = "~/.svan/colors"
SHOW_DAYS = 14

VERSION = "3.0"

def print_help():
    """Vypise napovedu k pouziti a info o programu."""
    sys.stderr.write("""\
Pouit: svan [PARAMETRY]
Upomna na svtky a narozeniny, (c) 2006 Ji Benc <benc@upir.cz>

  -n, --count=N         zobraz N dn ponaje dnekem (standardn %d)
  -s, --start=DATUM     vpis nezane ode dneka, ale od data DATUM, kter
                        mus bt ve tvaru RRRR-MM-DD
  -f, --find=JMENO      pokus se najt, kdy ma dan jmno svtek (me bt
                        zadno bez diakritiky, na velikosti psmen nezle)
  -d, --database=SOUBOR cesta k souboru s databz narozenin
                        (standardn %s)
  --svatky=SOUBOR       cesta k souboru s definic svtku
                        (standardn %s)
  --colors=SOUBOR       cesta k souboru, kter obsahuje definice barev
                        (standardn %s)
  -h, --help            vype tuto npovdu a skon
  -V, --version         vype verzi a podmnky uit (licenci) a skon
""" % (SHOW_DAYS, FILE_DATABASE, FILE_SVATKY, FILE_COLORS))

def print_version():
    """Vypise verzi a licenci."""
    sys.stderr.write("""\
svan version """ + VERSION + """ - birthday and name-day reminder
Copyright (c) 2006 Jiri Benc <benc@upir.cz>
http://upir.cz/linux

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation.

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
""")

def check_params(argv):
    """Projde parametry. Vraci dictionary naplnene parametry (vcetne
    implicitnich)."""
    result = {}
    try:
        (optlist, args) = getopt.gnu_getopt(argv, "n:s:f:d:hV",
            ["count=", "start=", "find=", "database=", "help", "version", "svatky=", "colors="])
    except getopt.GetoptError, err:
        sys.stderr.write("Neznm parametr (%s).\n" % str(err))
        sys.exit(1)
    for (arg, val) in optlist:
        if arg == "-n" or arg == "--count": result["count"] = int(val)
        elif arg == "-s" or arg == "--start": result["start"] = val
        elif arg == "-f" or arg == "--find": result["find"] = val
        elif arg == "-d" or arg == "--database": result["database"] = os.path.expanduser(val)
        elif arg == "--svatky": result["svatky"] = os.path.expanduser(val)
        elif arg == "--colors": result["colors"] = os.path.expanduser(val)
        elif arg == "-h" or arg == "--help":
            print_help()
            sys.exit(1)
        elif arg == "-V" or arg == "--version":
            print_version()
            sys.exit(1)
    # doplneni defaultnich hodnot
    if not result.has_key("count"): result["count"] = SHOW_DAYS
    if not result.has_key("start"): result["start"] = datetime.date.today()
    else:
        try:
            result["start"] = datetime.date.fromtimestamp(time.mktime(time.strptime(result["start"], "%Y-%m-%d")))
        except ValueError:
            sys.stderr.write("patn zadan datum (%s) - mus bt zadno ve tvaru RRRR-MM-DD.\n" % result["start"])
            sys.exit(1)
    if not result.has_key("database"): result["database"] = os.path.expanduser(FILE_DATABASE)
    if not result.has_key("svatky"): result["svatky"] = os.path.expanduser(FILE_SVATKY)
    if not result.has_key("colors"): result["colors"] = os.path.expanduser(FILE_COLORS)

    return result

def read_colors(filename):
    """Nacte definice barev a vrati je jako directory."""
    try:
        file = open(filename, "r")
    except IOError:
        sys.stderr.write("Nemohu najt definice barev (soubor %s).\n" % filename)
        sys.exit(1)
    colors = {}
    conf = shlex.shlex(file)
    while True:
        name = conf.get_token()
        if len(name) == 0: break
        eq = conf.get_token()
        val = conf.get_token()
        if eq != "=" or len(val) == 0:
            sys.stderr.write("Chyba v definici barev (soubor %s, dek %d).\n" % (filename, lineno))
            sys.exit(1)
        if val[0] == '"' or val[0] == "'":
            val = val[1:]
            if val[len(val) - 1] == '"' or val[len(val) - 1] == "'":
                val = val[:-1]
        colors[name] = eval('"' + val.replace('"', '\\"') + '"')
    if not colors.has_key("normal"): colors["normal"] = ""
    if not colors.has_key("birth"): colors["birth"] = ""
    if not colors.has_key("svatek"): colors["svatek"] = ""
    if not colors.has_key("svatek_name"): colors["svatek_name"] = ""
    if not colors.has_key("birth_vip"): colors["birth_vip"] = ""
    if not colors.has_key("svatek_vip"): colors["svatek_vip"] = ""
    if not colors.has_key("svatek_name_vip"): colors["svatek_name_vip"] = ""
    return colors

def read_svatky(filename):
    """Nacte vsechny svatky. Vraci list mesicu, ktery obsahuje list dni, kde
    kazda polozka je tuple dvou stringu: vyznamne a mene vyznamne svatky."""
    try:
        file = open(filename, "r")
    except IOError:
        sys.stderr.write("Nemohu najt svtky (soubor %s).\n" % filename)
        sys.exit(1)
    svatky = []
    exp_day = 1
    exp_mon = 1
    mon_len = calendar.monthrange(2004, 1)[1]
    while True:
        if exp_day == 1:
            svatky.append([])
        try:
            line = file.readline()
        except IOError:
            sys.stderr.write("Chyba pi ten svtk (soubor %s).\n" % filename)
            sys.exit(1)
        if not line:
            sys.stderr.write("Chyba: nepln svtky (soubor %s).\n" % filename)
            sys.exit(1)
        line = line.split("\n", 1)[0]
        if not line: continue
        sp_line = line.split("\t")
        if exp_day != int(sp_line[0]) or exp_mon != int(sp_line[1]):
            sys.stderr.write("Chyba v souboru svtk (soubor %s, oekvan datum: %d %d).\n" % (filename, exp_day, exp_mon))
            sys.exit(1)
        if len(sp_line) < 3: sp_line.append("")
        if len(sp_line) < 4: sp_line.append("")
        svatky[exp_mon - 1].append((sp_line[2], sp_line[3]))
        exp_day += 1
        if exp_day > mon_len:
            exp_day = 1
            exp_mon += 1
            if exp_mon > 12: break
            mon_len = calendar.monthrange(2004, exp_mon)[1]
    file.close()
    return svatky

def read_database(filename):
    """Nacte vsechny narozeniny. Vraci list polozek, kde kazda polozka je
    directory s klicema: by, bm, bd, sm, sd, name, vip."""
    try:
        file = open(filename, "r")
    except IOError:
        sys.stderr.write("Nemohu najt databzi narozenin (soubor %s).\n" % filename)
        sys.exit(1)
    database = []
    while True:
        try:
            line = file.readline()
        except IOError:
            sys.stderr.write("Chyba pi ten narozenin (soubor %s).\n" % filename)
            sys.exit(1)
        if not line: break
        line = line.split("\n", 1)[0]
        if not line or line[0] == "#" or line[0] == ";" or line[0] == "%": continue
        sp_line = line.split(None, 2)
        if sp_line[0][0] < "0" or sp_line[0][0] > "9":
            by = bm = bd = -1
        else:
            dat = sp_line[0].split("-")
            by = int(dat[0])
            bm = int(dat[1])
            bd = int(dat[2])
        if sp_line[1][0] < "0" or sp_line[1][0] > "9":
            sm = sd = -1
        else:
            dat = sp_line[1].split("-")
            sm = int(dat[0])
            sd = int(dat[1])
        if sp_line[2][0] == "+":
            vip = True
            name = sp_line[2].lstrip("+")
        else:
            vip = False
            name = sp_line[2]
        database.append({ "by": by, "bm": bm, "bd": bd, "sm": sm, "sd": sd, "name": name, "vip": vip })
    file.close()
    return database

day_names = [ "Po", "t", "St", "t", "P", "So", "Ne", "--" ]
def day_name(day):
    """Vrati nazev dne (zkracene)."""
    global day_names
    return day_names[day]

def inflect_year(num):
    """Vrati spravne skloneny tvar slova "rok"."""
    if num == 0: return "rok"
    if num == 1: return "rok"
    if num > 1 and num < 5: return "roky"
    return "let"

dia_xlat = string.maketrans("ةݮ", "acdeeinorstuuyzACDEEINORSTUUYZuU")
def remove_diacritics(str):
    """Vrati retezec bez hacku a carek."""
    global dia_xlat
    return str.translate(dia_xlat)

def find_svatek(str, svatky):
    """Pokusi se najit dany svatek a vypsat ho."""
    name = remove_diacritics(str.lower())
    cnt = 0
    month = 0
    for s_month in svatky:
        month += 1
        day = 0
        for s_day in s_month:
            day += 1
            i = 0
            for s_svatek in s_day:
                i += 1
                s_names = s_svatek.split(", ")
                for s in s_names:
                    if remove_diacritics(s.lower()) == name:
                        if i == 1: relevance = ""
                        else: relevance = " (nen v bnch kalendch)"
                        sys.stdout.write("%s: %d. %d.%s\n" % (s, day, month, relevance))
                        cnt += 1
    if cnt == 0:
        sys.stdout.write("Jmno '%s' nebylo nalezeno.\n" % str)

def main():
    """Main program."""
    args = check_params(sys.argv[1:])
    svatky = read_svatky(args["svatky"])
    if args.has_key("find"):
        find_svatek(args["find"], svatky)
        sys.exit(0)
    db = read_database(args["database"])
    colors = read_colors(args["colors"])
    sys.stdout.write("%s----------------.\n" % colors["normal"])
    now = args["start"]
    oneday = datetime.timedelta(1)
    if is_easter:
        easter_sun = easter.easter(now.year)
        easter_mon = easter_sun + oneday
    leap_handled = False
    i = 0
    show_days = args["count"]
    while i < show_days:
        month = now.month
        day = now.day
        wday = now.weekday()
        if not leap_handled and now.month == 3 and now.day == 1 and not calendar.isleap(now.year):
            # osetrime 29. 2., i kdyz neexistuje
            month = 2
            day = 29
            wday = 7
            leap_handled = True
        else:
            now += oneday
        sys.stdout.write("%s %2d. %2d. %4d | " % (day_name(wday), day, month, now.year))

        # nalezeni lidi, kteri maji svatek
        cnt = 0
        output = ""
        attrib = colors["svatek"]
        for person in db:
            if person["sm"] == month and person["sd"] == day:
                if cnt: output += ", "
                if person["vip"]:
                    output += colors["svatek_name_vip"]
                    attrib = colors["svatek_vip"]
                else:
                    output += colors["svatek_name"]
                output += person["name"] + colors["normal"]
                cnt += 1
        if cnt > 0:
            sys.stdout.write("%s%s%s (%s)\n" % (attrib, svatky[month - 1][day - 1][0], colors["normal"], output))
        else:
            sys.stdout.write("%s\n" % svatky[month - 1][day - 1][0])

        # Velikonoce
        if is_easter:
            if easter_sun.year != now.year:
                easter_sun = easter.easter(now.year)
                easter_mon = easter_sun + oneday
            if easter_sun.month == month and easter_sun.day == day:
                sys.stdout.write("                | (nedle velikonon)\n")
            if easter_mon.month == month and easter_mon.day == day:
                sys.stdout.write("                | (pondl velikonon)\n")

        # svatek matek
        if wday == 6 and month == 5 and day >= 8 and day <= 14:
            sys.stdout.write("                | %s(svtek matek)%s\n" % (colors["svatek"], colors["normal"]))

        # nalezeni lidi, kteri maji narozeniny
        for person in db:
            if person["bm"] == month and person["bd"] == day:
                age = now.year - person["by"]
                sys.stdout.write("                | ")
                if person["vip"]: sys.stdout.write(colors["birth_vip"])
                else: sys.stdout.write(colors["birth"])
                sys.stdout.write("* %s (%d %s)%s\n" % (person["name"], age, inflect_year(age), colors["normal"]))

        i += 1

main()
