/* Top X users (Top Ten Users)
 *
 * Copyright © 1995 Elseware Development
 *
 * A more advanced example door for Reccoon.
 * You may use the source in whatever you whish.
 * You don't have to credit me for anything.
 * (But I don't mind if you do ;))
 */

#include <exec/types.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <exec/exec.h>
#include <dos/dos.h>

#define RLIB_MACRO
#include <Reccoon/Recclib.h>
#include <Reccoon/Reccoon2.h>
#include <Reccoon/All.h>
#include <Reccoon/DoorMacro.h>

extern struct DosLibrary *DOSBase;

#define VERSION "1.20"

/* __COMMODORE_DATE__ is Dice-only */

UBYTE *Version = "\0$VER: TopXusers "VERSION" ("__COMMODORE_DATE__")";

struct CtrlMessage  ctrlmsg;
struct control *    ctrl        = 0;
struct MsgPort *    ctrlport    = 0;
struct MsgPort *    ctrlreplyport   = 0;
struct Library *    RecclibBase = 0;

void *          doordata=0;
struct DoorCallback *   dcb;
struct InternalData *   id;
struct ReccNode *   rn;

struct usr        usr;

UBYTE           outbuff[100];

int Sluta( int );
int CXBRK(void);

#define puts(x)   Write(Output(),x,sizeof(x))
#define print(x)  SendString(x, SS_NOBRK)
#define cls()     SendString("~ai~", SS_NOBRK)

/* Init ourer hiscore list */

char nam[12][100];
long poi[12] = {0,0,0,0,0,0,0,0,0,0,0,0};

void main(UWORD argc, UBYTE *argv[])
{
UWORD line;

    /* The line number is passed as an argument to the program */

    if(argc!=2||argv[1][0]<'0'||argv[1][0]>'9')
    {
        puts("This is a Rcn door program, and should not be run from the CLI.\n");
        exit(20);
    }
    line=atoi(argv[1]);

    RecclibBase=OpenLibrary("reccoon.library",0);
    if(!RecclibBase) exit(20);

    /* Open ReccControl. Always use line number -1 for doors and utilities */

    ctrl=OpenRCtrl(0,65535,&ctrlmsg,&ctrlport,&ctrlreplyport);
    if(!ctrl)
    {
        CloseLibrary(RecclibBase);
        exit(10);
    }

    /* Set an exit trap so the program never exits without cleanup */

    atexit((void *)Sluta);

    /* Find this line's ReccNode structure */

    for(rn=ctrl->reccnodes;rn && rn->line!=line;rn=rn->next)
        ;
    if(!rn || !rn->flags.running)
        exit(10);

    /*
    ** Here we got all callback functions and internal data for this
    ** Reccoon line.
    */

    dcb = rn->doorcallback;
    id  = rn->internaldata;

    /* Reccoon need to allocate some memory etc for your door. */

    doordata = OpenDoor();
    if(!doordata) exit(20);

    /*
    **        Note: UL 1KB    =  3p
    **              DL 1KB    = -2p
    **              UL 1 File =  3p
    **              DL 1 file = -2p
    **              1 Message =  3p
    **              1 Call    =  1p
    */

    /* Find out whether the user has ANSI or not */

    int ansi;
    ansi = ansidetect();

    /* Clear screen (see #define) */

    cls();

    /* Display title text and some version info */

    ShowFile("Title", SF_NOBRK);
    if(ansi)
        print("[0m[37m\n\r        TopXUsers v"VERSION" - Copyright (c) 1994 - Elseware Developments\n\n\r");
    else
        print("\n\r        TopXUsers v"VERSION" - Copyright (c) 1994 - Elseware Developments\n\n\r");
    print("Please wait, scanning users...");

    long point;
    char name[100], buf[256];

    FILE *fp;

    /* Open User.DAT and scan user statistics (Faster than using ReccControl
    ** but more dangerous since the structure size can change
    */

    fp = fopen("RData:User.DAT", "r");
    if(!fp)
    {
        if(ansi)
        {
            print("[35m\n\r*** ERROR --> Couldn't open user.dat\n\r");
            print("              Please notify SysOp!\n\r");
            GetReturn();
        } else {
            print("\n\r*** ERROR --> Couldn't open user.dat\n\r");
            print("              Please notify SysOp!\n\r");
            GetReturn();
        }
        exit(0);
    }

    /* The main scanning routine                            */
    /* (NOTE! never forget CARRIER checking when in a loop) */

    while((fread(&usr, sizeof(usr), 1, fp)) != 0 && id->CARRIER)
    {
        point = 0;

        if(name[0])
        {
            strcpy(name, usr.username);

            point += (usr.byteul/1024)*3;
            point -= (usr.bytedl/1024)*2;

            point += usr.fileul*3;
            point -= usr.filedl*2;

            point += usr.calls;
            point += usr.messages*3;

            /* Add entry, and sort the hiscore list */

            Update(name, point);
        }
    }

    fclose(fp);

    print("Ok!\r");

    int g = 0;

    if(ansi)
    {
        print("[37m                     Nr  User                    Points\n\r");
        print("                     --  ----------------------  ------\n\r");
    } else {
        print("                     Nr  User                    Points\n\r");
        print("                     --  ----------------------  ------\n\r");
    }

    /* Display our nice sorted hiscorelist */

    for(g = 0; g < 10; g++)
    {
        if(g == 0)
        {
            if(ansi)
                sprintf(buf, "                     [45m%2d  %-22.22s  %6d\n\r[40m", g+1, nam[g], poi[g]);
            else
                sprintf(buf, "                  -> %2d  %-22.22s  %6d <-\n\r", g+1, nam[g], poi[g]);
        }
        else
            sprintf(buf, "                     %2d  %-22.22s  %6d\n\r", g+1, nam[g], poi[g]);
        print(buf);
    }

    print("                     --  ----------------------  ------\n\r\n\r");

    /* Display end of screen textfile */

    ShowFile("End", SF_NOBRK);

    /* Wait for the user to press return */

    GetReturn();

    /* End of program */

    exit(0);

}

/* Our nice update and sort hiscorelist routine */

Update(char *name, long point)
{
   int g, t;

   for(g = 0; g <= 9; g++)
   {
      if(point > poi[g])
      {
         for(t = 8; t >= g ; t--)
         {
            strcpy(nam[t+1], nam[t]);
            poi[t+1] = poi[t];
         }
         poi[g] = point;
         strcpy(nam[g], name);
         return;
      }
   }
   return;
}

/* ANSI detect routine, use it freely */

ansidetect()
{
   struct StringList * string;
   string = id->bstrings;
   if(strlen(string->bstrings[112]) > 0)
      return(1);
   return(0);
}

/*
** Cleanup routine. This routine is always executed when
** this program exits.
*/

int Sluta(int e)
{
    /* Deallocate door memory */

    ContinueBBS();
    if(doordata) CloseDoor();
    doordata=0;

    /* Close reccoon.library */

    if(RecclibBase){
        CloseRCtrl(ctrlport,ctrlreplyport,&ctrlmsg);
        CloseLibrary(RecclibBase);
        RecclibBase=0;
    }
    return(e);
}

