#! /usr/bin/env python
# -*- coding: utf-8 -*-
"""
Hijri Tray Applet for GNOME (also works with KDE)
Copyright (c) 2006-2011 Muayyad Saleh Alsadi<alsadi@gmail.com>

Based on an enhanced algorithm designed by me
the algorithm is discussed in a book titled "حتى لا ندخل جحور الضباب"
(not yet published)

The algorith itself is not here, it's in another file called hijra.py


    Released under terms on Waqf Public License.
    This program is free software; you can redistribute it and/or modify
    it under the terms of the latest version Waqf Public License as
    published by Ojuba.org.

    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.

    The Latest version of the license can be found on
    "http://www.ojuba.org/wiki/doku.php/waqf/license"

"""

# TODO: Implement ocations reminder
# TODO: Implement configuration, ie. allow direction change, and setting first way of week

import sys, os, os.path, time, re
import pango, gobject, gtk

import sys

no_notify=False

try: import pynotify
except ImportError: no_notify=True

from HijriCal import HijriCal
cal=HijriCal()

week_days=[ "الأحد", "الإثنين", "الثلاثاء", "الأربعاء", "الخميس", "الجمعة", "السبت" ]
months=[
  "محرم","صفر","ربيع الأول","ربيع الثاني",
  "جمادى الأولى","جمادى الثانية","رجب","شعبان",
  "رمضان","شوال","ذو القعدة","ذو الحجة"
  ]
gmonths=[
  "كانون ثاني", "شباط", "آذار", "نيسان",
  "أيار", "حزيران","تموز","آب",
  "أيلول", "تشرين أول", "تشرين ثاني", "كانون أول"
  ]

def wday_index(i):
  ws=cal.get_week_start()
  if (cal.get_direction()==1): return (i+ws) % 7
  else: return ((6-i)+ws) % 7

def wday_from_index(i):
  ws=cal.get_week_start()
  if (cal.get_direction()==1): return (7+i-ws) % 7
  else: return (6+ws-i)%7

def guess_data_dir():
  d=os.path.dirname(__file__)
  dd=os.path.join(d, '..', 'share', 'hijra', 'hijra-data')
  if os.path.isdir(dd): return dd
  dd=os.path.join(d, 'hijra-data')
  return dd

# Update icon file
def update_icon_file(day,mth):
  svg_fn = os.path.join(guess_data_dir(), "cal-template.svg")
  if not os.path.isfile(svg_fn): return None
  out_fn = os.path.expanduser('~/.hijra-icon.svg')
  day_re = re.compile(r"""<!--// DAY STARTS HERE //-->(\d+)<!--// DAY ENDS HERE //-->""", re.M)
  mth_re = re.compile(r"""<!--// MONTH STARTS HERE //-->(\d+)<!--// MONTH ENDS HERE //-->""", re.M)
  svg_text = open(svg_fn, 'rt').read()
  svg_text, n = mth_re.subn("%02d" % mth, svg_text)
  svg_text, n = day_re.subn("%02d" % day, svg_text)
  try: open(out_fn, 'wt+').write(svg_text)
  except: return None
  return out_fn


class CalAbout(gtk.AboutDialog):
  def __init__(self):
    gtk.AboutDialog.__init__(self)
    self.set_default_response(gtk.RESPONSE_CLOSE)
    self.connect('delete-event', lambda w, *a: w.hide() or True)
    self.connect('response', lambda w, *a: w.hide() or True)
    try: self.set_program_name("Hijra")
    except AttributeError: pass
    self.set_name("Hijra")
    #self.set_version(version)
    self.set_copyright("Copyright (c) 2006-2008 Muayyad Saleh Alsadi <alsadi@gmail.com>")
    self.set_comments("Hijri Islamic Calendar")
    self.set_license("""
      Released under terms on Waqf Public License.
      This program is free software; you can redistribute it and/or modify
      it under the terms of the latest version Waqf Public License as
      published by Ojuba.org.

      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.

      The Latest version of the license can be found on
      "http://www.ojuba.org/wiki/waqf/license"

""")
    self.set_website("http://hijra.ojuba.org/")
    self.set_website_label("http://hijra.ojuba.org")
    self.set_authors(["Muayyad Saleh Alsadi <alsadi@ojuba.org>"])
#  self.set_documenters(documenters)
#  self.set_artists(artists)
#  self.set_translator_credits(translator_credits)
#  self.set_logo(logo)
#  self.set_logo_icon_name(icon_name)


class CalButton(gtk.Button):
  def __init__(self, title=None, tooltip=None, img=None, ico_name=None, img_fn=None, stock=None):
    gtk.Button.__init__(self)
    if title: self.set_text(title)
    if stock: img=gtk.Image(); img.set_from_stock(stock, gtk.ICON_SIZE_SMALL_TOOLBAR)
    if img_fn: img=gtk.Image(); img.set_from_file(img_fn)
    if ico_name: img=gtk.Image(); img.set_from_icon_name(ico_name)
    if img: self.set_image(img)
    if tooltip: self.set_tooltip_text(tooltip)
    self.set_focus_on_click(False)


class CalWindow(gtk.Window):
  def __init__(self, cal):
    self._first_time_show=False
    self.cal=cal
    self.about=CalAbout()
    gtk.Window.__init__(self)
    self.accel=gtk.AccelGroup()
    self.add_accel_group(self.accel)
    self.hide_on_delete()
    #self.set_size_request(200, 300)
    self.connect('delete-event', lambda w, *a: w.hide() or True)
    vb=gtk.VBox(False,0); self.add(vb)
    hb=gtk.HBox(False,0); vb.pack_start(hb,False, False, 0)
    b=CalButton(tooltip="عام سابق", stock=gtk.STOCK_GOTO_FIRST)
    hb.pack_start(b,False, False, 0)
    b.connect('clicked', self.prev_year_cb)
    b=CalButton(tooltip="شهر سابق", stock=gtk.STOCK_GO_BACK)
    hb.pack_start(b,False, False, 0)
    b.connect('clicked', self.prev_month_cb)
    self.t=gtk.Label("...")
    self.t.set_justify(gtk.JUSTIFY_CENTER)
    hb.pack_start(self.t,True, True, 0)
    b=CalButton(tooltip="شهر تالي", stock=gtk.STOCK_GO_FORWARD)
    hb.pack_start(b,False, False, 0)
    b.connect('clicked', self.next_month_cb)
    b=CalButton(tooltip="عام تالي", stock=gtk.STOCK_GOTO_LAST)
    hb.pack_start(b,False, False, 0)
    b.connect('clicked', self.next_year_cb)
    
    hb=gtk.HBox(False,0); vb.pack_start(hb,False, False, 0)
    grid = gtk.Table(7,6,True)
    vb.pack_start(grid,True, True, 0)
    self.fill_grid(grid)
    
    hb=gtk.HBox(False,0); vb.pack_start(hb,False, False, 0)
    self.g_e=e=gtk.Entry()
    e.set_width_chars(6)
    e.set_tooltip_text("العام الجريجوري")
    e.connect("activate", self.convert_cb)
    hb.pack_start(e,False, False, 0)
    img=gtk.Image(); img.set_from_stock(gtk.STOCK_CONVERT, gtk.ICON_SIZE_MENU)
    hb.pack_start(img,False, False, 0)
    self.h_e=e=gtk.Entry()
    e.set_width_chars(6)
    e.set_tooltip_text("العام الهجري")
    hb.pack_start(e,False, False, 0)
    img=gtk.Image(); img.set_from_stock(gtk.STOCK_JUMP_TO, gtk.ICON_SIZE_MENU)
    hb.pack_start(img,False, False, 0)
    e.connect("activate", self.jump_cb)
    b=CalButton(tooltip="اليوم", stock=gtk.STOCK_REFRESH)
    b.connect("clicked", self.today_cb)
    hb.pack_start(b, False, False, 0)
    b=CalButton(tooltip="حول", stock=gtk.STOCK_ABOUT)
    b.connect("clicked", lambda *a: self.about.run())
    hb.pack_start(b, False, False, 0)
    
    
  def fill_grid(self, grid):
    days_l=[None for i in range(7)]
    cell=[[None for i in range(7)] for j in range(7)]
    for i in range(7):
      days_l[i]=gtk.Label('...')
      e=gtk.EventBox(); e.add(days_l[i])
      grid.attach(e, i, i+1, 0, 1, gtk.FILL | gtk.EXPAND,gtk.FILL | gtk.EXPAND, 0, 0)
    for n in range(42):
      i=n%7; j=n/7;
      cell[j][i]=gtk.Label("-")
      cell[j][i].set_alignment(0.5,0.5)
      cell[j][i].set_justify(gtk.JUSTIFY_CENTER)
      cell[j][i].set_tooltip_text(None)
      e=gtk.EventBox(); e.add(cell[j][i])
      grid.attach(e, i,i+1,j+1,j+2,gtk.FILL | gtk.EXPAND,gtk.FILL | gtk.EXPAND,0,0)
    self.cell=cell
    self.days_l=days_l
    
  def prev_year_cb(self, *a, **kw):
    self.cal.goto_hijri_day(self.cal.Y-1, self.cal.M, 1);
    self.update()

  def next_year_cb(self, *a, **kw):
    self.cal.goto_hijri_day(self.cal.Y+1, self.cal.M, 1);
    self.update()

  def prev_month_cb(self, *a, **kw):
    Y,M,D=self.cal.Y, self.cal.M-1, 1
    if (M<1): M=12; Y-=1
    self.cal.goto_hijri_day(Y, M, 1)
    self.update()

  def next_month_cb(self, *a, **kw):
    Y,M,D=self.cal.Y, self.cal.M+1, 1
    if (M>12): M=1; Y+=1
    self.cal.goto_hijri_day(Y, M, 1)
    self.update()

  def today_cb(self, *a, **kw):
    self.cal.goto_today()
    self.update()

  def convert_cb(self, *a, **kw):
    try: y=int(self.g_e.get_text())
    except ValueError: return
    self.cal.goto_gregorian_day(y,1,1)
    self.h_e.set_text(str(self.cal.Y))
    self.update()

  def jump_cb(self, *a, **kw):
    try: y=int(self.h_e.get_text())
    except ValueError: return
    self.cal.goto_hijri_day(y,1,1)
    self.update()


  def get_theme_colors(self):
    # colors[is_holyday][normal selected inactive]=[fg,bg,fg_s,bg_s]
    colors=[[[0 for i in 1,2,3,4] for j in 1,2,3] for k in 1,2]
    for i,s in enumerate((gtk.STATE_NORMAL,gtk.STATE_SELECTED,gtk.STATE_INSENSITIVE)):
      fg,bg=self.style.fg[s],self.style.bg[s]
      colors[0][i]=bg,fg,bg.to_string(),fg.to_string()
      colors[1][i]=fg,bg,fg.to_string(),bg.to_string()
    self.colors=colors
    return self.colors

  def update(self):
    colors=self.get_theme_colors()
    if (self.cal.Y, self.cal.M)==self.cal.today[0:2]: self.cal.goto_today()
    self.t.set_text(months[self.cal.M-1]+" "+str(self.cal.Y))
    self.g_e.set_text(str(self.cal.gy))
    self.h_e.set_text(str(self.cal.Y))
    a=self.cal.get_array()
    b=self.cal.get_g_array()
    for i in range(7):
      self.days_l[i].set_text(week_days[wday_index(i)])
      # colors[is_holyday][normal selected inactive]=[fg,bg,fg_s,bg_s]
      self.days_l[i].modify_fg(gtk.STATE_NORMAL,colors[1][1][0])
      self.days_l[i].parent.modify_fg(gtk.STATE_NORMAL,colors[1][1][0])
      self.days_l[i].parent.modify_bg(gtk.STATE_NORMAL,colors[1][1][1]);
    holyday_col=wday_from_index((6+self.cal.get_week_start())%7)
    for j in range(6): self.cell[j][holyday_col].parent.modify_bg(gtk.STATE_NORMAL,colors[1][1][1])
    for n in range(42):
      i=n%7; j=n/7;
      if (a[j][i]):
        if a[j][i]==self.cal.D: is_selected=0
        else: is_selected=1
        if i==holyday_col: is_holyday=1
        else: is_holyday=0
        self.cell[j][i].set_markup('<span size="large" weight="bold" foreground="%s" background="%s">%02d</span>\n<span size="small" weight="bold" foreground="grey">%02d/%02d</span>' % (
          colors[is_holyday][is_selected][2],colors[is_holyday][is_selected][3],
          a[j][i], b[j][i][0],b[j][i][1]))
        h_str="%d من %s لعام %d هـ" % (a[j][i], months[self.cal.M-1], self.cal.Y)
        g_str="%d من %s لعام %d م" % (b[j][i][0], gmonths[b[j][i][1]-1], b[j][i][2])
        self.cell[j][i].set_tooltip_text("%s\n%s" % (h_str,g_str))
      else: self.cell[j][i].set_text('-'); self.cell[j][i].set_tooltip_text(None)


  def first_time_show(self):
    self._first_time_show=True
    if gtk.widget_get_default_direction()==gtk.TEXT_DIR_LTR:
      self.cal.set_direction(-1) # LTR
    else:
      self.cal.set_direction(1) # RTL
    self.update()

  def show(self):
    r=super(gtk.Window, self).show()
    if not self._first_time_show: self.first_time_show()
    return r

  def show_all(self):
    r=super(gtk.Window, self).show_all()
    if not self._first_time_show: self.first_time_show()
    return r


class CalStatusIcon(gtk.StatusIcon):
  def __init__(self, cal):
    gtk.StatusIcon.__init__(self)
    self.cal=cal
    self.notify=None
    if not no_notify:
      pynotify.init('HijriApplet')
      self.notify=pynotify.Notification("التقويم الهجري")

    Y,M,D,W=self.cal.today
    yy,mm,dd=self.cal.g_today
    self.connect ('button-press-event', self.clicked_cb)
    self.set_title("التقويم الهجري")
    self.last_d_m="%02d/%02d" % (D,M)
    self.set_tooltip_text("%s, %d من %s لعام %d" % (week_days[W], D, months[M-1], Y))
    svg_fn=update_icon_file(D,M)
    if svg_fn:
      self.set_from_file(svg_fn)
      gtk.window_set_default_icon_from_file(svg_fn)
    else:
      self.set_from_icon_name("stock_calendar")
      gtk.window_set_default_icon_name('stock_calendar')
    self.win=CalWindow(cal)
    self.set_visible(True)
    self.setup_popup_menu()
    self.update_notify()
    if not '--hidden' in sys.argv: self.win.show_all()
    else: self.show_notify()
    gobject.timeout_add(5000, self.update)

  def update_notify(self):
    if not self.notify: return
    Y,M,D,W=self.cal.today
    yy,mm,dd=self.cal.g_today
    self.notify.set_property('icon-name','gtk-dialog-info')
    self.notify.set_property('summary', "التقويم الهجري" )
    self.notify.set_property('body', "%s, %d من %s لعام %d\nالموافق %d من %s لعام %s" % (week_days[W], D, months[M-1], Y,dd,gmonths[mm-1],yy) )
    #self.notify.set_timeout(5)
  
  def show_notify(self):
    if not self.notify: return
    self.notify.show()

  def update(self):
    self.cal.refresh_today()
    Y,M,D,W=self.cal.today
    d_m="%02d/%02d" % (D,M)
    if self.last_d_m == d_m: return True
    self.update_notify()
    self.set_tooltip_text("%s, %d من %s لعام %d" % (week_days[W], D, months[M-1], Y))
    self.last_d_m=d_m
    svg_fn=update_icon_file(D,M)
    if svg_fn:
      self.set_from_file(svg_fn)
      gtk.window_set_default_icon_from_file(svg_fn)
      self.win.set_icon_from_file(svg_fn)
      self.win.about.set_icon_from_file(svg_fn)
    self.win.update()
    #self.setup_notify(Y,M,D,W)
    self.show_notify()
    return True

  def show_popup_menu(self):
    self.popup_menu.show_all()
    if self.win.get_visible(): self.popup_menu.get_children()[0].hide()
    else: self.popup_menu.get_children()[1].hide()
    self.popup_menu.popup(None, None, gtk.status_icon_position_menu, 3, gtk.get_current_event_time(), self)


  def clicked_cb(self, w, event):
    # TODO: use defined buttons
    if event.button == 1:
      if not self.win.g_e.get_property('visible'):
        self.win.show_all()
      else:
        self.win.set_property("visible", not self.win.get_property("visible"))
    elif event.button == 2: self.show_notify()
    elif event.button == 3: self.show_popup_menu()
  
  def show_cb(self, *a):
    self.win.show_all()
    self.update()

  def setup_popup_menu(self):
    self.popup_menu=popup_menu=gtk.Menu()
    i = gtk.ImageMenuItem("Show")
    i.connect('activate', self.show_cb)
    popup_menu.add(i)
    i = gtk.ImageMenuItem("Hide")
    i.connect('activate', lambda *args: self.win.hide())
    popup_menu.add(i)
    i = gtk.ImageMenuItem(gtk.STOCK_ABOUT)
    i.connect('activate', lambda *a: self.win.about.run())
    i.set_always_show_image(True)
    popup_menu.add(i)
    i = gtk.ImageMenuItem(gtk.STOCK_QUIT)
    i.connect('activate', gtk.main_quit)
    i.set_always_show_image(True)
    popup_menu.add(i)

bus=None
st=None
try:
  import dbus
  import dbus.service
  #import gobject # for gobject.MainLoop() if no gtk is to be used
  from dbus.mainloop.glib import DBusGMainLoop

  dbus_loop = DBusGMainLoop(set_as_default=True)
  bus = dbus.SessionBus()
except ImportError: pass

# use dbus so that, only one Applet is shown
def init_dbus():
  global bus_name, bus_object,win
  if not bus: return
  class Manager(dbus.service.Object):
    def __init__(self, bus, path):
          dbus.service.Object.__init__(self,bus,path)

    @dbus.service.method("org.freedesktop.HijriApplet", in_signature='', out_signature='')
    def Show(self):
      st.win.show_all()

    @dbus.service.method("org.freedesktop.HijriApplet", in_signature='', out_signature='s')
    def Version(self):
      return "0.1"
  # values from /usr/include/dbus-1.0/dbus/dbus-shared.h
  r=bus.request_name('org.freedesktop.HijriApplet', flags=0x4)
  if r!=1:
    print "Another process own HijriApplet Service, ask it to show up: "
    trials=0; appletbus=False
    while(appletbus==False and trials<20):
      print ".",
      try:
        appletbus=bus.get_object("org.freedesktop.HijriApplet","/Manager"); break
      except:
        appletbus=False
      time.sleep(1); trials+=1
    print "*"
    if appletbus: appletbus.Show(dbus_interface='org.freedesktop.HijriApplet')
    else: print "unable to connect"
    exit(0)
  bus_name = dbus.service.BusName("org.freedesktop.HijriApplet", bus)
  bus_object = Manager(bus, '/Manager')

def main():
  global st
  init_dbus()
  st=CalStatusIcon(cal)
  gtk.main()

main()
