#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#include <exec/types.h>
#include <exec/memory.h>
#include <dos/dos.h>

#include <clib/exec_protos.h>
#include <clib/dos_protos.h>

#include <FidoNet.h>

#define BREAK  (SetSignal(0L,0L)  & SIGBREAKF_CTRL_C)

jbNewList(struct jbList *list);
jbAddNode(struct jbList *list, struct jbNode *node);
jbFreeList(struct jbList *list,struct jbNode *firstnode,ULONG sizenode);

ULONG jbcpos;

jbstrcpy(UBYTE *dest,UBYTE *src,ULONG maxlen)
{
   ULONG d=0;
   UBYTE stopchar1,stopchar2;

   while(src[jbcpos]==32 || src[jbcpos]==9) jbcpos++;

   if(src[jbcpos]=='"')
   {
      jbcpos++;
      stopchar1='"';
      stopchar2=0;
   }
   else
   {
   	stopchar1=' ';
   	stopchar2=9;
   }

   while(src[jbcpos]!=stopchar1 && src[jbcpos]!=stopchar2 && src[jbcpos]!=10 && src[jbcpos]!=0 && d<maxlen-1)
   {
      if(src[jbcpos]=='\\' && src[jbcpos+1]!=0 && src[jbcpos+1]!=10)
      {
         jbcpos++;
         dest[d++]=src[jbcpos++];
      }

      else
         dest[d++]=src[jbcpos++];
   }
   dest[d]=0;
   if(src[jbcpos]==9 || src[jbcpos]==' ' || src[jbcpos]=='"') jbcpos++;

   if(d!=0 || stopchar1=='"') return(TRUE);

   return(FALSE);
}

struct jbList
{
   struct jbNode *First;
   struct jbNode *Last;
};

struct jbNode
{
   struct jbNode *Next;
};

#define VERSION "1.1"

UBYTE *ver="$VER: CrashHW "VERSION" ("__COMMODORE_DATE__")";

struct Area
{
   struct Area *Next;
   UBYTE Tagname[80];
   UBYTE Path[80];
};

struct jbList AreaList;

struct Msg
{
   struct Msg *Next;
   ULONG Num;
   ULONG NewNum;
};

struct jbList MsgList;

#define SHOW      0
#define UPDATE    1
#define SETTINGS  2

ULONG argarray[]={NULL,NULL,NULL};
UBYTE argstr[]="SHOW/K,UPDATE/K,SETTINGS/K";

struct RDArgs *rdargs;

ULONG today;

CleanUp(int err)
{
   if(rdargs) FreeArgs(rdargs);
   jbFreeList(&AreaList,AreaList.First,sizeof(struct Area));
   _exit(err);
}

UBYTE *MonthNames[12]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};

MakeFidoDate(UBYTE *dest)
{
   struct DateTime dt;
   UBYTE date[40],time[40];

   DateStamp(&dt.dat_Stamp);
   dt.dat_Format=FORMAT_CDN;
   dt.dat_Flags=0;
   dt.dat_StrDay=NULL;
   dt.dat_StrDate=date;
   dt.dat_StrTime=time;
   DateToStr(&dt);

   date[2]=0;
   date[5]=0;

   sprintf(dest,"%s %s %s  %s",date,MonthNames[atoi(&date[3])-1],&date[6],time);
}

int Compare(struct Msg **m1,struct Msg **m2)
{
   if((*m1)->Num > (*m2)->Num) return(1);
   if((*m1)->Num < (*m2)->Num) return(-1);
   return(0);
}

BOOL Sort(struct jbList *list)
{
   struct Msg *msg,**buf,**work;
   ULONG alloc,nodecount = 0;

   for(msg=list->First;msg;msg=msg->Next)
      nodecount++;

   if(!nodecount) return;

   if(!(buf=(struct FileToProcess **)AllocMem(nodecount * 4,MEMF_ANY)))
      return(FALSE);

   alloc=nodecount*4;

   work=buf;

   for(msg=list->First;msg;msg=msg->Next)
      *work++=msg;

   qsort(buf,nodecount,4,Compare);

   jbNewList(list);

   for(work=buf;nodecount--;)
      jbAddNode(list,(struct jbNode *)*work++);

   FreeMem(buf,alloc);
   return(TRUE);
}

struct StoredMsg StoredMsg;

UpdateArea(struct Area *area)
{
   BPTR l;
   struct FileInfoBlock *fib;
   struct Msg *msg;
   UBYTE buf[100],buf2[20];
   ULONG highwater;
   BPTR fh;
   BOOL kg;

   highwater=0;

   jbNewList(&MsgList);

   Printf("Updating %s...",area->Tagname);
   Flush(Output());

   if(l=Lock(area->Path,SHARED_LOCK))
   {
      if(!(fib=(struct FileInfoBlock *)AllocMem(sizeof(struct FileInfoBlock),MEMF_CLEAR)))
      {
         Printf("\nOut of memory\n");
         UnLock(l);
         CleanUp(10);
      }

      Examine(l,fib);

      while(ExNext(l,fib) && !BREAK)
         if(strlen(fib->fib_FileName)>=5)
            if(stricmp(&fib->fib_FileName[strlen(fib->fib_FileName)-4],".msg")==0)
            {
               if(atol(fib->fib_FileName) > 1)
               {
                  if(!(msg=(struct Msg *)AllocMem(sizeof(struct Msg),MEMF_CLEAR)))
                  {
                     Printf("\nOut of memory\n");
                     jbFreeList(&MsgList,MsgList.First,sizeof(struct Msg));
                     UnLock(l);
                     FreeMem(fib,sizeof(struct FileInfoBlock));
                     CleanUp(10);
                  }

                  jbAddNode(&MsgList,(struct jbNode *)msg);
                  msg->Num=atol(fib->fib_FileName);
               }
            }

      UnLock(l);
      FreeMem(fib,sizeof(struct FileInfoBlock));
   }
   else
   {
      Printf("\n Error: Couldn't scan dirrectory %s\n",area->Path);
      return;
   }

   if(MsgList.First && !BREAK)
   {
      if(!Sort(&MsgList))
      {
         jbFreeList(&MsgList,MsgList.First,sizeof(struct Msg));
         Printf("\nOut of memory\n");
         CleanUp(10);
      }

      highwater=0;
      kg=TRUE;

      for(msg=MsgList.First;msg && kg && !BREAK;msg=msg->Next)
      {
         strcpy(buf,area->Path);
         sprintf(buf2,"%lu.msg",msg->Num);
         AddPart(buf,buf2,100);

         if(fh=Open(buf,MODE_OLDFILE))
         {
            Read(fh,&StoredMsg,sizeof(struct StoredMsg));
            Close(fh);

            if(StoredMsg.Attr & SENT)
            {
               highwater=msg->Num;
            }
            else
            {
               kg=FALSE;
            }
         }
      }

      if(!BREAK)
      {
         strcpy(StoredMsg.From,"CrashMail");
         strcpy(StoredMsg.To,"All");
         strcpy(StoredMsg.Subject,"HighWater mark");
         MakeFidoDate(StoredMsg.DateTime);

         StoredMsg.TimesRead=0;
         StoredMsg.DestNode=0;
         StoredMsg.OrigNode=0;
         StoredMsg.Cost=0;
         StoredMsg.OrigNet=0;
         StoredMsg.DestNet=0;
         StoredMsg.DestZone=0;
         StoredMsg.OrigZone=0;
         StoredMsg.OrigPoint=0;
         StoredMsg.DestPoint=0;
         StoredMsg.ReplyTo=highwater;
         StoredMsg.Attr=SENT | PVT;
         StoredMsg.NextReply=0;

         strcpy(buf,area->Path);
         AddPart(buf,"1.msg",100);

         if(fh=Open(buf,MODE_NEWFILE))
         {
            Write(fh,&StoredMsg,sizeof(struct StoredMsg));
            Write(fh,"",1);
            Close(fh);
         }
         Printf(" New HighWater mark: %lu\n",highwater);
      }
   }
   else if(!BREAK)
   {
      Printf(" Area is empty\n");
   }

   jbFreeList(&MsgList,MsgList.First,sizeof(struct Msg));
}

ShowArea(struct Area *area)
{
   UBYTE buf[100];
   BPTR fh;
   ULONG highwater;

   highwater=-1;

   strcpy(buf,area->Path);
   AddPart(buf,"1.msg",100);

   if(fh=Open(buf,MODE_OLDFILE))
   {
      if(Read(fh,&StoredMsg,sizeof(struct StoredMsg))==sizeof(struct StoredMsg))
      {
         highwater=StoredMsg.ReplyTo;
      }
      Close(fh);
   }

   if(highwater == -1)
      Printf("No HighWater mark found in area %s\n",area->Tagname);

   else
      Printf("The HighWater mark in area %s is %lu\n",area->Tagname,highwater);
}

UBYTE cfgbuf[4000];

BOOL ReadConfig(UBYTE *file)
{
   BPTR fh;
   UBYTE cfgword[20];
   UBYTE tag[80];
   UBYTE path[80];
   struct Area *tmparea,*LastArea;

   if(!(fh=Open(file,MODE_OLDFILE)))
      return(FALSE);

   while(FGets(fh,cfgbuf,4000))
   {
      jbcpos=0;
      jbstrcpy(cfgword,cfgbuf,20);

      if(stricmp(cfgword,"AREA")==0 || stricmp(cfgword,"NETMAIL")==0)
      {
         jbstrcpy(tag,cfgbuf,80);

         jbstrcpy(path,cfgbuf,80);
         jbstrcpy(path,cfgbuf,80);

         if(stricmp(path,"MSG")==0 && stricmp(tag,"DEFAULT")!=0)
         {
            if(jbstrcpy(path,cfgbuf,80))
            {
               if(!(tmparea=(struct Area *)AllocMem(sizeof(struct Area),MEMF_CLEAR)))
               {
                  Printf("Out of memory\n");
                  Close(fh);
                  CleanUp(10);
               }

               jbAddNode(&AreaList,(struct jbNode *)tmparea);
               LastArea=tmparea;

               strcpy(tmparea->Tagname,tag);
               strcpy(tmparea->Path,path);
            }
         }
      }
   }
   Close(fh);

   return(TRUE);
}

_main()
{
   struct Area *area;
   BOOL config;
   
   jbNewList(&AreaList);

   if(!(rdargs=(struct RDArgs *)ReadArgs(argstr,argarray,NULL)))
   {
      PrintFault(IoErr(),NULL);
      CleanUp(10);
   }

   if(argarray[SHOW]==0 && argarray[UPDATE]==0)
   {
      PutStr("Nothing to do.\n");
      CleanUp(0);
   }

   config=FALSE;

   if(argarray[SETTINGS]==NULL)
   {
      if(ReadConfig("PROGDIR:CrashMail.prefs"))
         config=TRUE;

      else if(ReadConfig("MAIL:CrashMail.prefs"))
         config=TRUE;

      if(!config)
      {
         Printf("Couldn't read config file PROGDIR:CrashMail.prefs or MAIL:CrashMail.prefs\n");
         CleanUp(10);
      }
   }
   else
   {
      if(!ReadConfig((UBYTE *)argarray[SETTINGS]))
      {
         Printf("Couldn't read config file %s\n",(long)argarray[SETTINGS]);
         CleanUp(10);
      }
   }

   if(argarray[SHOW])
   {
      for(area=AreaList.First;area;area=area->Next)
         if(stricmp(area->Tagname,(UBYTE *)argarray[SHOW])==0) break;

      if(area)
      {
         ShowArea(area);
      }
      else
      {
         Printf("Area %s does not exist or is not a *.msg area\n",argarray[SHOW]);
      }
   }

   if(argarray[UPDATE])
   {
      if(stricmp((UBYTE *)argarray[UPDATE],"ALL")==0)
      {
         for(area=AreaList.First;area && !BREAK;area=area->Next)
            UpdateArea(area);
      }
      else
      {
         for(area=AreaList.First;area;area=area->Next)
            if(stricmp(area->Tagname,(UBYTE *)argarray[UPDATE])==0) break;

         if(area)
         {
            UpdateArea(area);
         }
         else
         {
            Printf("Area %s does not exist or is not a *.msg area\n",argarray[UPDATE]);
         }
      }
   }

   if(BREAK)
      Printf("*** Break\n");

   CleanUp(0);
}


