
#include <exec/types.h>
#include <exec/memory.h>
#include <dos/dos.h>
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <clib/alib_protos.h>

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <time.h>
#include <math.h>

#ifdef __SASC
int CXBRK(void) { return(0); }
int _CXBRK(void) { return(0); }
void chkabort(void) {}
#endif

#include <HBBS/ANSI_Codes.h>
#include <HBBS/Defines.h>
#include <HBBS/types.h>
#include <HBBS/structures.h>
#include <HBBS/hbbscommon_protos.h>
#include <HBBS/hbbscommon_pragmas.h>
#include <HBBS/Hbbsnode_protos.h>
#include <HBBS/Hbbsnode_pragmas.h>

/* hbbs variables */
struct Library *HBBSCommonBase=NULL;
struct BBSGlobalData *BBSGlobal=NULL;
char outstr[1024];


/* lastcaller variables */

struct CallerNode
{
  struct Node node; // ->ln_Name contains the handle..
  char Group[LEN_GROUP];
  char OnTime[LEN_TIMESTR];
  char OffTime[LEN_TIMESTR];
  char Date[LEN_DATESTR];
  char Actions[LEN_ACTIONS];
  char Speed[MAX_CPSBAUD_LEN+1];
  V_BIGNUM NodeNum;

  V_BIGNUM FilesUploaded;
  V_BIGNUM FilesDownloaded;
  V_BIGNUM BytesUploaded;
  V_BIGNUM BytesDownloaded;

};

struct List *CallerList=NULL;



static VOID cleanup(ULONG num)
{
  if (HBBSCommonBase)
  {
    HBBS_CleanUpCommon();
    CloseLibrary (HBBSCommonBase);
  }

  if (num) printf("Door Error = %d\n",num);

  exit(0);
}

static VOID init(char *name)
{
  if(!(HBBSCommonBase = OpenLibrary("HBBSCommon.library",0)))
  {
    cleanup(1);
  }

  if (!(HBBS_InitCommon()))
  {
    cleanup(2);
  }

  SetProgramName(name);
}

void DisplayHelp( void )
{
  puts("LastCallers util for HydraBBS - ()C 1996 Hydra/LSD - Deluxe Software Ltd.");
  puts("usage: LastCallers [UPDATE <nodenum>] [[CREATE <filename> <users> <nodenum>|ALLNODES [IncludeLOCAL]]");

  puts("you should add a line like:\n"
       "LastCallers UPDATE CREATE HBBS:Nodes/Node[NodeNum]/Screens/Special/LastCallers.TXT [NodeNum] 20\n"
       "to the LogOffGlobal script..");
  puts("you must specify UPDATE in the command line so that lastcaller can add the current\n"
       "users details to it's datafile...");
  puts("you can ,of course,create more than one output file, just don't specify the\n"
       "UPDATE keyword in subsequent calls to LastCallers in the logoff script..\n");
}

void FHWriteStr(BPTR FH,char *str)
{
  Write(FH,str,strlen(str));
  Write(FH,"\n",1);
}

void FHWriteStrNOCR(BPTR FH,char *str)
{
  Write(FH,str,strlen(str));
}

void CreateFile(BOOL NoLocal,BOOL ScanAll,LONG OnlyNode,LONG Users,char *filename)
{

  // sort the list and save the first <Users> nodes into <filename>

  LONG Callers,loop,found=0;
  char tmpstr[1024],activitystring[80],ulstr[30],dlstr[30],speedstr[10];
  struct CallerNode *Caller;
  BPTR FH;

  if (FH=Open(filename,MODE_NEWFILE))
  {

    FHWriteStr(FH,"[2J[H[37;44m [ HAMFETAMiN V1.00 ]   [32mby [37mHydra / LSD[36m 1996[37m     [36m  [37m[ Utopia +44 (0)1202 43412 ] \n");
    FHWriteStr(FH,"[36;40mN User Name[37m       [34m [37m  [36mGroup[37m              [36mTimeOnBoard ACTVT U#/Byte D#/Byte[37m [36mSpeed ");
    FHWriteStr(FH,"[34m- ------------------[37m [34m------------------[37m [34m-----------[37m [34m-----[37m [34m-------[37m [34m-------[37m [34m-----");


    Callers=HBBS_NodesInList(CallerList);

    for (loop=0;loop<Callers && found<Users;loop++)
    {
      Caller=(struct CallerNode *)GetNode(CallerList,loop);

      if ((ScanAll || Caller->NodeNum==OnlyNode) && ((!(NoLocal && stricmp(Caller->Speed,"LOCAL")==0)) || (!NoLocal)))
      {
        found++;

        activitystring[0]=0;
        if (strchr(Caller->Actions,'L')) strcat(activitystring,"[36mC"); else strcat(activitystring,"[34m-");
        if (strchr(Caller->Actions,'N')) strcat(activitystring,"[36mN"); else strcat(activitystring,"[34m-");
        if (strchr(Caller->Actions,'H')) strcat(activitystring,"[36mH"); else strcat(activitystring,"[34m-");
        if (strchr(Caller->Actions,'P')) strcat(activitystring,"[36mP"); else strcat(activitystring,"[34m-");
        if (strchr(Caller->Actions,'S')) strcat(activitystring,"[36mS"); else strcat(activitystring,"[34m-");

        if (!(Caller->BytesUploaded))
        {
          strcpy(ulstr,"[33m---[37m-");
        }
        else
        {
          if (Caller->BytesUploaded>=(1024 * 1024)) // 1MB+
          {
//            if (Caller->BytesUploaded>=(10 * (1024 * 1024))) // 10MB+
//            {
              sprintf(ulstr,"%3.0f",(float)(Caller->BytesUploaded / (1024 * 1024)));
//            }
//            else sprintf(ulstr,"%1.1f",(float)(Caller->BytesUploaded / (1024 * 1024)));
            strcat(ulstr,"[35mM");
          }
          else
          {
            if (Caller->BytesUploaded>(10 * 1024)) // 10K+
            {
              sprintf(ulstr,"%3.0f",(float)(Caller->BytesUploaded / 1024));
            }
            else sprintf(ulstr,"%1.1f",(float)(Caller->BytesUploaded / 1024));
            strcat(ulstr,"[37mK");
          }
          replace(ulstr,ulstr," ","0");
        }

        if (!(Caller->BytesDownloaded))
        {
          strcpy(dlstr,"[33m---[37m-");
        }
        else
        {
          if (Caller->BytesDownloaded>(1024 * 1024))
          {
            if (Caller->BytesDownloaded>( 10 * (1024 * 1024)))
            {
              sprintf(dlstr,"%3.0f",(float)(Caller->BytesDownloaded / (1024 * 1024)));
            }
            else sprintf(dlstr,"%1.1f",(float)(Caller->BytesDownloaded / (1024 * 1024)));
            strcat(dlstr,"[35mM");
          }
          else
          {
//            if (Caller->BytesDownloaded>=(10 * 1024))
//            {
              sprintf(dlstr,"%3.0f",(float)(Caller->BytesDownloaded / 1024));
//            }
//            else sprintf(dlstr,"%1.1f",(float)(Caller->BytesDownloaded / 1024));
            strcat(dlstr,"[37mK");
          }
          replace(dlstr,dlstr," ","0");
        }

        sprintf(speedstr,"%5s",Caller->Speed);
        replace(speedstr,speedstr," ","0");

        //                    node       handle       group   ontimeofftime speed  activity
        sprintf(tmpstr,"[36m%1.1ld [37m%-18.18s [37m%-18.18s [33m%-5.5s-%-5.5s %s [33m%02ld[34m:[33m%s [33m%02ld[34m:[33m%s [36m%-5.5s ",
                Caller->NodeNum,Caller->node.ln_Name,Caller->Group,Caller->OnTime,Caller->OffTime,activitystring,
                Caller->FilesUploaded,ulstr,
                Caller->FilesDownloaded,dlstr,speedstr);

        FHWriteStr(FH,tmpstr);
      }
    }

    FHWriteStr(FH,"[34m-------------------------------------------------------------------------------[37m");
    Close(FH);
  }
  else
  {
    puts("could not write file!");
  }
}


void SaveDataFile( void )
{
  BPTR FH;
  struct CallerNode *Caller;
  char tmpstr[20];

  if (FH=Open("Progdir:LastCallers.DATA",MODE_NEWFILE))
  {
    for (Caller=(struct CallerNode*)CallerList->lh_Head;Caller->node.ln_Succ;Caller=(struct CallerNode*)Caller->node.ln_Succ)
    {
      FHWriteStr(FH,"!DATA!");
      FHWriteStr(FH,Caller->node.ln_Name);
      FHWriteStr(FH,Caller->Group);
      FHWriteStr(FH,Caller->OnTime);
      FHWriteStr(FH,Caller->OffTime);
      FHWriteStr(FH,Caller->Date);
      FHWriteStr(FH,Caller->Actions);
      FHWriteStr(FH,Caller->Speed);
      sprintf(tmpstr,"%ld",Caller->NodeNum);
      FHWriteStr(FH,tmpstr);

      sprintf(tmpstr,"%ld",Caller->FilesUploaded);
      FHWriteStr(FH,tmpstr);

      sprintf(tmpstr,"%ld",Caller->FilesDownloaded);
      FHWriteStr(FH,tmpstr);

      sprintf(tmpstr,"%ld",Caller->BytesUploaded);
      FHWriteStr(FH,tmpstr);

      sprintf(tmpstr,"%ld",Caller->BytesDownloaded);
      FHWriteStr(FH,tmpstr);
    }
    Close(FH);
  }
}

BOOL ReadDataFile(LONG Users)
{
  char buffer[1024];
  BPTR FH;
  BOOL retval=FALSE,eof=FALSE,error=FALSE;
  struct CallerNode *Caller;

  if (FH=Open("Progdir:LastCallers.DATA",MODE_OLDFILE))
  {
    if (CallerList=HBBS_CreateList())
    {
      do
      {
        while ((FGets(FH,buffer,1024)) && (strnicmp(buffer,"!DATA!",6)!=0));

        if (strnicmp(buffer,"!DATA!",6)==0) // if this is NOT what we want then we're at the EOF
        {
          if (FGets(FH,buffer,1024))
          {
            error=TRUE; // memory error!
            stripcr(buffer);
            if (Caller=(struct CallerNode *)HBBS_CreateNode(buffer,sizeof(struct CallerNode)))
            {
              if (FGets(FH,buffer,1024))
              {
                stripcr(buffer);
                strNcpy(Caller->Group,buffer,LEN_GROUP);
                if (FGets(FH,buffer,1024))
                {
                  stripcr(buffer);
                  strNcpy(Caller->OnTime,buffer,LEN_TIMESTR);
                  if (FGets(FH,buffer,1024))
                  {
                    stripcr(buffer);
                    strNcpy(Caller->OffTime,buffer,LEN_TIMESTR);
                    if (FGets(FH,buffer,1024))
                    {
                      stripcr(buffer);
                      strNcpy(Caller->Date,buffer,LEN_DATESTR);
                      if (FGets(FH,buffer,1024))
                      {
                        stripcr(buffer);
                        strNcpy(Caller->Actions,buffer,LEN_ACTIONS);
                        if (FGets(FH,buffer,1024))
                        {
                          stripcr(buffer);
                          strNcpy(Caller->Speed,buffer,MAX_CPSBAUD_LEN+1);
                          if (FGets(FH,buffer,1024))
                          {
                            stripcr(buffer);
                            if (sscanf(buffer,"%ld",&Caller->NodeNum) && Caller->NodeNum>=0 && Caller->NodeNum<BBSGlobal->BBSNodes)
                            {
                              if (FGets(FH,buffer,1024))
                              {
                                stripcr(buffer);
                                if (sscanf(buffer,"%ld",&Caller->FilesUploaded))
                                {
                                  if (FGets(FH,buffer,1024))
                                  {
                                    stripcr(buffer);
                                    if (sscanf(buffer,"%ld",&Caller->FilesDownloaded))
                                    {
                                      if (FGets(FH,buffer,1024))
                                      {
                                        stripcr(buffer);
                                        if (sscanf(buffer,"%ld",&Caller->BytesUploaded))
                                        {
                                          if (FGets(FH,buffer,1024))
                                          {
                                            stripcr(buffer);
                                            if (sscanf(buffer,"%ld",&Caller->BytesDownloaded))
                                            {
                                              AddTail(CallerList,(struct Node*)Caller);
                                              error=FALSE;
                                            }
                                          }
                                        }
                                      }
                                    }
                                  }
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  }        // cooooool, pretty patterns...
                }
              }
            }
          }
        }
        else
        {
          eof=TRUE;
        }
      } while (!eof && !error);

      if (error)
      {
        FreeStrList(CallerList);
      }
      else
      {
        if (HBBS_NodesInList(CallerList))
        {
          retval=TRUE;
        }
      }
    }
    Close(FH);
  }
  return(retval);
}

void UpdateDataFile(LONG NodeNum)
{
  LONG Nodes,ToRemove;
  struct NodeData *nd;
  struct CallerNode *Caller;

  if (!(nd=HBBS_NodeDataPtr(NodeNum))) return;


  if (!(ReadDataFile(80)))  // *C* #define 80
  {
    CallerList=HBBS_CreateList();
  }
  if (CallerList)
  {
    Nodes=HBBS_NodesInList(CallerList);

    // make sure there are 79 nodes MAX in the list..
    if (Nodes>79)
    {
      ToRemove=Nodes-79;

      while (ToRemove>0)
      {
        ToRemove--;
        Caller=(struct CallerNode *)CallerList->lh_Tail; //making sure we remove the last one in the list
        HBBS_FreeNode((struct Node*)Caller,TRUE);
      }
    }
    // then add our new one..
    if (nd->Status==STAT_ONLINE && nd->User.Valid)
    {
      if (Caller=(struct CallerNode *)HBBS_CreateNode(nd->User.CallData.Handle,sizeof(struct CallerNode)))
      {
        strNcpy(Caller->Group,nd->User.CallData.Group,LEN_GROUP);
        strNcpy(Caller->OnTime,nd->LastCalledTime,LEN_TIMESTR);
        HBBS_GetTime(Caller->OffTime);
        HBBS_GetDate(Caller->Date);
        strNcpy(Caller->Actions,nd->Actions,LEN_ACTIONS);
        replace(Caller->Actions,Caller->Actions," ","");
        strNcpy(Caller->Speed,nd->ConnectBaud,MAX_CPSBAUD_LEN+1);
        Caller->NodeNum=NodeNum;
        Caller->FilesUploaded=nd->FilesUploaded;
        Caller->FilesDownloaded=nd->FilesDownloaded;
        Caller->BytesUploaded=nd->BytesUploaded;
        Caller->BytesDownloaded=nd->BytesDownloaded;
        AddHead(CallerList,(struct Node*)Caller);

        SaveDataFile();
      }
    }
    else
    {
      puts ("No User Online!");
    }
  }
}

int main(int argc,char *argv[])
{
  LONG loop,Users,startparam=1,nodenum;

  BOOL scanall=FALSE,OK=FALSE,NoLocal=FALSE;
  char *filename=NULL;

  init("LastCallers");

  if (BBSGlobal=HBBS_GimmeBBS())
  {
    if (argc>=2)
    {
      if (argv[1][0]=='?')
      {
        DisplayHelp();
      }
      else
      {
        if (stricmp(argv[startparam],"UPDATE")==0)
        {
          startparam++;
          if (sscanf(argv[startparam],"%ld",&loop) && loop>=0 && loop<BBSGlobal->BBSNodes)
          {
            startparam++;
            UpdateDataFile(loop); // loop=node number to get users data from..
          }
        }
        if (stricmp(argv[startparam],"CREATE")==0)
        {
          startparam++;
          filename=argv[startparam++];

          if (sscanf(argv[startparam++],"%ld",&Users) && Users)
          {
            if (stricmp(argv[startparam],"ALLNODES")==0)
            {
              startparam++;
              scanall=TRUE;
              OK=TRUE;
            }
            else
            {
              if (sscanf(argv[startparam],"%ld",&loop) && loop>=0 && loop<BBSGlobal->BBSNodes)
              {
                startparam++;
                nodenum=loop;
                OK=TRUE;
              }
            }
            if (stricmp(argv[startparam],"NOLOCAL")==0)
            {
              NoLocal=TRUE;
            }
            if (OK && filename && filename[0])
            {
              if (ReadDataFile(Users))
              {
                CreateFile(NoLocal,scanall,nodenum,Users,filename);
              }
              if (CallerList) FreeStrList(CallerList);
            }
          }
        }
      }
    }
    else
    {
      DisplayHelp();
    }
  }
  else
  {
    puts("Control must be running!! This util requires access to runtime data!");
  }
  cleanup(0);
}
