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

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

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

#include <Memory.h>

#define VERSION "1.2"

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

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

struct StatsNode
{
   struct StatsNode *Next;
   UBYTE Tagname[80];
   ULONG Average;
   ULONG Total;
   ULONG Dupes;
   ULONG FirstDay;
   ULONG LastDay;
   UWORD Last8Days[8];
};

struct NodeStatsNode
{
   struct StatsNode *Next;
   struct Node4D Node;
   ULONG GotNetmails;
   ULONG GotNetmailBytes;
   ULONG SentNetmails;
   ULONG SentNetmailBytes;
   ULONG GotEchomails;
   ULONG GotEchomailBytes;
   ULONG SentEchomails;
   ULONG SentEchomailBytes;
   ULONG Dupes;
   ULONG Days;
   ULONG FirstDay;
};

#define FILE    0
#define SORT    1
#define LAST7   2
#define NOAREAS 3
#define NONODES 4
#define GROUP   5

ULONG argarray[]={NULL,NULL,NULL,NULL,NULL,NULL};
UBYTE *argstr="FILE/A,SORT,LAST7/S,NOAREAS/S,NONODES/S,GROUP";

BOOL brk,nomem;

int CompareAlpha(struct StatsNode **s1, struct StatsNode **s2)
{
   return(stricmp((*s1)->Tagname,(*s2)->Tagname));
}

int CompareTotal(struct StatsNode **s1, struct StatsNode **s2)
{
   if((*s1)->Total < (*s2)->Total) return(1);
   if((*s1)->Total > (*s2)->Total) return(-1);
   return(0);
}

int CompareDupes(struct StatsNode **s1, struct StatsNode **s2)
{
   if((*s1)->Dupes < (*s2)->Dupes) return(1);
   if((*s1)->Dupes > (*s2)->Dupes) return(-1);
   return(0);
}

int CompareMsgsDay(struct StatsNode **s1, struct StatsNode **s2)
{
   if((*s1)->Average < (*s2)->Average) return(1);
   if((*s1)->Average > (*s2)->Average) return(-1);
   return(0);
}

int CompareDay(struct StatsNode **s1, struct StatsNode **s2)
{
   if((*s1)->FirstDay < (*s2)->FirstDay) return(1);
   if((*s1)->FirstDay > (*s2)->FirstDay) return(-1);
   return(0);
}

int CompareLastDay(struct StatsNode **s1, struct StatsNode **s2)
{
   if((*s1)->LastDay < (*s2)->LastDay) return(1);
   if((*s1)->LastDay > (*s2)->LastDay) return(-1);
   return(0);
}

Sort(struct jbList *list,UBYTE sortmode)
{
   ULONG nc,alloc;
   struct StatsNode *sn,*buf,**work;

   nc=0;

   for(sn=list->First;sn;sn=sn->Next)
      nc++;

   if(nc==0)
      return;

   alloc=nc*4;

   if(!(buf=(struct StatsNode **)AllocMem(alloc,MEMF_ANY)))
   {
      nomem=TRUE;
      return;
   }

   work=buf;

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

   switch(sortmode)
   {
      case 'a': qsort(buf,nc,4,CompareAlpha);
                break;

      case 't': qsort(buf,nc,4,CompareTotal);
                break;

      case 'm': qsort(buf,nc,4,CompareMsgsDay);
                break;

      case 'd': qsort(buf,nc,4,CompareDay);
                break;

      case 'l': qsort(buf,nc,4,CompareLastDay);
                break;

      case 'u': qsort(buf,nc,4,CompareDupes);
                break;
   }

   jbNewList(list);

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

   FreeMem(buf,alloc);
}

int Compare4D(struct Node4D *node1,struct Node4D *node2)
{
   if(node1->Zone!=0 && node2->Zone!=0)
   {
      if(node1->Zone > node2->Zone) return(1);
      if(node1->Zone < node2->Zone) return(-1);
   }

   if(node1->Net  > node2->Net) return(1);
   if(node1->Net  < node2->Net) return(-1);

   if(node1->Node > node2->Node) return(1);
   if(node1->Node < node2->Node) return(-1);

   if(node1->Point > node2->Point) return(1);
   if(node1->Point < node2->Point) return(-1);

   return(0);
}

Copy4D(struct Node4D *node1,struct Node4D *node2)
{
   node1->Zone=node2->Zone;
   node1->Net=node2->Net;
   node1->Node=node2->Node;
   node1->Point=node2->Point;
}

int CompareNodes(struct NodeStatsNode **s1, struct NodeStatsNode **s2)
{
   return(Compare4D(&(*s1)->Node,&(*s2)->Node));
}

SortNodes(struct jbList *list)
{
   ULONG nc,alloc;
   struct StatsNode *sn,*buf,**work;

   nc=0;

   for(sn=list->First;sn;sn=sn->Next)
      nc++;

   if(nc==0)
      return;

   alloc=nc*4;

   if(!(buf=(struct StatsNode **)AllocMem(alloc,MEMF_ANY)))
   {
      nomem=TRUE;
      return;
   }

   work=buf;

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

   qsort(buf,nc,4,CompareNodes);

   jbNewList(list);

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

   FreeMem(buf,alloc);
}

void smallsprintf(UBYTE *buffer,UBYTE *ctl,...)
{
   va_list args;

   va_start(args, ctl);
   RawDoFmt(ctl,args,(void (*))"\x16\xc0\x4e\x75", buffer);
   va_end(args);
   return;
}

char *unit(long i)
{
   static char buf[40];
   if ((i>10000000)||(i<-10000000)) smallsprintf(buf,"%ld MB",i/(1024*1024));
   else if ((i>10000)||(i<-10000)) smallsprintf(buf,"%ld KB",i/1024);
   else smallsprintf(buf,"%ld bytes",i);
   return buf;
}

BOOL CheckFlags(UBYTE group,UBYTE *node)
{
   UBYTE c;

   for(c=0;c<strlen(node);c++)
   {
      if(group==ToUpper(node[c]))
         return(TRUE);
    }

   return(FALSE);
}

ULONG CalculateAverage(UWORD *last8array,ULONG total,UWORD daystatswritten,UWORD firstday)
{
   UWORD days,c;
   ULONG sum;

   if(daystatswritten == 0 || firstday == 0)
      return(0);

   days=daystatswritten-firstday;
   if(days > 7) days=7;

   sum=0;

   for(c=1;c<days+1;c++)
      sum+=last8array[c];

   if(days == 0) days=1;

   if(sum == 0 && total!=0)
   {
      days=daystatswritten-firstday;
      if(days==0) days=1;

      return(total/days);
   }

   return(sum/days);
}

#define STATS_IDENTIFIER   "CST2"

_main()
{
   BPTR fh;
   ULONG total,firstday,areas,totaldupes;
   ULONG DayStatsWritten;
   UBYTE date[40],date2[40],buf[200];
   struct DiskAreaStats dastat;
   struct DiskNodeStats dnstat;
   struct DateTime dt;
   struct StatsNode *sn;
   struct NodeStatsNode *nsn;
   struct jbList StatsList;
   struct jbList NodesList;
   struct ReadArgs *rdargs;
   ULONG c,num,tot;
   UWORD total8days[8];

   for(c=0;c<8;c++)
      total8days[c]=0;

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

   if(argarray[SORT]==NULL)
      argarray[SORT]=(ULONG)"Alpha";

   if((((UBYTE *)argarray[SORT])[0]|32)!='a' &&
      (((UBYTE *)argarray[SORT])[0]|32)!='m' &&
      (((UBYTE *)argarray[SORT])[0]|32)!='t' &&
      (((UBYTE *)argarray[SORT])[0]|32)!='d' &&
      (((UBYTE *)argarray[SORT])[0]|32)!='l' &&
      (((UBYTE *)argarray[SORT])[0]|32)!='u')
   {
      Printf("Unknown sort mode %s\n",(long)argarray[SORT]);
      FreeArgs(rdargs);
      _exit(10);
   }

   if(argarray[NOAREAS] && argarray[NONODES])
   {
      Printf("Nothing to do\n");
      FreeArgs(rdargs);
      _exit(10);
    }

   PutStr("CrashStats "VERSION" © 1995 Johan Billing\n");

   if(!(fh=Open((UBYTE *)argarray[FILE],MODE_OLDFILE)))
   {
      Printf("Error opening %s\n",(long)argarray[FILE]);
      FreeArgs(rdargs);
      _exit(10);
   }

   Read(fh,buf,4);
   buf[4]=0;

   if(strcmp(buf,STATS_IDENTIFIER)!=0)
   {
      Printf("Unknown format of stats file\n");
      FreeArgs(rdargs);
      Close(fh);
      return;
   }

   Read(fh,&DayStatsWritten,4);

   dt.dat_Stamp.ds_Minute=0;
   dt.dat_Stamp.ds_Tick=0;
   dt.dat_Format=FORMAT_DOS;
   dt.dat_Flags=0;
   dt.dat_StrDay=NULL;
   dt.dat_StrDate=date;
   dt.dat_StrTime=NULL;

   total=0;
   totaldupes=0;
   firstday=0;
   areas=0;

   brk=FALSE;
   nomem=FALSE;

   jbNewList(&StatsList);
   jbNewList(&NodesList);

   Read(fh,&num,4);
   c=0;

   if(!argarray[NOAREAS])
   {
      while(c<num && Read(fh,&dastat,sizeof(struct DiskAreaStats))==sizeof(struct DiskAreaStats))
      {
         if(SetSignal(0L,0L) & SIGBREAKF_CTRL_C)
         {
            brk=TRUE;
            break;
         }

         if(!argarray[GROUP] || CheckFlags(dastat.Group,(UBYTE *)argarray[GROUP]))
         {
            if(!(sn=AllocMem(sizeof(struct StatsNode),MEMF_ANY)))
            {
               nomem=TRUE;
               break;
            }

            jbAddNode(&StatsList,(struct jbNode *)sn);

            strcpy(sn->Tagname,dastat.Tagname);
            sn->Dupes=dastat.Dupes;
            sn->Total=dastat.TotalTexts;
            sn->FirstDay=dastat.FirstTime.ds_Days;
            sn->LastDay=dastat.LastDay;
            CopyMem(&dastat.Last8Days[0],&sn->Last8Days[0],8*2);

            sn->Average=CalculateAverage(&dastat.Last8Days[0],dastat.TotalTexts,DayStatsWritten,sn->FirstDay);
         }

         if(dastat.FirstTime.ds_Days!=0)
            if(firstday==0 || firstday > dastat.FirstTime.ds_Days)
               firstday=dastat.FirstTime.ds_Days;

         c++;
      }
   }
   else
   {
      while(c<num && Read(fh,&dastat,sizeof(struct DiskAreaStats))==sizeof(struct DiskAreaStats))
         c++;
   }

   Read(fh,&num,4);
   c=0;

   if(!argarray[NONODES] && !nomem && !brk)
   {
      while(c<num && Read(fh,&dnstat,sizeof(struct DiskNodeStats))==sizeof(struct DiskNodeStats))
      {
         if(SetSignal(0L,0L) & SIGBREAKF_CTRL_C)
         {
            brk=TRUE;
            break;
         }

         if(!(nsn=AllocMem(sizeof(struct NodeStatsNode),MEMF_ANY)))
         {
            nomem=TRUE;
            break;
         }

         jbAddNode(&NodesList,(struct jbNode *)nsn);

         Copy4D(&nsn->Node,&dnstat.Node);

         nsn->GotNetmails=dnstat.GotNetmails;
         nsn->GotNetmailBytes=dnstat.GotNetmailBytes;
         nsn->SentNetmails=dnstat.SentNetmails;
         nsn->SentNetmailBytes=dnstat.SentNetmailBytes;
         nsn->GotEchomails=dnstat.GotEchomails;
         nsn->GotEchomailBytes=dnstat.GotEchomailBytes;
         nsn->SentEchomails=dnstat.SentEchomails;
         nsn->SentEchomailBytes=dnstat.SentEchomailBytes;
         nsn->Dupes=dnstat.Dupes;

         nsn->Days=DayStatsWritten-dnstat.FirstDay;
         if(nsn->Days==0) nsn->Days=1;

         nsn->FirstDay=dnstat.FirstDay;

         if(dnstat.FirstDay!=0)
            if(firstday==0 || firstday > dnstat.FirstDay)
               firstday=dnstat.FirstDay;

         c++;
      }
   }
   else
   {
      while(c<num && Read(fh,&dnstat,sizeof(struct DiskNodeStats))==sizeof(struct DiskNodeStats))
         c++;
   }

   Close(fh);

   dt.dat_Stamp.ds_Days=DayStatsWritten;
   DateToStr(&dt);
   strcpy(date2,date);

   dt.dat_Stamp.ds_Days=firstday;
   DateToStr(&dt);

   Printf("\nStatistics from %s to %s\n",(long)date,(long)date2);

   if(!brk && !nomem && !argarray[NOAREAS])
   {
      Sort(&StatsList,'a');
      Sort(&StatsList,((UBYTE *)argarray[SORT])[0]|32);
      PutStr("\n");

      if(argarray[LAST7])
      {
         PutStr("Area                             ");

         for(c=1;c<8;c++)
         {
            dt.dat_Stamp.ds_Days=DayStatsWritten-c;
            DateToStr(&dt);
            Printf("   %.2s",date);
         }

         PutStr("   Total\n============================================================================\n");


         if(!brk && !nomem)
         {
            for(sn=StatsList.First;sn;sn=sn->Next)
            {
               if(SetSignal(0L,0L) & SIGBREAKF_CTRL_C)
               {
                  brk=TRUE;
                  break;
               }

               tot=0;

               for(c=1;c<8;c++)
                  tot+=sn->Last8Days[c];

               Printf("%-33.33s %4ld %4ld %4ld %4ld %4ld %4ld %4ld : %5ld\n",
                  sn->Tagname,
                  sn->Last8Days[1],
                  sn->Last8Days[2],
                  sn->Last8Days[3],
                  sn->Last8Days[4],
                  sn->Last8Days[5],
                  sn->Last8Days[6],
                  sn->Last8Days[7],
                  tot);

               for(c=1;c<8;c++)
                  total8days[c]+=sn->Last8Days[c];

               areas++;
            }

            if(!nomem && !brk)
            {
               tot=0;

               for(c=1;c<8;c++)
                  tot+=total8days[c];

               PutStr("=============================================================================\n");
               sprintf(buf,"Totally in all %lu areas",areas);

               Printf("%-33.33s %4ld %4ld %4ld %4ld %4ld %4ld %4ld : %5ld\n",
                  buf,
                  total8days[1],
                  total8days[2],
                  total8days[3],
                  total8days[4],
                  total8days[5],
                  total8days[6],
                  total8days[7],
                  tot);
            }
         }
      }
      else
      {
         PutStr("Area                           First     Last         Msgs  Msgs/day   Dupes\n");
         PutStr("============================================================================\n");

         if(!brk)
         {
            for(sn=StatsList.First;sn;sn=sn->Next)
            {
               if(SetSignal(0L,0L) & SIGBREAKF_CTRL_C)
               {
                  brk=TRUE;
                  break;
               }

               if(sn->LastDay==0)
               {
                  strcpy(date2,"<Never>");
               }
               else
               {
                  dt.dat_Stamp.ds_Days=sn->LastDay;
                  DateToStr(&dt);
                  strcpy(date2,date);
               }

               if(sn->FirstDay==0)
               {
                  strcpy(date,"<Never>");
               }
               else
               {
                  dt.dat_Stamp.ds_Days=sn->FirstDay;
                  DateToStr(&dt);
               }

               for(c=0;c<8;c++)
                  total8days[c]+=sn->Last8Days[c];

               total+=sn->Total;
               totaldupes+=sn->Dupes;
               areas++;

               Printf("%-30.30s %-9.9s %-9.9s %7ld   %7ld %7ld\n",(long)sn->Tagname,date,date2,sn->Total,sn->Average,sn->Dupes);
            }
         }

         if(!nomem && !brk)
         {
            PutStr("============================================================================\n");
            sprintf(buf,"Totally in all %lu areas",areas);
            Printf("%-42s         %7ld   %7ld %7ld\n",
               buf,
               total,
               CalculateAverage(&total8days[0],total,DayStatsWritten,firstday),
               totaldupes);
         }
      }
   }

   if(!brk && !nomem && !argarray[NONODES])
   {
      SortNodes(&NodesList);

      PutStr("\n");
      PutStr("Nodes statistics\n");
      PutStr("================\n");

      for(nsn=NodesList.First;nsn;nsn=nsn->Next)
      {
         if(SetSignal(0L,0L) & SIGBREAKF_CTRL_C)
         {
            brk=TRUE;
            break;
         }

         if(nsn->FirstDay==0)
         {
            strcpy(date,"<Never>");
         }
         else
         {
            dt.dat_Stamp.ds_Days=nsn->FirstDay;
            DateToStr(&dt);
         }

         smallsprintf(buf,"%lu:%lu/%lu.%lu",nsn->Node.Zone,nsn->Node.Net,nsn->Node.Node,nsn->Node.Point);

			Printf("%-30.40s Statistics since: %s\n\n",buf,date);
			Printf("                                  Sent netmails: %lu/%s\n",nsn->SentNetmails,unit(nsn->SentNetmailBytes));
			Printf("                              Received netmails: %lu/%s\n",nsn->GotNetmails,unit(nsn->GotNetmailBytes));
			Printf("                                 Sent echomails: %lu/%s\n",nsn->SentEchomails,unit(nsn->SentEchomailBytes));
			Printf("                             Received echomails: %lu/%s\n",nsn->GotEchomails,unit(nsn->GotEchomailBytes));
			Printf("                                          Dupes: %lu\n",nsn->Dupes);
         Printf("\n");
      }
   }

   if(brk)
      PutStr("*** Break\n");

   else if(nomem)
      PutStr("Out of memory!\n");

   else
   {
      Printf("\n");
   }

   FreeArgs(rdargs);

   jbFreeList(&StatsList,StatsList.First,sizeof(struct StatsNode));
   jbFreeList(&NodesList,NodesList.First,sizeof(struct NodeStatsNode));

   if(nomem)
      _exit(20);

   else
      _exit(0);
}




/* smallsprintf kraschar */

