# -*- coding: UTF-8 -*-

from Gnumeric import *

# This is a port of libhdate to python
# libhdate: http://libhdate.sourceforge.net
#
# By Yaacov Zamir 2003, 2004 (c) under GPL

# constants, in 1/18th of minute
def HOUR():
	return 1080
def DAY():
	return (24*HOUR())
def WEEK():
	return (7*DAY())
def M(h,p):
	return ((h)*HOUR()+p)
def MONTH():
	return (DAY()+M(12,793))

class hdate:
	# init a new hdate object with gregorian calendar date ( day, month, year )
	def __init__(self, d, m, y):
		# labels for day of week
		self.h_days = [("א"), ("ב"), ("ג"), ("ד"), ("ה"), ("ו"), ("ש")]
		self.days = [("Su"), ("Mo"), ("Tu"), ("We"), ("Th"), ("Fr"), ("Sat")]
		
		# labels for hebrew months
		self.h_heb_months = [("תשרי"), ("חשון"), ("כסלו"),
		               ("טבת"), ("שבט"),("אדר"), ("ניסן"), ("איר"), ("סיון"), ("תמוז"), 
		               ("אב"), ("אלול"), ("אדר א"), ("אדר ב")]
		self.heb_months = [("Tishre"), ("Heshvan"), ("Kislev"),
		               ("Tevet"), ("Shvat"),("Adar"), ("Nisan"), ("Eyar"), ("Sivan"), ("Tamuz"), 
		               ("Av"), ("Elul"), ("Adar I"), ("Adar II")]
		
		# labels for gregorian months
		self.h_geo_months = [("ינואר"), ("פברואר"), ("מרץ"), ("אפריל"), 
		               ("מאי"), ("יוני"), ("יולי"), ("אוגוסט"), ("ספטמבר"),
		               ("אוקטובר"), ("נובמבר"), ("דצמבר")]
		self.geo_months = [("January"), ("February"), ("March"), ("April"), 
		               ("May"), ("June"), ("July"), ("August"), ("September"),
		               ("October"), ("November"), ("December")]
		
		# init a new date from gregorian day month year
		self.julean = self.gdate_to_jd (d, m, y)
		self.day , self.month , self.year = self.jd_to_hdate (self.julean)
		self.g_day , self.g_month , self.g_year = self.jd_to_gdate (self.julean)
		self.day_of_week = (self.julean + 1) % 7
	
	# reset a new date from the julean day
	def set_julean(self, jd):
		self.julean = jd
		self.day, self.month ,self.year = self.jd_to_hdate (self.julean)
		self.g_day , self.g_month , self.g_year = self.jd_to_gdate (self.julean)
		self.day_of_week = (self.julean + 1) % 7
	
	# reset a new date from the hebrew date
	def set_hdate(self, d, m, y):
		self.julean = self.hdate_to_jd (d, m, y)
		self.day , self.month , self.year = self.jd_to_hdate (self.julean)
		self.g_day , self.g_month , self.g_year = self.jd_to_gdate(self.julean)
		self.day_of_week = (self.julean + 1) % 7
	
	################################
	# get day,month and year strings
	################################
	
	# get the name of the day in hebrew
	def h_day_of_week_name (self):
		return self.h_days[self.day_of_week]
	
	# get the name of the day in english
	def day_of_week_name (self):
		return self.days[self.day_of_week]
	
	# hebrew date functions 
	
	# get the day of the hebrew month in hebrew digits
	def h_dayname (self):
		return self.int_to_hebrew (self.day + 1)
	
	# get the day of the hebrew month in arabic digits
	def dayname(self):
		return str (self.day + 1)
	
	# get the name of the hebrew month in hebrew
	def h_monthname(self):
		return self.h_heb_months[self.month]
	
	#get the name of the hebrew month in english
	def monthname(self):
		return self.heb_months[self.month]
	
	# get the hebrew year in hebrew digits
	def h_yearname(self):
		return self.int_to_hebrew (self.year)
	
	# get the hebrew year in arabic digits
	def yearname(self):
		return str (self.year)
	
	# gregorian date functions 
	
	# get the day of gregorian month
	def geo_yearname(self):
		return str (self.g_day)
	
	# get the name of the gregorian month in hebrew
	def h_geo_monthname(self):
		return self.h_geo_months[self.g_month - 1]
	
	# get the name of the gregorian month in english
	def geo_monthname(self):
		return self.geo_months[self.g_month - 1]
	
	# get the hebrew year in arabic digits
	def geo_yearname(self):
		return str (self.g_year)	
	
	# get a formated hebrew date in hebrew
	def h_date(self):
		return self.h_day_of_week_name() + " " + self.h_dayname() + " " + self.h_monthname() + " " + self.h_yearname()
	
	# get a formated hebrew date in english
	def date(self):
		return self.day_of_week_name() + " " + self.dayname() + " " + self.monthname() + " " + self.yearname()
	
	###############
	# int to hebrew
	###############
	
	# convert an int number 1..10000 to a utf-8 hebrew string
	def int_to_hebrew(self, number ):
		digits = [	" ","א","ב","ג","ד","ה","ו","ז","ח","ט",
						"ט","י","כ","ל","מ","נ","ס","ע","פ","צ",
						" ","ק","ר","ש","ת" ]
		
		h_number = "" 
		
		# sanity check
		if number<1 or number>10000 :
			return ""
		
		if number >= 1000 :
			h_number = digits [number/1000]
			number %= 1000
		
		while number >= 400 :
			h_number += digits [20+4]
			number -= 400
		
		if number >= 100 :
			h_number += digits [20+number/100]
			number %= 100
		
		if number >= 10 :
			if number == 15 or number == 16 :
				number -= 9
			h_number += digits [10+number/10] 
			number %= 10
		
		if number > 0 :
			h_number += digits [number]
		
		return h_number	
	
	#####################
	# size of hebrew year
	#####################
	
	#
	# no. of days in y hebrew years since 1,1,3744
	# Algorithm : Amos Shapir 1984 (rev. 1985, 1992) Yaacov Zmair 2003
	# by Yaacov Zamir 2003 (2004)
	#
	def days_from_start (self, y):
		
		l = y*7+1	# no. of leap months #
		m = y*12+l/19	# total no. of months #
		l %= 19
		
		nm = m*MONTH()+M(1+6,779)	# molad new year 3744 (16BC) + 6 hours #
		s = m*28+int(nm/DAY())-2
		
		nm %= WEEK()
		dw = int(nm/DAY())
		nm %= DAY()
		
		# special cases of Molad Zaken #
		if (l < 12 and dw==3 and nm>=M(9+6,204)) or (l < 7 and dw==2 and nm>=M(15+6,589)):
			s = s+1
			dw = dw+1
		# ADU #
		if dw == 1 or dw == 4 or dw == 6 :
			s = s+1
		return s
	
	############################
	# julean and date convertion
	############################
	
	#
	# compute Julian day (jd from Gregorian day, month and year (d, m, y)
	# Algorithm : 'Julian and Gregorian Day Numbers' by Peter Meyer 
	# from  Henry F. Fliegel and Thomas C. Van Flandern (1968)
	# by Yaacov Zamir 2003 (2004)
	#
	def gdate_to_jd (self, d, m, y):
		
		jd = ( 1461 * ( y + 4800 + int(( m - 14.0 ) / 12.0) ) ) / 4 	
		jd += ( 367 * ( int(m) - 2 - 12 * int( ( m - 14 ) / 12.0 ) ) ) / 12
		jd -= ( 3 * ( ( int(y) + 4900 + int(( m - 14 ) / 12.0) ) / 100 ) ) / 4
		jd += d - 32075
				
		return int(jd);
	
	#
	# compute date from Julian day
	# Algorithm : 'Julian and Gregorian Day Numbers' by Peter Meyer 
	# from  Henry F. Fliegel and Thomas C. Van Flandern (1968)
	# by Yaacov Zamir 2003 (2004)
	#
	def jd_to_gdate (self, jd):
		
		l = jd + 68569
		n = (4 * l) / 146097
		l = l - (146097 * n + 3) / 4
		i = (4000 * (l + 1)) / 1461001
		l = l - (1461 * i) / 4 + 31
		j = (80 * l) / 2447
		d = l - (2447 * j) / 80
		l = j / 11
		m = j + 2 - (12 * l)
		y = 100 * (n - 49) + i + l
		
		return (d, m, y)
	
	#
	# compute Julian day (jd from Hebrew day, month and year (d, m, y)
	# Algorithm : Amos Shapir 1984 (rev. 1985, 1992) Yaacov Zmair 2003
	# by Yaacov Zamir 2003 (2004)
	#
	# d - 1 first day of month
	# m - 1 tishrey, 13 - adar I, 14 - adar II
	# y - hebrew year
	#
	def hdate_to_jd (self, day, month, year):
		
		d = day - 1
		m = month - 1
		y = year
		
		# Adar I,II
		if (m > 11):
			m = 5
				
		# number of years since 3744
		y -= 3744;
		s = self.days_from_start(y)
		d += s
		
		# length of year
		s = self.days_from_start(y+1)-s
		
		# regular months
		d += int((59*m+1)/2)
		
		# special cases
		if(s%10>4 and m>1):	# long Heshvan
			d = d + 1
		if(s%10<4 and m>2):	# short Kislev
			d = d - 1
		if(s>365 and m>5):	# leap year
			d += 30
		
		jd = d + 1715119 # days since 1 Tishrei 3744 to julean 
				
		return jd
	
	#
	# Converting from the Julian day to the hebrew day
	# Algorithm : Amos Shapir 1984 (rev. 1985, 1992) Yaacov Zmair 2003
	# by Yaacov Zamir 2003 (2004)
	# 
	def jd_to_hdate (self, jd):
		
		l = jd + 68569
		n = ( 4 * l ) / 146097
		l = l - ( 146097 * n + 3 ) / 4
		i = ( 4000 * ( l + 1 ) ) / 1461001	# (that's 1,461,001)
		l = l - ( 1461 * i ) / 4 + 31
		j = ( 80 * l ) / 2447
		l = j / 11
		y = 100 * ( n - 49 ) + i + l	# Gregorian year
		
		d = jd - 1715119	# julean to days since 1 Tishrei 3744
		
		y += 16 # hebrew years since 3744
		s = self.days_from_start (y)
		m = self.days_from_start (y+1)
		
		while	d >= m: 	# computed year was underestimated #
			s = m
			y = y+1
			m = self.days_from_start (y+1)
		
		d -= s
		s = m-s	# size of current year #
		y += 3744
		
		# compute day and month #
		if d >= s-236:	# last 8 months are regular #
			d -= s-236
			m = d*2/59
			d -= (m*59+1)/2
			m += 4
			if s>365 and m<=5:	# Adar of Meuberet #
				m += 8
		else:
			#first 4 months have 117-119 days #
			s = 114+s%10
			m = d*4/s
			d -= (m*s+3)/4
			
		return d, m ,y
	
	#############################
	# get the name of the holyday
	#############################
	
	#
	# Gets the hebrew holyday in hebrew
	# by Yaacov Zamir 2003 (2004)
	#
	def h_holyday (self):
		
		day = self.day+1
		day_of_week = self.day_of_week
		
		if self.month == 0:    # Tishrey
			if day == 1: 
				return ("א ראש השנה")
			if day == 2: 
				return ("ב ראש השנה")
			if (day == 3) and (day_of_week !=6):
				return ("צום גדליה")
			if (day == 4) and (day_of_week ==0):
				return ("צום גדליה")
			if day == 10:
				return ("יום כיפור")
			if day == 15: 
				return ("סוכות")
			if (day > 15) and (day < 21):
				return ("חול המועד סוכות")
			if day == 21:
				return ("הושענה רבה")
			if day == 22:
				return ("שמחת תורה")
			return 1
		if self.month == 1:  # Heshvan
			return 1
		if self.month == 2:  # Kislev
			if (day > 24) and (day < 32):
				return ("חנוכה")
			return 1
		if self.month == 3: #Tevet
			if day < 3:
				return ("חנוכה")
			#if (hd_flg<0) and (day == 3):  # Hanukah in a short year 
			#	return ("חנוכה")
			if (day == 10) and (day_of_week !=0):
				return ("י בטבת")
			return 1
		if self.month == 4: # Shvat
			if day == 15: 
				return ("טו בשבת")
			return 1
		if self.month == 5: # Adar
			if (day == 11) and (day_of_week ==4):
				return ("תענית אסתר")
			if (day == 13) and (day_of_week !=6):
				return ("תענית אסתר")
			if day == 14:
				return ("פורים")
			if day == 15:
				return ("שושן פורים")
			return 1
		if self.month == 6: # Nisan
			if (day == 15):
				return ("פסח")
			if (day > 15) and (day < 22):
				return ("חול המועד פסח")
			return 1
		if self.month == 7: # Iyar
			if (day == 3) and (day_of_week == 4):
				return ("יום העצמאות")
			if (day == 4) and (day_of_week == 4):
				return ("יום העצמאות")
			if (day == 5) and (day_of_week != 5) and (day_of_week != 6):
				return ("יום העצמאות")
			if day == 18:
				return ("לג בעומר")
			return  1
		if self.month == 8: # Sivan
			if day == 5:
				return ("ערב שבועות")
			if day == 6:
				return ("שבועות")
			return  1
		if self.month == 9: # Tamuz
			if (day == 17) and (day_of_week !=6):
				return ("יז בתמוז")
			if (day == 18) and (day_of_week ==0):
				return ("יז בתמוז")
			return  1
		if self.month == 10: # Av
			if (day == 9) and (day_of_week !=6):
				return ("ט באב")
			if (day == 10) and (day_of_week ==0): 
				return ("ט באב")
			if day == 15:
				return ("טו באב")
			return  1
		if self.month == 11: # Elul
			return  1
		if self.month == 12: # Adar I
			return  1
		if self.month == 13: # Adar II
			if (day == 11) and (day_of_week ==4):
				return ("תענית אסתר")
			if (day == 13) and (day_of_week !=6):
				return ("תענית אסתר")
			if day == 14:
				return ("פורים")
			if day == 15:
				return ("שושן פורים")
			return 1
		return 1
	
	#
	# Gets the hebrew holyday in english
	# by Yaacov Zamir 2003 (2004)
	#
	def holyday (self):
		
		day = self.day+1
		day_of_week = self.day_of_week
		
		if self.month == 0:    # Tishrey
			if day == 1: 
				return ("New year I")
			if day == 2: 
				return ("New year II")
			if (day == 3) and (day_of_week !=6):
				return ("Gdalya fast")
			if (day == 4) and (day_of_week ==0):
				return ("Gdalya fast")
			if day == 10:
				return ("Yom Cipur")
			if day == 15: 
				return ("Sucut")
			if (day > 15) and (day < 21):
				return ("Hol hmoed Sucut")
			if day == 21:
				return ("Hoshana Raba")
			if day == 22:
				return ("Tora happiness")
			return 1
		if self.month == 1:  # Heshvan
			return 1
		if self.month == 2:  # Kislev
			if (day > 24) and (day < 32):
				return ("Hanuka")
			return 1
		if self.month == 3: #Tevet
			if day < 3:
				return ("Hanuka")
			#if (hd_flg<0) and (day == 3):  # Hanukah in a short year 
			#	return ("Hanuka")
			if (day == 10) and (day_of_week !=0):
				return ("10 of Tevet")
			return 1
		if self.month == 4: # Shvat
			if day == 15: 
				return ("15 of Shvat")
			return 1
		if self.month == 5: # Adar
			if (day == 11) and (day_of_week ==4):
				return ("Ester fast")
			if (day == 13) and (day_of_week !=6):
				return ("Ester fast")
			if day == 14:
				return ("Purim")
			if day == 15:
				return ("Shushan Purim")
			return 1
		if self.month == 6: # Nisan
			if (day == 15):
				return ("Pesah")
			if (day > 15) and (day < 22):
				return ("Hol hamoed Pesah")
			return 1
		if self.month == 7: # Iyar
			if (day == 3) and (day_of_week == 4):
				return ("Independs day")
			if (day == 4) and (day_of_week == 4):
				return ("Independs day")
			if (day == 5) and (day_of_week != 5) and (day_of_week != 6):
				return ("Independs day")
			if day == 18:
				return ("33 of Omer")
			return  1
		if self.month == 8: # Sivan
			if day == 5:
				return ("Shavot eve")
			if day == 6:
				return ("Shavot")
			return  1
		if self.month == 9: # Tamuz
			if (day == 17) and (day_of_week !=6):
				return ("17 Tamuz")
			if (day == 18) and (day_of_week ==0):
				return ("17 Tamuz")
			return  1
		if self.month == 10: # Av
			if (day == 9) and (day_of_week !=6):
				return ("9 of Av")
			if (day == 10) and (day_of_week ==0): 
				return ("9 of Av")
			if day == 15:
				return ("15 of Av")
			return  1
		if self.month == 11: # Elul
			return  1
		if self.month == 12: # Adar I
			return  1
		if self.month == 13: # Adar II
			if (day == 11) and (day_of_week ==4):
				return ("Ester fast")
			if (day == 13) and (day_of_week !=6):
				return ("Ester fast")
			if day == 14:
				return ("Purim")
			if day == 15:
				return ("Shushan Purim")
			return 1
		return 1

# Here are the functions
def func_hdate(day, month, year):
	'@FUNCTION=HDATE\n'\
	'@SYNTAX=HDATE (day, month, year)\n'\
	'@DESCRIPTION=Gets the Hebrew date for Gregorian date (day, month, year)'\
	'\n'\
	'@EXAMPLES=\n'\
	'HDATE(1,1,2001) equals \'Mo 6 Tevet 5761\'.'\
	'\n'\
	'@SEEALSO=HDAY, HMONTH, HYEAR, HDATE_TO_GEORGIAN, HDATE_HEB, HDATE_HOLYDAY, HDATE_HOLYDAY_HEB'
	
	hd = hdate(day, month, year)
	
	return hd.date()

def func_hday(day, month, year):
	'@FUNCTION=HDAY\n'\
	'@SYNTAX=HDAY (day, month, year)\n'\
	'@DESCRIPTION=Gets the Hebrew day for Gregorian date (day, month, year)'\
	'\n'\
	'@EXAMPLES=\n'\
	'HDAY(1,1,2001) equals \'6\'.'\
	'\n'\
	'@SEEALSO=HDATE, HMONTH, HYEAR, HDATE_TO_GEORGIAN, HDATE_HEB, HDATE_HOLYDAY, HDATE_HOLYDAY_HEB'
	
	hd = hdate(day, month, year)
	
	return (hd.day + 1)
	
def func_hmonth(day, month, year):
	'@FUNCTION=HMONTH\n'\
	'@SYNTAX=HMONTH (day, month, year)\n'\
	'@DESCRIPTION=Gets the Hebrew month for Gregorian date (day, month, year) '\
	'month = 1 is Tishrey, 13 is Adar I and 14 is Adar II\n'\
	'@EXAMPLES=\n'\
	'HMONTH(1,1,2001) equals \'4\'.'\
	'\n'\
	'@SEEALSO=HDATE, HDAY, HYEAR, HDATE_TO_GEORGIAN, HDATE_HEB, HDATE_HOLYDAY, HDATE_HOLYDAY_HEB'
	
	hd = hdate(day, month, year)
	
	return (hd.month + 1)

def func_hyear(day, month, year):
	'@FUNCTION=HYEAR\n'\
	'@SYNTAX=HYEAR (day, month, year)\n'\
	'@DESCRIPTION=Gets the Hebrew year for Gregorian date (day, month, year)'\
	'\n'\
	'@EXAMPLES=\n'\
	'HYEAR(1,1,2001) equals \'5761\'.'\
	'\n'\
	'@SEEALSO=HDATE, HDAY, HMONTH, HDATE_TO_GEORGIAN, HDATE_HEB, HDATE_HOLYDAY, HDATE_HOLYDAY_HEB'
	
	hd = hdate(day, month, year)
	
	return hd.year

def func_hdate_to_gregorian (day, month, year):
	'@FUNCTION=HDATE_TO_GEORGIAN\n'\
	'@SYNTAX=HDATE_TO_GEORGIAN (day, month, year)\n'\
	'@DESCRIPTION=Gets the Gregorian date for Hebrew date (day, month, year) '\
	'month = 1 is Tishrey, 13 is Adar I and 14 is Adar II\n'\
	'@EXAMPLES=\n'\
	'HDATE_TO_GEORGIAN(6,4,5761) equals \'1,1,2001\'.'\
	'\n'\
	'@SEEALSO=HDATE, HDAY, HMONTH, HYEAR, HDATE_HEB, HDATE_HOLYDAY, HDATE_HOLYDAY_HEB'
	
	hd = hdate(1, 1, 1970)
	hd.set_hdate (day, month, year)
	
	return str(hd.g_day) + "," +  str(hd.g_month) + "," + str(hd.g_year)

# The heb functions are not a locale thing, one my speek Franch or Hindo
# but will want to see the Hebrew date in Hebrew. 
def func_hdate_heb(day, month, year):
	'@FUNCTION=HDATE_HEB\n'\
	'@SYNTAX=HDATE_HEB (day, month, year)\n'\
	'@DESCRIPTION=Gets the Hebrew date (in hebrew) for Gregorian date (day, month, year)'\
	'\n'\
	'@EXAMPLES=\n'\
	'HDATE_HEB(1,1,2001) equals \'ב ו טבת התשסא\'.'\
	'\n'\
	'@SEEALSO=HDATE, HDAY, HMONTH, HYEAR, HDATE_TO_GEORGIAN, HDATE_HOLYDAY, HDATE_HOLYDAY_HEB'
	
	hd = hdate(day, month, year)
	
	return hd.h_date()

def func_hdate_holyday(day, month, year):
	'@FUNCTION=HDATE_HOLYDAY\n'\
	'@SYNTAX=HDATE_HOLYDAY (day, month, year)\n'\
	'@DESCRIPTION=Gets the Hebrew holyday on Gregorian date (day, month, year)'\
	'\n'\
	'@EXAMPLES=\n'\
	'HDATE_HOLYDAY(16,9,2004) equals \'New Year I\'.'\
	'\n'\
	'@SEEALSO=HDATE, HDAY, HMONTH, HYEAR, HDATE_TO_GEORGIAN, HDATE_HEB, HDATE_HOLYDAY_HEB'
	
	hd = hdate(day, month, year)
	
	if (hd.holyday() == 1):
		return "None"
	else:
		return hd.holyday()

def func_hdate_holyday_heb(day, month, year):
	'@FUNCTION=HDATE_HOLYDAY_HEB\n'\
	'@SYNTAX=HDATE_HOLYDAY_HEB (day, month, year)\n'\
	'@DESCRIPTION=Gets the Hebrew holyday (in hebrew) on Gregorian date (day, month, year)'\
	'\n'\
	'@EXAMPLES=\n'\
	'HDATE_HOLYDAY_HEB(1,1,2001) equals \'א ראש השנה\'.'\
	'\n'\
	'@SEEALSO=HDATE, HDAY, HMONTH, HYEAR, HDATE_TO_GEORGIAN, HDATE_HEB, HDATE_HOLYDAY'
	
	hd = hdate(day, month, year)
	
	if (hd.h_holyday() == 1):
		return "None"
	else:
		return hd.h_holyday()

# functions table
hdate_functions = {
	'hdate':   ('fff', 'day, month, year', func_hdate),
	'hday':   ('fff', 'day, month, year', func_hday),
	'hmonth':   ('fff', 'day, month, year', func_hmonth),
	'hyear':   ('fff', 'day, month, year', func_hyear),
	'hdate_to_gregorian':   ('fff', 'day, month, year', func_hdate_to_gregorian),
	'hdate_heb':   ('fff', 'day, month, year', func_hdate_heb),
	'hdate_holyday':   ('fff', 'day, month, year', func_hdate_holyday),
	'hdate_holyday_heb':   ('fff', 'day, month, year', func_hdate_holyday_heb)
}
