"""
$Id: psionAgenda.py,v 1.2 1998/09/27 04:31:11 connolly Exp $

PSIONICS FILE - AGENDA3A.FMT
============================
Format of Series 3a, 3c, and Siena Agenda files
Last modified 1997-08-04
http://www.davros.org//psion/psionics/agenda3a.fmt
"""

from struct import unpack, calcsize
from time import localtime, gmtime, strftime
import psionData, psionWord
from psionData import readStruct, readQstr, debug

class Entry(psionData.Record):
    def __init__(self, parent, ty, siz):
	self.siz = siz

    # in Modula-3, this would be
    # Set of {Repeating, CrossedOut, YearView, AlarmFollows, Memo}
    Repeating = (1<<0)
    CrossedOut = (1<<1)
    YearView = (1<<2)
    AlarmFollows = (1<<3)
    Memo = (1 << 4)
    
    def loadAttrs(self, stream, attrs):
	if attrs & self.AlarmFollows == 0:
            self.loadAlarm(stream)
	if attrs & self.Memo == 0:
            m = Memo()
            m.load(stream)
            self.memo = m

    def loadAlarm(self, stream):
            # "alarm time in minutes before 23:59 of the day the event
            # occurs on (0 to 46079, meaning 00:00 31 days earlier)"
	    (self.alarmTime, qty) = readStruct('hb', stream)
            if qty == 1:
                (self.alarmIndex,) = readStruct('b', stream)
                self.alarmName = None
                stream.read(7)
            else:
                self.alarmName = stream.read(8)[:qty]
                self.alarmIndex = None

class Memo(psionWord.T):
    def load(self, stream):
        sizA = 10
        (L, w2, sizD) = readStruct('hhh', stream)
        sizAtoC = w2 % (1<<12)
        crypFlag = w2 / (1<<12) # 4: unencrypted 12: encrypted
        self.A = stream.read(10)
        if crypFlag == 12:
            sizB = 18
            self.B = stream.read(sizB)
        else:
            sizB = 0
            self.B = ''
        sizC = sizAtoC - sizA - sizB
        text = psionWord.Text(self, 8, sizC)
        text.load(stream)
        fmt = psionWord.Format(self, 9, sizD)
        fmt.load(stream)

class TimedDayEntry(Entry):

#Type 1 (timed day entry):
#  Offset  0 (word): day number of the entry
#  Offset  2 (word): time of entry, in minutes past midnight
#  Offset  4 (byte): attributes (see below)
#  Offset  5 (byte): symbol in year view (0 to 31 mean do not show)
#  Offset  6 (word): length in minutes
#  Offset  8 (byte): text style (see below)
#  Offset  9 (qstr): text

    fmt = 'HHBcHB'
    fmtQty = calcsize(fmt)

    def load(self, stream):
	(dayNum, time, attrs, yearSym, duration, sty) = \
		 readStruct(self.fmt, stream)

	text = readQstr(stream)

	debug(isotime(gmtime(d2s(dayNum) + time*60)),
              'timed day entry', attrs, 
	      yearSym, duration, sty, text)

	self.loadAttrs(stream, attrs)

class UntimedDayEntry(Entry):

    fmt = 'HHBcB'
    fmtQty = calcsize(fmt)

    def load(self, stream):
	(dayNum, timeShown, attrs, yearSym, sty) = \
		 readStruct(self.fmt, stream)

	text = readQstr(stream)

	debug(isotime(gmtime(d2s(dayNum) )),
              'untimed day entry', attrs, 
	      yearSym, sty, text)

	self.loadAttrs(stream, attrs)

class Anniversary(Entry):
    ShowBaseYear = (1<<0)
    ShowElapsedYears = (1<<1)
                        
    fmt = 'HHBBHBB'
    fmtQty = calcsize(fmt)

    def load(self, stream):
	(dayNum, timeShown, attrs, entryCode, startYear, yearFlags,
         sty) = readStruct(self.fmt, stream)

	text = readQstr(stream)

	debug('ann day,tim,attrs,code,year,flags,sty,text', dayNum, timeShown, attrs, chr(entryCode), startYear, yearFlags, sty, text)

        if dayNum <> 65535:
            debug('ann day', isotime(gmtime(d2s(dayNum) )))
	self.loadAttrs(stream, attrs)

class ToDoEntry(Entry):
    fmt = 'HHBcHBB'
    fmtQty = calcsize(fmt)

    def load(self, stream):
	(dayNum, time, attrs, yearSym, doneBy, listIndex, priDataFmt) =\
		 readStruct(self.fmt, stream)

	# watch out for alignment...
	(posn, style) = readStruct('lb', stream)
	text = readQstr(stream)

	debug('todo entry', dayNum, time, attrs, 
	      yearSym, doneBy, listIndex, priDataFmt, posn,
	      style, text)
        if doneBy <> 65535:
            debug('todo by', isotime(gmtime(d2s(doneBy)))[:10])
        if dayNum <> 65535:
            debug('todo show day/time', isotime(gmtime(d2s(dayNum) + time*60)))
	
	self.loadAttrs(stream, attrs)

class Repeat(psionData.Record):
    def __init__(self, parent, ty, siz):
        self.siz = siz
        self.agenda = parent

    Daily = 0
    Weekly = 1
    MonthlyByDate = 2
    MonthlyByDays = 3
    Annually = 4

    NextOnly = (1 << 3)

    fmt = 'BBHB'
    fmtQty = calcsize(fmt)

    def load(self, stream):
        left = self.siz
        (self.period, self.interval, self.endingDay, self.entryType) = \
                      readStruct(self.fmt, stream)
        self.nextOnly = self.period/8
        self.period = self.period % 8
        self.interval = self.interval + 1
        debug('repeat: per, nextOnly, int, endday, ty',
              self.period, self.nextOnly, self.interval, self.nextOnly,
              self.endingDay,
              self.entryType)
        if self.endingDay <> 65535:
            debug('endday', isotime(gmtime(d2s(self.endingDay))))
        left = left - self.fmtQty
        
        if self.period is self.Weekly:
            (self.dayBits, self.firstDay) = readStruct('bb', stream)
            left = left - 2
        elif self.period is self.MonthlyByDate:
            (self.dates) = readStruct('L', stream)
            left = left - 4
        elif self.period is self.MonthlyByDays:
            (self.first, self.second, self.third, self.fourth, self.last) = \
                         readStruct('bbbbb', stream)
            left = left - 5
            

        (self.position, ) = readStruct('L', stream)
        self.agenda.repeats[self.position] = self
        left = left - 4
        self.suppress = []
        
        while left > 0:
            self.suppress.append(readStruct('h', stream))
            left = left - 2
        debug('repeat supp', left, self.suppress)
        
class T(psionData.DBF):
    recTypes = {
        # 0: Deleted,
        1: TimedDayEntry,
        2: UntimedDayEntry,
        3: Anniversary,
        4: ToDoEntry,
        5: Repeat,
        # 6: AnonymousData,
        # 7: Reserved,
        # 8: EntryCodeDescriptiveRecord,
        # 9: TodoListDefinition,
        }
    def factory(self, ty):
        try:
            debug(ty, self.recTypes[ty])
            return self.recTypes[ty]
        except KeyError:
            return psionData.Record

    def __init__(self):
        #psionData.DBF.__init__(self)
        self.repeats = {}

# Utilities

def d2s(daynum):
    # assume 1970 bias in both systems
    return 60*60*24*daynum

def isotime(ttup):
    return strftime("%Y-%m-%dT%H:%M:%S", ttup)

def test():
    import sys

    a = T()
    a.load(sys.stdin)

if __name__ == '__main__': test()
