/* datebook.c:  Translate Pilot datebook data formats
 *
 * Copyright (c) 1996, Kenneth Albanowski
 *
 * This is free software, licensed under the GNU Public License V2.
 * See the file COPYING for details.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "pi-source.h"
#include "pi-socket.h"
#include "pi-dlp.h"
#include "pi-datebook.h"

char * DatebookAlarmTypeNames[] =
	{ "Minutes", "Hours", "Days", NULL };

char * DatebookRepeatTypeNames[] =
	{ "None", "Daily", "Weekly", "MonthlyByDay", "MonthlyByDate", "Yearly", NULL };

/* dom1stSun = REM Sun 1  
 dom1stMon = Rem Mon 1 
 dom2ndSun = REM Sun 8 
 domLastSun = REM Sun 1 -7*/

void free_Appointment(struct Appointment * a) {
  if(a->exception)
    free(a->exception);
  if(a->description)
    free(a->description);
  if(a->note)
    free(a->note);
}

int unpack_Appointment(struct Appointment * a, unsigned char * buffer, int len) {
  int iflags;
  unsigned char * p2;
  unsigned long d;
  int j;
  int destlen;
  
  /* Note: There are possible timezone conversion problems related to the
           use of the begin, end, repeatEnd, and exception[] members of a
           struct Appointment. As they are kept in local (wall) time in
           struct tm's, the timezone of the Pilot is irrelevant, _assuming_
           that any UNIX program keeping time in time_t's converts them to
           the correct local time. If the Pilot is in a different timezone
           than the UNIX box, it may not be simple to deduce that correct
           (desired) timezone.
           
           The easiest solution is to keep apointments in struct tm's, and
           out of time_t's. Of course, this might not actually be a help if
           you are constantly darting across timezones and trying to keep
           appointments.
                                                                    -- KJA
           */
  
  destlen = 8;
  if (len<destlen)
    return 0;
  
  a->begin.tm_hour = get_byte(buffer);
  a->begin.tm_min = get_byte(buffer+1);
  a->begin.tm_sec = 0;
  d = (unsigned short int)get_short(buffer+4);
  a->begin.tm_year = (d >> 9) + 4;
  a->begin.tm_mon = ((d >> 5) & 15) - 1;
  a->begin.tm_mday = d & 31;
  a->begin.tm_isdst = -1;
  a->end = a->begin;

  a->end.tm_hour = get_byte(buffer+2);
  a->end.tm_min = get_byte(buffer+3);
	
  if(get_short(buffer) == 0xffff) {
    a->event = 1;
    a->begin.tm_hour = 0;
    a->begin.tm_min = 0;
    a->end.tm_hour = 0;
    a->end.tm_min = 0;
  } else {
    a->event = 0;
  }
  
  mktime(&a->begin);
  mktime(&a->end);
	  
  iflags = get_byte(buffer+6);

  /* buffer+7 is gapfil */
	
	p2 = (char*)buffer+8;
	
#define alarmFlag 64
#define repeatFlag 32
#define noteFlag 16
#define exceptFlag 8
#define descFlag 4
	
	if (iflags & alarmFlag) 
		{
		a->alarm = 1;
		a->advance = get_byte(p2);
		p2+=1;
		a->advanceUnits = get_byte(p2);
		p2+=1;
		
		}
	else {
		a->alarm = 0;
		a->advance = 0;
		a->advanceUnits = 0;
	}
		
	if (iflags & repeatFlag)
		{
		int i,	on;
		a->repeatType = get_byte(p2); p2+=2;
		d = (unsigned short int)get_short(p2); p2+=2;
		if(d==0xffff)
			a->repeatForever=1; /* repeatEnd is invalid */
		else {
			a->repeatEnd.tm_year = (d >> 9) + 4;
			a->repeatEnd.tm_mon = ((d >> 5) & 15) - 1;
			a->repeatEnd.tm_mday = d & 31;
			a->repeatEnd.tm_min = 0;
			a->repeatEnd.tm_hour = 0;
			a->repeatEnd.tm_sec = 0;
			a->repeatEnd.tm_isdst = -1;
			mktime(&a->repeatEnd);
			a->repeatForever = 0;
		}
		a->repeatFrequency = get_byte(p2); p2++;
		on = get_byte(p2); p2++;
		a->repeatDay = 0;
		for(i=0;i<7;i++)
			a->repeatDays[i] = 0;
			
		if (a->repeatType == repeatMonthlyByDay)
			a->repeatDay = on;
		else if (a->repeatType == repeatWeekly)
			for(i=0;i<7;i++)
				a->repeatDays[i] = !!(on & (1<<i));
		a->repeatWeekstart = get_byte(p2); p2++;
		p2++;
		}
	else {
		int i;
		a->repeatType = 0;
		a->repeatForever = 1; /* repeatEnd is invalid */
		a->repeatFrequency = 0;
		a->repeatDay = 0;
		for(i=0;i<7;i++)
		  a->repeatDays[i] = 0;
		a->repeatWeekstart = 0;
	}

	if (iflags & exceptFlag)
		{
		a->exceptions = get_short(p2);p2+=2;
		a->exception = malloc(sizeof(struct tm)*a->exceptions);
		
		for(j=0;j<a->exceptions;j++,p2+=2) {
			d = (unsigned short int)get_short(p2);
			a->exception[j].tm_year = (d >> 9) + 4;
			a->exception[j].tm_mon = ((d >> 5) & 15) - 1;
			a->exception[j].tm_mday = d & 31;
			a->exception[j].tm_hour = 0;
			a->exception[j].tm_min = 0;
			a->exception[j].tm_sec = 0;
			a->exception[j].tm_isdst = -1;
			mktime(&a->exception[j]);
		}
		
		}
	else  {
		a->exceptions = 0;
		a->exception = 0;
	}

	if (iflags & descFlag)
	{
		a->description = strdup(p2);
		p2 += strlen(p2) + 1;
	} else
		a->description = 0;

	if (iflags & noteFlag)
	{
		a->note = strdup(p2);
		p2 += strlen(p2) + 1;
	}
	else {
		a->note = 0;
	}
  return (p2-buffer);
}

int pack_Appointment(struct Appointment *a,
                      unsigned char *buf,
                      int len)
{
    int iflags;
    char *pos;
    int destlen = 8;
    
    if (a->alarm)
      destlen+=2;
    if (a->repeatType)
      destlen+=8;
    if (a->exceptions)
      destlen+=2+2*a->exceptions;
    if (a->note)
      destlen+=strlen(a->note)+1;
    if (a->description)
      destlen+=strlen(a->description)+1;
    
    if (!buf)
      return destlen;
    if (len<destlen)
      return 0;

    set_byte(buf, a->begin.tm_hour);
    set_byte(buf+1, a->begin.tm_min);
    set_byte(buf+2, a->end.tm_hour);
    set_byte(buf+3, a->end.tm_min);
    set_short(buf+4, ((a->begin.tm_year - 4) << 9) |
              ((a->begin.tm_mon  + 1) << 5) |
              a->begin.tm_mday);
    
    if (a->event)
    {
        set_long(buf, 0xffffffff);
    }

#define alarmFlag 64
#define repeatFlag 32
#define noteFlag 16
#define exceptFlag 8
#define descFlag 4

    iflags = 0;
    
    pos = (char *)buf + 8;

    if (a->alarm)
    {
        iflags |= alarmFlag;
        
        set_byte(pos, a->advance);
        set_byte(pos+1, a->advanceUnits);
        pos+=2;
    }
    
    if (a->repeatType) {
    	int on,i;

        iflags |= repeatFlag;
        
        if (a->repeatType == repeatMonthlyByDay)
            on = a->repeatDay;
        else if (a->repeatType == repeatWeekly) {
            on = 0;
	    for (i=0;i<7;i++)
	         if (a->repeatDays[i])
	             on |= 1<<i;
	} else
	    on = 0;

    	set_byte(pos, a->repeatType);
    	set_byte(pos+1, 0);
    	pos+=2;
    	
    	if (a->repeatForever)
    	    set_short(pos, 0xffff);
    	else
    	    set_short(pos, ((a->repeatEnd.tm_year - 4) << 9) |
                ((a->repeatEnd.tm_mon  + 1) << 5) |
                a->repeatEnd.tm_mday);
        
        pos+=2;
        
        set_byte(pos, a->repeatFrequency); pos++;
        set_byte(pos, on); pos++;
        set_byte(pos, a->repeatWeekstart); pos++;
        set_byte(pos, 0); pos++;
    }
    
    if (a->exceptions) {
        int i;
        
        iflags |= exceptFlag;
        
        set_short(pos, a->exceptions); pos+=2;
        
        for(i=0;i<a->exceptions;i++,pos+=2)
    	    set_short(pos, ((a->exception[i].tm_year - 4) << 9) |
                ((a->exception[i].tm_mon  + 1) << 5) |
                a->exception[i].tm_mday);
    }

    if (a->description != NULL)
    {
        iflags |= descFlag;
        
        strcpy(pos, a->description);
        pos += strlen(pos) + 1;
    }
    
    if (a->note != NULL)
    {
        iflags |= noteFlag;
        
        strcpy(pos, a->note);
        pos += strlen(pos) + 1;
    }

    set_byte(buf+6, iflags);
    set_byte(buf+7, 0); /* gapfil */

    return ((long)pos - (long)buf);
}

                  
int unpack_AppointmentAppInfo(struct AppointmentAppInfo * ai, unsigned char * record, int len) {
  int i;
  i = unpack_CategoryAppInfo(&ai->category, record, len);
  if (!i)
    return 0;
  record += i;
  len -= i;
  if (len<2)
    return 0;
  ai->startOfWeek = get_byte(record);
  return i+2;
}

int pack_AppointmentAppInfo(struct AppointmentAppInfo * ai, unsigned char * record, int len) {
  int i;
  unsigned char * start = record;
  
  i = pack_CategoryAppInfo(&ai->category, record, len);
  if (!record)
    return i+2;
  if (!i)
    return i;
  record += i;
  len -= i;
  if (i<2)
    return 0;
  set_short(record, 0);
  set_byte(record, ai->startOfWeek);
  record+=2;
  
  return (record-start);
}
