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

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

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

#include <shared/typedefs.h>
#include <shared/jblist.h>
#include <shared/node4d.h>
#include <ctshared/ctstats.h>

#define VERSION "1.0"

#ifdef __SASC__
   const UBYTE ver[]="\0$VER: CrashTickStats " VERSION " " __AMIGADATE__ ;
#endif

#ifdef _DCC
   const UBYTE ver[]="\0$VER: CrashTickStats "VERSION" ("__COMMODORE_DATE__")";
#endif

struct AreaStatsNode
{
   struct AreaStatsNode *Next;
   UBYTE Tagname[80];
   ULONG Week;
   ULONG TotalFiles;
   ULONG TotalBytes;
   ULONG FirstDay;
   ULONG LastDay;
   ULONG Last8DaysFiles[8];
   ULONG Last8DaysBytes[8];
};

struct NodeStatsNode
{
   struct NodeStatsNode *Next;
   struct Node4D Node;
   ULONG SentFiles;
   ULONG SentBytes;
   ULONG GotFiles;
   ULONG GotBytes;
   ULONG FirstDay;
};

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

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

BOOL brk,nomem;

extern struct ExecBase *SysBase;
APTR mempool;

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

int CompareBytes(struct AreaStatsNode **s1, struct AreaStatsNode **s2)
{
   if((*s1)->TotalBytes < (*s2)->TotalBytes) return(1);
   if((*s1)->TotalBytes > (*s2)->TotalBytes) return(-1);
   return(0);
}

int CompareFiles(struct AreaStatsNode **s1, struct AreaStatsNode **s2)
{
   if((*s1)->TotalFiles < (*s2)->TotalFiles) return(1);
   if((*s1)->TotalFiles > (*s2)->TotalFiles) return(-1);
   return(0);
}

int CompareWeek(struct AreaStatsNode **s1, struct AreaStatsNode **s2)
{
   if((*s1)->Week < (*s2)->Week) return(1);
   if((*s1)->Week > (*s2)->Week) return(-1);
   return(0);
}

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

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

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

   nc=0;

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

   if(nc==0)
      return;

   alloc=nc*4;

   if(!(buf=(struct AreaStatsNode **)LibAllocPooled(mempool,alloc)))
   {
      nomem=TRUE;
      return;
   }

   work=buf;

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

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

      case 'f': qsort(buf,nc,4,CompareFiles);
                break;

      case 'b': qsort(buf,nc,4,CompareBytes);
                break;

      case 'w': qsort(buf,nc,4,CompareWeek);
                break;

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

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

   jbNewList(list);

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

   LibFreePooled(mempool,buf,alloc);
}

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

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

   nc=0;

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

   if(nc==0)
      return;

   alloc=nc*4;

   if(!(buf=(struct NodeStatsNode **)LibAllocPooled(mempool,alloc)))
   {
      nomem=TRUE;
      return;
   }

   work=buf;

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

   qsort(buf,nc,4,CompareNodes);

   jbNewList(list);

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

   LibFreePooled(mempool,buf,alloc);
}

/*
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 CalculateWeekKB(ULONG *last8array,ULONG daystatswritten,ULONG 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];

   return(sum/1024);
}

int main(int argc, char **argv)
{
   BPTR fh;
   ULONG totalfiles,totalbytes,firstday,areas;
   ULONG DayStatsWritten;
   UBYTE date[40],date2[40],buf[200];
   struct DiskAreaStats dastat;
   struct DiskNodeStats dnstat;
   struct DateTime dt;
   struct AreaStatsNode *asn;
   struct NodeStatsNode *nsn;
   struct jbList AreaList;
   struct jbList NodesList;
   struct RDArgs *rdargs;
   ULONG c,num,tot;
   ULONG total8daysbytes[8];
   ULONG total8daysfiles[8];

   if(SysBase->LibNode.lib_Version<37)
   {
      printf("Sorry, you need at least Kickstart 2.04 to run CrashTick\n");
      exit(10);
   }

   if(!(mempool=LibCreatePool(MEMF_ANY,10000,1000)))
   {
      printf("Failed to allocate memory pool\n");
      LibDeletePool(mempool);
      exit(10);
   }

   for(c=0;c<8;c++)
   {
      total8daysfiles[c]=0;
      total8daysbytes[c]=0;
   }

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

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

   if((((UBYTE *)argarray[SORT])[0]|32)!='a' &&
      (((UBYTE *)argarray[SORT])[0]|32)!='f' &&
      (((UBYTE *)argarray[SORT])[0]|32)!='b' &&
      (((UBYTE *)argarray[SORT])[0]|32)!='w' &&
      (((UBYTE *)argarray[SORT])[0]|32)!='d' &&
      (((UBYTE *)argarray[SORT])[0]|32)!='l')
   {
      Printf("Unknown sort mode %s\n",(long)argarray[SORT]);
      FreeArgs(rdargs);
      LibDeletePool(mempool);
      exit(10);
   }

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

   Printf("CrashTickStats "VERSION" © 1996 Johan Billing\n");

   if(!(fh=Open((UBYTE *)argarray[FILE],MODE_OLDFILE)))
   {
      Printf("Error opening %s\n",(long)argarray[FILE]);
      FreeArgs(rdargs);
      LibDeletePool(mempool);
      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;
   }

   /* Läs in stats */

   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;

   totalfiles=0;
   totalbytes=0;
   firstday=0;
   areas=0;

   brk=FALSE;
   nomem=FALSE;

   jbNewList(&AreaList);
   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(!(asn=LibAllocPooled(mempool,sizeof(struct AreaStatsNode))))
            {
               nomem=TRUE;
               break;
            }

            jbAddNode(&AreaList,(struct jbNode *)asn);

            strcpy(asn->Tagname,dastat.Tagname);
            asn->TotalFiles=dastat.TotalFiles;
            asn->TotalBytes=dastat.TotalBytes;
            asn->FirstDay=dastat.FirstTime.ds_Days;
            asn->LastDay=dastat.LastDay;
            CopyMem(&dastat.Last8DaysFiles[0],&asn->Last8DaysFiles[0],8*4);
            CopyMem(&dastat.Last8DaysBytes[0],&asn->Last8DaysBytes[0],8*4);
            asn->Week=CalculateWeekKB(&dastat.Last8DaysBytes[0],DayStatsWritten,asn->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=LibAllocPooled(mempool,sizeof(struct NodeStatsNode))))
         {
            nomem=TRUE;
            break;
         }

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

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

         nsn->SentFiles=dnstat.SentFiles;
         nsn->SentBytes=dnstat.SentBytes;
         nsn->GotFiles=dnstat.GotFiles;
         nsn->GotBytes=dnstat.GotBytes;
         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(&AreaList,'a');
      Sort(&AreaList,((UBYTE *)argarray[SORT])[0]|32);
      PutStr("\n");

      if(argarray[LAST7])
      {
         Printf("File flow in kilobytes:\n\n");

         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(asn=(struct AreaStatsNode *)AreaList.First;asn;asn=asn->Next)
            {
               if(SetSignal(0L,0L) & SIGBREAKF_CTRL_C)
               {
                  brk=TRUE;
                  break;
               }

               tot=0;

               for(c=1;c<8;c++)
                  tot+=asn->Last8DaysBytes[c];

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

               for(c=1;c<8;c++)
               {
                  total8daysfiles[c]+=asn->Last8DaysFiles[c];
                  total8daysbytes[c]+=asn->Last8DaysBytes[c];
               }

               areas++;
            }

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

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

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

               Printf("%-33.33s %4ld %4ld %4ld %4ld %4ld %4ld %4ld : %5ld\n",
                  buf,
                  total8daysbytes[1]/1024,
                  total8daysbytes[2]/1024,
                  total8daysbytes[3]/1024,
                  total8daysbytes[4]/1024,
                  total8daysbytes[5]/1024,
                  total8daysbytes[6]/1024,
                  total8daysbytes[7]/1024,
                  tot/1024);
            }
         }
      }
      else
      {
         PutStr("Area                           First       Last         Files     KB  /week\n");
         PutStr("===========================================================================\n");

         if(!brk)
         {
            for(asn=(struct AreaStatsNode *)AreaList.First;asn;asn=asn->Next)
            {
               if(SetSignal(0L,0L) & SIGBREAKF_CTRL_C)
               {
                  brk=TRUE;
                  break;
               }

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

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

               for(c=0;c<8;c++)
               {
                  total8daysfiles[c]+=asn->Last8DaysFiles[c];
                  total8daysbytes[c]+=asn->Last8DaysBytes[c];
               }

               totalfiles+=asn->TotalFiles;
               totalbytes+=asn->TotalBytes;
               areas++;

               Printf("%-30.30s %-11.11s %-11.11s %6ld %6ld %6ld\n",(long)asn->Tagname,date,date2,asn->TotalFiles,asn->TotalBytes/1024,asn->Week);
            }
         }

         if(!nomem && !brk)
         {
            PutStr("===========================================================================\n");
            sprintf(buf,"Totally in all %lu areas",areas);
            Printf("%-42s             %6ld %6ld %6ld\n",
               buf,
               totalfiles,
               totalbytes/1024,
               CalculateWeekKB(&total8daysbytes[0],DayStatsWritten,firstday));
         }
      }
   }

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

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

      for(nsn=(struct NodeStatsNode *)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);
         }

         sprintf(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 files: %lu KB/%lu files\n",nsn->SentBytes/1024,nsn->SentFiles);
			Printf("                                 Received files: %lu KB/%lu files\n",nsn->GotBytes/1024,nsn->GotFiles);
         Printf("\n");
      }
   }

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

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

   else
   {
      Printf("\n");
   }

   FreeArgs(rdargs);

   jbFreeList(&AreaList,sizeof(struct AreaStatsNode));
   jbFreeList(&NodesList,sizeof(struct NodeStatsNode));
   LibDeletePool(mempool);

   if(nomem)
      exit(20);

   else
      exit(0);
}

