#
#  $Id: Datebook.py,v 1.2 1999/12/11 12:35:11 rob Exp $
#
#  Copyright 1998-1999 Rob Tillotson <robt@debian.org>
#  All Rights Reserved
#
#  Permission to use, copy, modify, and distribute this software and
#  its documentation for any purpose and without fee or royalty is
#  hereby granted, provided that the above copyright notice appear in
#  all copies and that both the copyright notice and this permission
#  notice appear in supporting documentation or portions thereof,
#  including modifications, that you you make.
#
#  THE AUTHOR ROB TILLOTSON DISCLAIMS ALL WARRANTIES WITH REGARD TO
#  THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
#  AND FITNESS.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
#  SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
#  RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
#  CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
#  CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE!
#
"""Date Book.
"""

__version__ = '$Id: Datebook.py,v 1.2 1999/12/11 12:35:11 rob Exp $'

__copyright__ = 'Copyright 1998-1999 Rob Tillotson <robt@debian.org>'

import string, struct
import Pyrite
import Pyrite.Connector
from Pyrite import FLD_TIME_T, FLD_INT, FLD_UNKNOWN, FLD_STRING, _
from Pyrite import Blocks

class Connector(Pyrite.Connector.Connector):
    name = 'Datebook'
    version = Pyrite.version
    author = Pyrite.author
    url = ''
    description = _("The built in date book.")

    def __init__(self, *a, **kw):
	apply(Pyrite.Connector.Connector.__init__, (self,)+a, kw)
	self.default_name = 'DatebookDB'
	self.default_class = Database
	self.default_creator = 'date'
	self.default_type = 'DATA'
	
    def classify(self, info = {}):
	if info.get('type') == 'DATA' and info.get('creator') == 'date':
	    return Database
	

_fields = { 'begin': (FLD_TIME_T, (0,0,0,0,0,0,0,0,0)),
	    'end': (FLD_TIME_T, None),
	    'alarm': (FLD_TIME_T, None),
	    'repeatWeekStart': (FLD_INT, 0),
	    'repeat': (FLD_UNKNOWN, None),
	    'repeatEnd': (FLD_UNKNOWN, None),
	    'exceptions': (FLD_UNKNOWN, None),
	    'description': (FLD_STRING, ''),
	    'note': (FLD_STRING, '')
	    }

alarmTypeNames = ['Minutes', 'Hours', 'Days']
repeatTypeNames = ['None', 'Daily', 'Weekly', 'MonthlyByDay',
		   'MonthlyByDate', 'Yearly']
repeatNone = 0
repeatDaily = 1
repeatWeekly = 2
repeatMonthlyByDay = 3
repeatMonthlyByDate = 4
repeatYearly = 5

alarmFlag = 64
repeatFlag = 32
noteFlag = 16
exceptFlag = 8
descFlag = 4

class Record(Blocks.Record):
    def __init__(self, *a, **kw):
	self.fields = _fields
	apply(Pyrite.Record.__init__, (self,)+a, kw)

    def unpack(self, raw):
	self.raw = raw

	bh, bm, eh, em, d, flags = struct.unpack('>BBBBHbx', raw[:8])

	if bh == 0xff and bm == 0xff:
	    ev = 1
	    bh = bm = eh = em = 0
	else: ev = 0
	
	self.data['begin'] = Blocks.palm_date_to_tuple(d, bh, bm)
	if ev: self.data['end'] = None
	else: self.data['end'] = Blocks.palm_date_to_tuple(d, eh, em)

	raw = raw[8:]

	if flags & alarmFlag:
	    adv, au = struct.unpack('>bb', raw[:2])
	    self.data['alarm'] = (adv, alarmTypeNames[au])
	    raw = raw[2:]
	else:
	    self.data['alarm'] = None

	if flags & repeatFlag:
	    rtype, d, rfreq, on, rws = struct.unpack('>bxHbbbx', raw[:8])
	    if d == 0xffff:
		forever = 1
	    else:
		forever = 0
		end = Blocks.palm_date_to_tuple(d)

	    rday = 0
	    rdays = [0]*7
	    if rtype == repeatMonthlyByDay:
		rday = on
	    elif rtype == repeatWeekly:
		for i in range(0, 7):
		    if on & (1 << i): rdays[i] = 1

	    self.data['repeatWeekStart'] = rws
	    if rtype == repeatWeekly:
		ri = tuple(rdays)  # this might be better as a list?
	    elif rtype == repeatMonthlyByDay:
		if rday/7 >= 4: ri = (-1,)
		else: ri = (rday/7+1,)
		# FIXME translate?
		ri = ri + (('Sun','Mon','Tue','Wed','Thu','Fri','Sat')[rday % 7],)
	    else:
		ri = None

	    if rtype == repeatNone:
		self.data['repeat'] = None
	    else:
		self.data['repeat'] = (rfreq, repeatTypeNames[rtype], ri)

	    if forever:
		self.data['repeatEnd'] = None
	    else:
		self.data['repeatEnd'] = end

	    raw = raw[8:]
	else:
	    self.data['repeat'] = None
	    self.data['repeatEnd'] = None
	    self.data['repeatWeekStart'] = 0

	if flags & exceptFlag:
	    n = struct.unpack('>h',raw[:2])[0]
	    raw = raw[2:]
	    exc = []

	    for x in range(0, n):
		d = struct.unpack('>H', raw[:2])[0]
		raw = raw[2:]
		exc.append(Blocks.palm_date_to_tuple(d))
	    self['exceptions'] = exc
	else:
	    self['exceptions'] = None

	if flags & descFlag:
	    s, raw = string.split(raw, '\0', 1)
	    self['description'] = s
	else:
	    self['description'] = None

	if flags & noteFlag:
	    s, raw = string.split(raw, '\0', 1)
	    self['note'] = s
	else:
	    self['note'] = None
	    
	    
    def pack(self):
	date = Blocks.tuple_to_palm_date(self.data['begin'])
	if self.data['end'] is None:
	    bh = bm = eh = em = 0xff
	else:
	    bh = self.data['begin'][3]
	    bm = self.data['begin'][4]
	    eh = self.data['end'][3]
	    em = self.data['end'][4]

	flags = 0
	raw = ''
	if self.data['alarm'] is not None:
	    flags = flags | alarmFlag
	    raw = raw + struct.pack('>bb', self.data['alarm'][0],
				    alarmTypeNames.index(self.data['alarm'][1]))
	    
	# ?? can repeat == None while still having some repeat data?
	if self.data['repeat'] is not None or \
	   self.data['repeatEnd'] is not None:
	    flags = flags | repeatFlag

	    if self.data['repeatEnd'] is not None:
		d = Blocks.tuple_to_palm_date(self.data['repeatEnd'])
	    else:
		d = 0xffff
		
	    if self.data['repeat']:
		rfreq, rtname, ri = self.data['repeat'][:3]
		rtype = repeatTypeNames.index(rtname)
	    else:
		rfreq = 0
		rtname = ri = None
		rtype = 0
	    rws = self.data['repeatWeekStart']

	    on = 0
	    if rtype == repeatWeekly:
		for i in range(0, 7):
		    if ri[i]: on = on | (1 << i)
	    elif rtype == repeatMonthlyByDay:
		# FIXME translate?
		rday = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'].index(ri[1])
		if ri[0] == -1:
		    rday = 7*4+rday
		else:
		    rday = 7*(ri[0]-1)+rday
		on = rday

	    raw = raw + struct.pack('>bxHbbbx', rtype, d, rfreq, on, rws)
	    
	    
	if self.data['exceptions'] is not None:
	    flags = flags | exceptFlag
	    raw = raw + struct.pack('>h', len(self.data['exceptions'])+1)
	    for d in self.data['exceptions']:
		raw = raw + struct.pack('>H', Blocks.tuple_to_palm_date(d))
		
	if self.data['description'] is not None:
	    flags = flags | descFlag
	    raw = raw + str(self.data['description']) + '\0'
	    
	if self.data['note'] is not None:
	    flags = flags | noteFlag
	    raw = raw + str(self.data['note']) + '\0'

	raw = struct.pack('>BBBBHbx', bh, bm, eh, em, date, flags) + raw
	self.raw = raw
	return raw
    

class AppBlock(Blocks.CategoryAppBlock):
    def __init__(self, *a, **kw):
	self.fields = { 'startOfWeek': (FLD_INT, 0) }
	apply(Blocks.CategoryAppBlock.__init__, (self,)+a, kw)

    def unpack(self, raw):
	self.raw = raw
	raw = Blocks.CategoryAppBlock.unpack(self, raw)
	if len(raw) >= 2:
	    self.data['startOfWeek'] = int(struct.unpack('>B', raw[0])[0])

    def pack(self):
	raw = Blocks.CategoryAppBlock.pack(self)
	raw = raw + struct.pack('>Bb', self.data['startOfWeek'], 0)
	self.raw = raw
	return self.raw


class Database(Pyrite.Database):
    def __init__(self, *a, **kw):
	apply(Pyrite.Database.__init__, (self,)+a, kw)
	self.record_class = Record
	self.appblock_class = AppBlock



