/*********************************************************************
**                                                                  **
**               IT'S FAST, IT'S MEAN, IT'S COOL...                 **
**         ...it's C, Gunnar and Zeus in a combination!             **
**                                                                  **
**                Kids, don't do this at home!                      **
**                                                                  **
*********************************************************************/
/*
** We all know what these are good for, right?
*/
#include <exec/types.h>
#include <exec/memory.h>
#include <dos/dos.h>
#include <dos/var.h>
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <strings.h>
#include <stdio.h>

#include <proto/pipeline.h>
#include <proto/pipeutil.h>
#include <zheaders.h>

#define NAME_LEN 41

struct TopUsers
{
   LONG FilesDL;
   LONG BytesDL;
   LONG FilesUL;
   LONG BytesUL;
   UBYTE Name[NAME_LEN];
};

/*
** ...for twenty-four years I've lived next door to Alex... Humm.. Alice...
*/
UBYTE *Months[] =
{
   "Jan",   "Feb",   "Mar",   "Apr",   "May",   "Jun",
   "Jul",   "Aug",   "Sep",   "Oct",   "Nov",   "Dec",
};

struct Library  *PipelineBase;

/*
** We gonna make four lists
*/
struct TopUsers *fdl;
struct TopUsers *bdl;
struct TopUsers *ful;
struct TopUsers *bul;

UBYTE            NumUsers = 10;
BOOL             Alias;

UBYTE Version[] = "$VER: ZufStats 1.0uk (2.10.95)";

/*
** ReadArgs() stuff...
*/
#define TEMPLATE "FILESDLPATH/A,BYTESDLPATH/A,FILESULPATH/A,BYTESULPATH/A,USERS/N,ALIAS/S"
#define ARG_FILESDLPATH 0
#define ARG_BYTESDLPATH 1
#define ARG_FILESULPATH 2
#define ARG_BYTESULPATH 3
#define ARG_USERS       4
#define ARG_ALIAS       5
#define ARG_TOTAL       6

/*
** Some nice prototypes, so our compiler doesn't get confused.
*/
BOOL __MakeList(struct TopUsers *tu, UBYTE *Path);
BOOL __ScanUsers(VOID);
VOID __DoList(struct User *User, struct FileRatioData *frd, struct TopUsers *tu);

/********************************************************************/
int main(void)
/********************************************************************/
{
struct RDArgs *Rargs;
STRPTR         Args[ARG_TOTAL];
int            Rc = RETURN_ERROR;

   /*
   ** Parse the arguments 
   */
   bzero(Args, sizeof(STRPTR) * ARG_TOTAL);
   if (Rargs = ReadArgs(TEMPLATE, (LONG *)Args, NULL))
   {
      if (Args[ARG_USERS]) NumUsers = *(ULONG *)Args[ARG_USERS];
      Alias = Args[ARG_ALIAS] ? TRUE : FALSE;

      /*
      ** Open zeus.library...
      */
      if (PipelineBase = OpenLibrary("zeus.library", 0L))
      {
         /*
         ** Allocate space. Yeah, I know it's a waste of memory to
         ** have three long values in the structure that I will not
         ** use. BUT I had thought to use them.
         */
         if (fdl = AllocMem(sizeof(struct TopUsers)*NumUsers, MEMF_ANY|MEMF_CLEAR))
         {
            if (bdl = AllocMem(sizeof(struct TopUsers)*NumUsers, MEMF_ANY|MEMF_CLEAR))
            {
               if (ful = AllocMem(sizeof(struct TopUsers)*NumUsers, MEMF_ANY|MEMF_CLEAR))
               {
                  if (bul = AllocMem(sizeof(struct TopUsers)*NumUsers, MEMF_ANY|MEMF_CLEAR))
                  {
                     /*
                     ** ...Oh, when I think of you...
                     **
                     ** Ok, lets get some stats.
                     */
                     if (__ScanUsers())
                     {
                        Rc = RETURN_OK;

                        /*
                        ** Make the lists
                        */
                        if (!__MakeList(fdl, Args[ARG_FILESDLPATH])) Rc = RETURN_WARN;
                        if (!__MakeList(bdl, Args[ARG_BYTESDLPATH])) Rc = RETURN_WARN;
                        if (!__MakeList(ful, Args[ARG_FILESULPATH])) Rc = RETURN_WARN;
                        if (!__MakeList(bul, Args[ARG_BYTESULPATH])) Rc = RETURN_WARN;
                     }
                     else
                        Printf("Can't init user scan!\n");

                     FreeMem(bul, sizeof(struct TopUsers)*NumUsers);
                  }
                  else
                     Printf("Out of memory!\n");

                  FreeMem(ful, sizeof(struct TopUsers)*NumUsers);
               }
               else
                  Printf("Out of memory!\n");

               FreeMem(bdl, sizeof(struct TopUsers)*NumUsers);
            }
            else
               Printf("Out of memory!\n");

            FreeMem(fdl, sizeof(struct TopUsers)*NumUsers);
         }
         else
            Printf("Out of memory!\n");

         CloseLibrary(PipelineBase);
      }
      else
         Printf("Can't open zeus.library\n");

      FreeArgs(Rargs);
   }
   else
   {
      /*
      ** WRRRRRRRRRRROOOOONG arguments.
      */
      if (IoErr()) PrintFault(IoErr(), NULL);

      Printf("\nSyntax:\n   <FilesDLPath> <BytesDLPath> <FilesULPath> <BytesULPath> [Users] [Alias]\n\n"
           "   [1mFilesDLPath.:[0m Files DL stats file to write.\n"
           "   [1mBytesDLPath.:[0m Bytes DL stats file to write.\n"
           "   [1mFilesULPath.:[0m Files UL stats file to write.\n"
           "   [1mBytesULPath.:[0m Bytes UL stats file to write.\n"
           "   [1mUsers.......:[0m Number of users to include in stats.\n"
           "   [1mAlias.......:[0m Use alias instead of real names.\n\n");
   }

   if (Rc == RETURN_WARN) Printf("WARNING: Error writing one/several files\n");

return(Rc);
}

/********************************************************************/
BOOL __MakeList(struct TopUsers *tu, UBYTE *Path)
/********************************************************************/
{
UBYTE           *p, *p2;
struct TopUsers *Top;
ULONG            Users;
UBYTE            Offset;
LONG            *Value;
FILE            *OutF;
struct tm       *tm;
time_t           MyTime;

   /*
   ** Sesam, sesam, open up the file!
   */
   if (!(OutF = fopen(Path, "w"))) return(FALSE);

   /*
   ** One o'clock, two o'clock...
   */
   time(&MyTime);
   tm = localtime(&MyTime);

   /*
   ** Write the header
   */
   fprintf(OutF,    "         [32mZufStats 1.0uk. List created %02d-%s-%02d %02d:%02d:%02d\n",
      tm->tm_mday,
      Months[tm->tm_mon],
      tm->tm_year,
      tm->tm_hour,
      tm->tm_min,
      tm->tm_sec);

   /*
   ** Top downloaders (These are *bad* people)
   */
   if (tu == fdl)
   {
      Offset = 0;

      fprintf(OutF, "                        Top %d file leechers\n"
                    "   [34mณ [31m#  Name:                                    Files Downloaded:[34m ณ\n"
                    "   ฦอออออออออออออออออออออออออออออออออออออออออออออออออออออออออออออออต\n",
         NumUsers);
   }
   /*
   ** These guys have leeched many many bytes (Even worse)...
   */
   if (tu == bdl)
   {
      Offset = 1;

      fprintf(OutF, "                        Top %d byte leechers\n"
                    "   [34mณ [31m#  Name:                                    Bytes Downloaded:[34m ณ\n"
                    "   ฦอออออออออออออออออออออออออออออออออออออออออออออออออออออออออออออออต\n",
         NumUsers);
   }
   /*
   ** Top uploaders... Good guys!
   */
   if (tu == ful)
   {
      Offset = 2;

      fprintf(OutF, "                        Top %d file uploaders\n"
                    "   [34mณ [31m#  Name:                                      Files Uploaded:[34m ณ\n"
                    "   ฦอออออออออออออออออออออออออออออออออออออออออออออออออออออออออออออออต\n",
         NumUsers);
   }
   /*
   ** Those that have uploaded the most bytes.
   */
   if (tu == bul)
   {
      Offset = 3;

      fprintf(OutF, "                        Top %d byte uploaders\n"
                    "   [34mณ [31m#  Name:                                      Bytes Uploaded:[34m ณ\n"
                    "   ฦอออออออออออออออออออออออออออออออออออออออออออออออออออออออออออออออต\n",
         NumUsers);
   }

   /*
   ** So we can count down.
   */
   Users = NumUsers;

   /*
   ** Loop the list
   */
   p = (UBYTE *)tu;
   while (Users--)
   {
      p2 = Top = (struct TopUsers *)p;
      p2 += Offset*sizeof(LONG);
      Value = (LONG *)p2;

      fprintf(OutF, "   [34mณ [33m%-2d %-40s %17ld [34mณ\n",
         NumUsers - Users, // Ranking
         Top->Name,        // Name/Alias
         *Value);          // How much (What depends on the Offset value above)

      p += sizeof(struct TopUsers);
   }

   /*
   ** HURRAY! This list is almost finished.
   */
   fprintf(OutF, "   [34mิอออออออออออออออออออออออออออออออออออออออออออออออออออออออออออออออพ[0m\n");
   fclose(OutF);

return(TRUE);
}

/********************************************************************/
BOOL __ScanUsers(VOID)
/********************************************************************/
{
struct UserKey      *uk;
struct User         *User;
struct FileRatioData frd;

   /*
   ** Lets bother our users.
   */
   if (uk = InitUserKey(KEY_CHRONOREVERSE))
   {
      /*
      ** ...don't spare anyone of them! Scan'em all!
      */
      do
      {
         /*
         ** Convert the scan key to a user and load its/his/hers XData.
         */
         if (User = LoadUserFromKey(uk))
         {
            if (LoadUserXData(User))
            {
               /*
               ** Get struct FileRatioData, and make sure we got the whole
               ** structure (We've paid for Zeus, so we want the whole stuff).
               */
               if (sizeof(struct FileRatioData) ==
                   GetUserXData(User, &frd, FRAT, sizeof(struct FileRatioData), 0))
               {
                  /*
                  ** Insert the user in the different lists if (s)he has been
                  ** nice/bad enough.
                  */
                  __DoList(User, &frd, fdl);
                  __DoList(User, &frd, bdl);
                  __DoList(User, &frd, ful);
                  __DoList(User, &frd, bul);
               }
            }

	    UnLoadUser(User);
         }
      }
      while (NextUserKey(uk));

      EndUserKey(uk);
   }
   else
      return(FALSE);

return(TRUE);
}

/********************************************************************/
VOID __DoList(struct User *User, struct FileRatioData *frd, struct TopUsers *tu)
/********************************************************************/
{
UBYTE           *p, *p2;
struct TopUsers *Top;
ULONG            Users;
UBYTE            Offset;
LONG            *Value;
LONG             UserValue;

   /*
   ** The same thing like above. We use offset to save some code.
   */
   if (tu == fdl)
   {
      Offset = 0;
      UserValue = frd->FilesDL;
   }
   if (tu == bdl)
   {
      Offset = 1;
      UserValue = frd->BytesDL;
   }
   if (tu == ful)
   {
      Offset = 2;
      UserValue = frd->FilesUL;
   }
   if (tu == bul)
   {
      Offset = 3;
      UserValue = frd->BytesUL;
   }

   Users = NumUsers;

   /*
   ** Loop the top x list to see if the user can compete with the others.
   */
   p = (UBYTE *)tu;
   while (Users--)
   {
      p2 = Top = (struct TopUsers *)p;
      p2 += Offset*sizeof(LONG);
      Value = (LONG *)p2;

      /*
      ** Yep! That's our guy.
      */
      if (UserValue >= *Value)
      {
         memmove(p+sizeof(struct TopUsers), Top, Users*sizeof(struct TopUsers));

         Top->FilesDL = frd->FilesDL;
         Top->BytesDL = frd->BytesDL;
         Top->FilesUL = frd->FilesUL;
         Top->BytesUL = frd->BytesUL;
         strcpy(Top->Name, Alias ? User->ur_Alias : User->ur_Name);

         break;
      }

      p += sizeof(struct TopUsers);
   }
}
