/*
    $Header: Welmat:src/RCS/lotek.c,v 1.20 92/09/12 20:43:18 rwm Exp Locker: rwm $

    Perform a Lotek/DietIFNA session

    Copyright (C) 1988,1989,1990 Michael Richardson
    Copyright (C) 1992 Russell McOrmond

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/


#ifdef RCSID
static char RCSid[]="$Id: lotek.c,v 1.20 92/09/12 20:43:18 rwm Exp Locker: rwm $\n";
#endif

#include <proto/all.h>
#include <exec/types.h>
#include <exec/memory.h>
#include <time.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "xproto.h"
#include "xprclock.h"

struct ExecBase *SysBase=(struct ExecBase *)4l;
extern struct DosLibrary *DOSBase;


static char *months[]={"Jan", "Feb", "Mar", "Apr",
                       "May", "Jun", "Jul", "Aug",
                       "Sep", "Oct", "Nov", "Dec" };


long  __saveds XProtocolSend(struct XPR_IO *io) {
  struct Vars *v;
  unsigned int i,state,dtime, ltime = 120;
  long timeout, dummy, tstart, time(),gotit=FALSE;
  int hours, minutes, seconds;
  unsigned char buffer[256];
  int tday, day, month;

  if(!(DOSBase=(struct DosLibrary *)OpenLibrary("dos.library",0)))
    return XPRS_FAILURE;

  /* Perform common setup and initializations */
  if (!(v = setup(io)))  return XPRS_FAILURE;

  SPrintF(v->scratch,"Year: %ld, Adjust: %ld",v->year, v->adjust);
  updmsg(v,v->scratch);
  updmsg(v,"Waiting for Atomic Clock time signal...");
  state=i=xpr_sread(&v->io,buffer,9,10000000); /* wait a maximum of 10 seconds */
  if(state==NODCD) {
    upderr(v,"Carrier Dropped");
    goto exit;
  }
  if(i==9) {
    updmsg(v,"Getting there.");
    timeout = time( &dummy) + 60l;
    while(((dtime = (timeout - time(&dummy))) > 0) && (i<255)) {
      if (ltime != dtime) {
        SPrintF(v->scratch,"\r%02ld", dtime);
        updmsg(v,v->scratch);
        ltime = dtime;
      }
      state=xpr_sread(&v->io,&buffer[i],1,10000000);
      if(state==NODCD) {
        upderr(v,"Carrier Dropped");
        goto exit;
      }
      if (((buffer[i-4]&0xf) == 6) &&
        (buffer[i] != buffer[i-10]) &&
        (buffer[i] == buffer[i-5]) &&
        (buffer[i-1] == buffer[i-6]) &&
        (buffer[i-2] == buffer[i-7]) &&
        (buffer[i-3] == buffer[i-8]) &&
        (buffer[i-4] == buffer[i-9])) {

        updmsg(v,"Getting Interesting.");
        tday = (buffer[i-4]>>4) * 100 + (buffer[i-3]&0xf) * 10 +
          (buffer[i-3]>>4);
        hours = (buffer[i-2]&0xf) * 10 + (buffer[i-2]>>4) + (v->adjust);
        minutes = (buffer[i-1]&0xf) * 10 + (buffer[i-1]>>4);
        seconds = (buffer[i]&0xf) * 10 + (buffer[i]>>4) +1;

        if (seconds == 60) {
          seconds = 0;
          minutes++;
          if (minutes == 60) {
            minutes = 0;
            hours++;
          }
        }
	if (hours > 23) {
          tday++;
          hours -= 24;
        }
        else if (hours < 0) {
          tday--;
          hours+= 24;
        }
        time(&tstart);

        {
          int feb;

          if ((v->year)/4*4 == (v->year) && (v->year)/100*100 != (v->year)) feb = 29;
          else feb = 28;

          if (tday > (306 + feb)) {
            month = 12;
            day = tday - 306 - feb;
          }
          else if (tday > (276 + feb)) {
            month = 11;
            day = tday - 276 - feb;
          }
          else if (tday > (245 + feb)) {
            month = 10;
            day = tday - 245 - feb;
          }
          else if (tday > (215 + feb)) {
            month = 9;
            day = tday - 215 - feb;
          }
          else if (tday > (184 + feb)) {
            month = 8;
            day = tday - 184 - feb;
          }
          else if (tday > (153 + feb)) {
            month = 7;
            day = tday - 153 - feb;
          }
          else if (tday > (123 + feb)) {
            month = 6;
            day = tday - 123 - feb;
          }
          else if (tday > (92 + feb)) {
            month = 5;
            day = tday - 92 - feb;
          }
          else if (tday > (62 + feb)) {
            month = 4;
            day = tday - 62 - feb;
          }
          else if (tday > (31 + feb)) {
            month = 3;
            day = tday - 31 - feb;
          }
          else if (tday > 31) {
            month = 2;
            day = tday - 31;
          }
          else {
            month = 1;
            day = tday;
          }
        }
        SPrintF(v->scratch,"Date %ld-%ld-%ld %02ld:%02ld:%02ld",
               (v->year)-1900,months[month-1],day,hours,minutes,seconds);

        updmsg(v,v->scratch);
        gotit=TRUE;
        Execute(v->scratch,NULL,NULL);
        break;
      }
      i=i+state;
    }

  } else
    updmsg(v,"No characters received...");

exit:

  if (!gotit) {
    updmsg(v,"\nUNABLE TO CONNECT TO Atomic Clock TIMEBASE\n");

    KPrintF("\n\n");
    for(state=0;state<i;state++) {
      KPrintF(" %02lx",buffer[state]&0xff);
    }
    KPrintF("\n\n");
    state=TRUE;
  } else
    state=OK;

  /* Clean up and return */
  FreeMem(v,(long)sizeof(struct Vars));
  return ((state) ? XPRS_FAILURE : XPRS_SUCCESS );
}


/* Main protocol for inbound calls */
long __saveds XProtocolReceive(struct XPR_IO *io) {
  /* just stub this out */
return(XProtocolSend(io));
}

/* Transfer options to use if XProtocolSetup not called */
struct SetupVars Default_Config = {
  NULL, NULL, 0,
  { "1992" }, { "-5" }
};


/* Called by comm program to set transfer options */
long __saveds XProtocolSetup(struct XPR_IO *io) {
  struct SetupVars *sv;
  UBYTE buf[512], *p;

  /* Allocate memory for transfer options string */
  if (!(sv = (void *)io->xpr_data)) {
    io->xpr_data = AllocMem((long)sizeof(struct SetupVars),MEMF_CLEAR);
    if (!(sv = (void *)io->xpr_data)) {
      ioerr(io,"Not enough memory");
      return XPRS_FAILURE;
    }
    /* Start out with default options; merge user changes into defaults */
    *sv = Default_Config;
  }

  /* If options string passed by comm prog, use it; else use defaults */
  if (io->xpr_filename)
    strcpy(buf,io->xpr_filename);
  else
    return XPRS_SUCCESS;

  /* Upshift options string for easier parsing */
  strupr(buf);

  if (p = find_option(buf,'Y')) {
    int year=atoi(p);
    if ((year > 1900) && (year < 9999)) SPrintF(sv->year,"%ld",year);
    else ioerr(io,"Invalid Y Flag Ignored; Should Be 1900-9999");
  }

  if (p = find_option(buf,'O')) {
    SPrintF(sv->adjust,"%ld",atoi(p));    
  }
  return ( XPRS_SUCCESS );
}


/* Called by comm program to give us a chance to clean up before program ends */
long __saveds XProtocolCleanup(struct XPR_IO *io) {
  /* Release option memory, if any */
  if (io->xpr_data) {
    FreeMem(io->xpr_data,(long)sizeof(struct SetupVars));
    io->xpr_data = NULL;
  }
  return XPRS_SUCCESS;
}


/* Called by comm program to let us monitor user's inputs; we never ask for
   this to be called, but it's better to recover gracefully than guru the machine */
long __saveds XProtocolHostMon(struct XPR_IO *io,UBYTE *serbuff,long actual,long maxsize) {
  return actual;
}


/* Called by comm program to let us monitor user's inputs; we never ask for
   this to be called, but it's better to recover gracefully than guru the machine */
long __saveds XProtocolUserMon(struct XPR_IO *io,UBYTE *serbuff,long actual,long maxsize) {
  return actual;
}

/* Perform setup and initializations common to both Send and Receive routines */
struct Vars *setup(struct XPR_IO *io) {
  struct SetupVars *sv;
  struct Vars *v;

  /* Make sure comm program supports the required call-back functions */
  if (!io->xpr_update) return NULL;
  if (!io->xpr_fopen || !io->xpr_fclose || !io->xpr_fread || !io->xpr_fwrite ||
      !io->xpr_fseek || !io->xpr_sread || !io->xpr_swrite ||
      !io->xpr_sflush) {
    ioerr(io,"Comm Prog Missing Required Function(s); See Docs");
    return NULL;
  }

  /* Hook in default transfer options if XProtocolSetup wasn't called */
  if (!(sv = (void *)io->xpr_data)) {
    io->xpr_data = AllocMem((long)sizeof(struct SetupVars),MEMF_CLEAR);
    if (!(sv = (void *)io->xpr_data)) {
      ioerr(io,"Not Enough Memory");
      return NULL;
    }
    *sv = Default_Config;
  }

  /* Allocate memory for our unshared variables, to provide reentrancy */
  if (!(v = AllocMem((long)sizeof(struct Vars),MEMF_CLEAR))) {
    ioerr(io,"Not Enough Memory");
    return NULL;
  }

  /* Copy caller's io struct into our Vars for easier passing */
  v->io = *io;

  v->year=atoi(sv->year);
  v->adjust=atoi(sv->adjust);
  return v;
}

/* Search for specified option setting in string */
UBYTE *find_option(UBYTE *buf,UBYTE option) {
  while (*buf) {
    buf += strspn(buf," ,\t\r\n");
    if (*buf == option) return ++buf;
    buf += strcspn(buf," ,\t\r\n");
  }
  return NULL;
}

/* Have the comm program display an error message for us, using a
   temporary XPR_UPDATE structure; used to display errors before Vars
   gets allocated */
void ioerr(struct XPR_IO *io,char *msg) {
  struct XPR_UPDATE xpru;

  if (io->xpr_update) {
    xpru.xpru_updatemask = XPRU_ERRORMSG;
    xpru.xpru_errormsg = msg;
    xpr_update(io,&xpru);
  }
}

/* Have the comm program display an error message for us, using the
   normal XPR_IO structure allocated in Vars */
void upderr(struct Vars *v,char *msg) {
  v->xpru.xpru_updatemask = XPRU_ERRORMSG;
  v->xpru.xpru_errormsg = msg;
  xpr_update(&v->io,&v->xpru);
}

/* Have the comm program display a normal message for us */
void updmsg(struct Vars *v,char *msg) {
  v->xpru.xpru_updatemask = XPRU_MSG;
  v->xpru.xpru_msg = msg;
  xpr_update(&v->io,&v->xpru);
}
