/*
 * FreeTime: Acorn Time Client
 * Daylight saving handling code
 *
 *  Joseph Heenan, 1996-8
 * All rights reserved.
 *
 * $Id: dst,v 1.2 1999/10/16 14:38:33 joseph Exp $
 *
 */

/*
 * The Seventh Directive (94/21/EC) of the European Parliament and of the
 * Council of 30 May 1994 defined the start and end dates of summer time
 * throughout the EEC, using a new algorithm with effect from 1996 (the
 * algorithm being to start on the last Sunday in March, and end on the last
 * Sunday in October, always at 01:00 GMT).
 */

/* Ie. Work out:
   bst_start : time_t at 0100GMT on the last Sunday in March
   bst_end   : time_t at 0100GMT on the last Sunday in October

   If bst_start <= time && time < BST_END, BST should be 1.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "swis.h"

#include "dst.h"

static _kernel_oserror *dst_read( int *state )
{
  _kernel_oserror *e;
  int val;

  e = _swix( OS_Byte, _INR( 0, 1 ) | _OUT(2), 161, 220, &val );

  if ( e )
    return e;

  *state = (val & 128 ) != 0;

  return NULL;
}


static _kernel_oserror *dst_set( int state )
{
  _kernel_oserror *e;
  int val;

  e = _swix( OS_Byte, _INR( 0, 1 ) | _OUT(2), 161, 220, &val );

  if ( e )
    return e;

  val = (val & ~128 ) | (state ? 128 : 0 );

  e = _swix( OS_Byte, _INR( 0, 2 ), 162, 220, val );

  if ( e )
    return e;

  return NULL;
}

/* Returns -1 on an error */
int dst_check( char **msg )
{
  time_t bst_start;
  time_t bst_end;
  struct tm tmptm;
  struct tm *year;
  time_t now = time( NULL );
  int dst;

  *msg = 0;

  year = gmtime( &now );
  if ( !year )
  {
    *msg = "Could not read current year";
    return -1;
  }

  /* First we find out which day the last day in March this year is */
  memset( &tmptm, 0, sizeof tmptm );
  tmptm.tm_sec = 0;
  tmptm.tm_min = 0;
  tmptm.tm_hour = 1; /* 01:00 GMT */
  tmptm.tm_mday = 31; /* last day */
  tmptm.tm_mon = 3 - 1; /* March (0 to 11) */
  tmptm.tm_year = year->tm_year; /* this year */

  bst_start = mktime( &tmptm );

  tmptm.tm_mday -= tmptm.tm_wday; /* subtract so we get back to the previous Sunday */
  bst_start = mktime( &tmptm );

  /* Now we find out which day the last day in October this year is */
  memset( &tmptm, 0, sizeof tmptm );
  tmptm.tm_sec = 0;
  tmptm.tm_min = 0;
  tmptm.tm_hour = 1; /* 01:00 GMT */
  tmptm.tm_mday = 31; /* last day */
  tmptm.tm_mon = 10 - 1; /* October (0 to 11) */
  tmptm.tm_year = year->tm_year; /* this year */

  bst_end = mktime( &tmptm );

  tmptm.tm_mday -= tmptm.tm_wday; /* subtract so we get back to the previous Sunday */
  bst_end = mktime( &tmptm );

  if ( dst_read( &dst ) )
  {
    *msg = "Error reading DST state";
    return -1;
  }

  if ( bst_start <= now && now < bst_end )
  {
    if ( dst )
      return 0; /* *msg = NULL */

    dst_set( 1 );
    *msg = "Clock set to BST";
    return 0;
  }

  if ( !dst )
    return 0; /* *msg = NULL */

  dst_set( 0 );
  *msg = "Clock set to GMT";
  return 0;
}

#ifdef TEST

int main( void )
{
  char *msg;
  _kernel_oserror *e;
  int state;

  e = dst_read( &state );
  printf("bst = %d, err = %p\n", state, e );

  e = dst_set( 1 );
  printf("err = %p\n", e );

  e = dst_read( &state );
  printf("bst = %d, err = %p\n", state, e );

  dst_check( &msg );
  if ( msg )
    printf("%s\n",msg);
  else
    printf("No change\n");

  e = dst_set( 0 );
  printf("err = %p\n", e );

  e = dst_read( &state );
  printf("bst = %d, err = %p\n", state, e );

  dst_check( &msg );
  if ( msg )
    printf("%s\n",msg);
  else
    printf("No change\n");

  return 0;
}


#endif
