/*
    rip.c - File for the fun ends Death or a total win
    
    XRogue: Expeditions into the Dungeons of Doom
    Copyright (C) 1991 Robert Pietkivitch
    All rights reserved.
    
    Based on "Advanced Rogue"
    Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka and AT&T
    All rights reserved.

    Based on "Rogue: Exploring the Dungeons of Doom"
    Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman
    All rights reserved.
    
    See the file LICENSE.TXT for full copyright and licensing information.
*/

#define REALLIFE 1      /* Print out machine and logname */
#define EDITSCORE 2     /* Edit the current score file */
#define ADDSCORE 3      /* Add a new score */

#include <stdlib.h>
#include <curses.h>
#include <time.h>
#include <signal.h>
#include <ctype.h>
#include <string.h>
#include <sys/types.h>
#include <fcntl.h>
#include "mach_dep.h"
#include "network.h"
#include "rogue.h"

/* Network machines (for mutual score keeping) */
struct network Network[] = {
    { "", "" },
};

static char *rip[] = {
"                      ___________",
"                     /           \\",
"                    /             \\",
"                   /    R. I. P.   \\",
"                  /                 \\",
"                 /                   \\",
"                 |                   |",
"                 |                   |",
"                 |     killed by     |",
"                 |                   |",
"                 |                   |",
"                 |                   |",
"                *|      *  *  *      |*",
"       _________)|//\\\\///\\///\\//\\//\\/|(_________",
NULL
};

int rs_read_scorefile(FILE *savef, struct sc_ent *entries, int count);
void rs_write_scorefile(FILE *savef, struct sc_ent *entries, int count);

char *killname(short monst);
void showpack(char *howso);
int update(struct sc_ent top_ten[], unsigned long amount, short quest, 
           char *whoami, short flags, short level, short monst, short ctype,
           char *system, char *login);

extern FILE *scorefi, *logfile;

/*UNUSED*/
void
byebye(int sig)
{
	NOOP(sig);
    exit_game(EXIT_ENDWIN);
}


/*
 * death:
 *      Do something really fun when he dies
 */

void
death(short monst)
{
    register char **dp = rip, *killer;
    register struct tm *lt;
    time_t date;
    char buf[LINELEN];
    struct tm *localtime();

    writelog(pstats.s_exp, KILLED, monst);
    time(&date);
    lt = localtime(&date);
    clear();
    move(8, 0);
    while (*dp)
        printw("%s\n", *dp++);
    mvaddstr(14, 28-((strlen(whoami)+1)/2), whoami);
    sprintf(buf, "%lu Points", pstats.s_exp );
    mvaddstr(15, 28-((strlen(buf)+1)/2), buf);
    killer = killname(monst);
    mvaddstr(17, 28-((strlen(killer)+1)/2), killer);
    mvaddstr(18, 25, (sprintf(prbuf, "%4d", 1900+lt->tm_year), prbuf));
    move(lines-1, 0);
    refresh();
    score(pstats.s_exp, KILLED, monst);
    exit_game(EXIT_ENDWIN);
}

char *
killname(short monst)
{
    static char mons_name[LINELEN/2];
    int i;

    if (monst > NUMMONST) return("a strange monster");

    if (monst >= 0) {
        switch (monsters[monst].m_name[0]) {
            case 'a':
            case 'e':
            case 'i':
            case 'o':
            case 'u':
                sprintf(mons_name, "an %s", monsters[monst].m_name);
                break;
            default:
                sprintf(mons_name, "a %s", monsters[monst].m_name);
        }
        return(mons_name);
    }
    for (i = 0; i< DEATHNUM; i++) {
        if (deaths[i].reason == monst)
            break;
    }
    if (i >= DEATHNUM)
        return ("strange death");
    return (deaths[i].name);
}

/* Writes an entry in the log file */

void 
writelog(unsigned long amount, int flags, short monst)
{
    char had_quest = '0';
    char fate[LINELEN];
    struct linked_list *item;
    struct object *obj;
#ifdef LOGFILE
    if (waswizard)
        return;
    if (logfile == NULL)
    {
        /* Error message? */
        return;
    }
    /* Adjustments to the score */
    if (level == 0 && max_level == 0) 
        amount = 0;
    if (flags == CHICKEN)
        amount /= 100;
    /* Check for quest item */
    for (item = pack; item != NULL; item = next(item)) {
        obj = OBJPTR(item);
        if (obj->o_type == RELIC && obj->o_which == quest_item)
            had_quest = '1';
    }
    /* Describe what happened */
    if (flags == KILLED) {
        snprintf(fate, LINELEN, "killed by %s", killname(monst));
    }
    else if (flags == CHICKEN) {
        strcpy(fate, "quit");
    }
    else if (flags == WINNER) {
        strcpy(fate, "escaped");
    }
    else
        return;
    /* Write the line */
    fprintf(logfile, "%d %d %s %d %s %d %d %d %c %s\n", time(NULL), amount,
            whoami, pstats.s_lvl, char_class[char_type].name, level, max_level,
            quest_item, had_quest, fate);
    fclose(logfile);
#endif
    return;
}

/*
 * score -- figure score and post it.
 */

/* VARARGS2 */
void
score(unsigned long amount, int flags, short monst)
{
    struct sc_ent top_ten[NUMSCORE];
    register struct sc_ent *scp;
    register int i;
    register struct sc_ent *sc2;
    register FILE *outf;
    register char *killer;
    register int prflags = 0;
    short upquest=0, wintype=0, uplevel=0, uptype=0;    /* For network updating */
    char upsystem[SYSLEN], uplogin[LOGLEN];
    char *thissys;      /* Holds the name of this system */

#define REASONLEN 3
    static char *reason[] = {
        "killed",
        "quit",
        "A total winner",
        "somehow left",
    };
    char *packend;

    memset(top_ten,0,sizeof(top_ten));

    signal(SIGINT, byebye);
    if (level == 0 && max_level == 0) 
        amount = 0; /*don't count if quit early */
    if (flags != WINNER && flags != SCOREIT && flags != UPDATE) {
        if (flags == CHICKEN) {
            packend = "when you quit";
        amount = amount / 100;
        }
        else
            packend = "at your untimely demise";
        mvaddstr(lines - 1, 0, retstr);
        refresh();
        getstr(prbuf);
        showpack(packend);
    }
    purse = 0;  /* Steal all the gold */

    /*
     * Open file and read list
     */

    if (scorefi == NULL)
    {
        mvprintw(lines - 1, 0, "Unable to open or create score file: %s",score_file);
        refresh();
        return;
    }
    else
    {
        outf = scorefi;
    }

	thissys = md_gethostname();

    /*
     * If this is a SCOREIT optin (rogue -s), don't call byebye.  The
     * endwin() calls in byebye() will and this results in a core dump.
     */
    if (flags == SCOREIT) signal(SIGINT, SIG_DFL);
    else signal(SIGINT, byebye);

    if (flags != SCOREIT && flags != UPDATE)
    {
        mvaddstr(lines - 1, 0, retstr);
        refresh();
        fflush(stdout);
        getstr(prbuf);
    }

    /* Check for special options */
    if (strcmp(prbuf, "names") == 0)
        prflags = REALLIFE;
    else if (wizard) {
        if (strcmp(prbuf, "edit") == 0) prflags = EDITSCORE;
        else if (strcmp(prbuf, "add") == 0) {
            prflags = ADDSCORE;
            waswizard = FALSE;  /* We want the new score recorded */
        }
    }

    /* Read the score and convert it to a compatible format */

    fseek(outf, 0, SEEK_SET);
    rs_read_scorefile(outf, top_ten, NUMSCORE);

    /* Get some values if this is an update */
    if (flags == UPDATE) {
        int errcheck, errors = 0;

        upquest = (short) netread(&errcheck, sizeof(short), stdin);
        if (errcheck) errors++;

        if (fread(whoami, 1, NAMELEN, stdin) != NAMELEN) errors++;

        wintype = (short) netread(&errcheck, sizeof(short), stdin);
        if (errcheck) errors++;

        uplevel = (short) netread(&errcheck, sizeof(short), stdin);
        if (errcheck) errors++;

        uptype = (short) netread(&errcheck, sizeof(short), stdin);
        if (errcheck) errors++;

        if (fread(upsystem, 1, SYSLEN, stdin) != SYSLEN)
                errors++;
        if (fread(uplogin, 1, LOGLEN, stdin) != LOGLEN)
                errors++;
        
        if (errors) {
            fclose(outf);
            return;
        }
    }

    /*
     * Insert player in list if need be
     */
    if (!waswizard) {
        char *login= NULL;

        if (flags != UPDATE) {
			login = md_getusername();
            
            if ((login == NULL) || (*login == 0))
                login = "another rogue fiend";
        }

        if (flags == UPDATE)
            (void) update(top_ten, amount, upquest, whoami, wintype,
                   uplevel, monst, uptype, upsystem, uplogin);
        else {
            if (prflags == ADDSCORE) {  /* Overlay characteristic by new ones */
                char buffer[LINELEN];

                clear();
                mvaddstr(1, 0, "Score: ");
                mvaddstr(2, 0, "Quest (number): ");
                mvaddstr(3, 0, "Name: ");
                mvaddstr(4, 0, "System: ");
                mvaddstr(5, 0, "Login: ");
                mvaddstr(6, 0, "Level: ");
                mvaddstr(7, 0, "Char type: ");
                mvaddstr(8, 0, "Result: ");

                /* Get the score */
                move(1, 7);
                get_str(buffer, stdscr);
                amount = atol(buffer);

                /* Get the character's quest -- must be a number */
                move(2, 16);
                get_str(buffer, stdscr);
                quest_item = atoi(buffer);

                /* Get the character's name */
                move(3, 6);
                get_str(buffer, stdscr);
                strncpy(whoami, buffer, NAMELEN);

                /* Get the system */
                move(4, 8);
                get_str(buffer, stdscr);
                strncpy(thissys, buffer, SYSLEN);

                /* Get the login */
                move(5, 7);
                get_str(buffer, stdscr);
                strncpy(login, buffer, LOGLEN);

                /* Get the level */
                move(6, 7);
                get_str(buffer, stdscr);
                level = max_level = (short) atoi(buffer);

                /* Get the character type */
                move(7, 11);
                get_str(buffer, stdscr);
                for (i=0; i<NUM_CHARTYPES; i++) {
                    if (EQSTR(buffer, char_class[i].name, strlen(buffer)))
                        break;
                }
                player.t_ctype = i;

                /* Get the win type */
                move(8, 8);
                get_str(buffer, stdscr);
                switch (buffer[0]) {
                    case 'W':
                    case 'w':
                    case 'T':
                    case 't':
                        flags = WINNER;
                        break;

                    case 'Q':
                    case 'q':
                        flags = CHICKEN;
                        break;

                    case 'k':
                    case 'K':
                    default:
                        flags = KILLED;
                        break;
                }

                /* Get the monster if player was killed */
                if (flags == KILLED) {
                    mvaddstr(9, 0, "Death type: ");
                    get_str(buffer, stdscr);
                    if (buffer[0] == 'M' || buffer[0] == 'm')
                        do {
                            monst = makemonster(TRUE, "choose");
                        } while (monst < 0); /* Force a choice */
                    else monst = getdeath();
                }
            }

            if (update(top_ten, amount, (short) quest_item, whoami, flags,
                    (flags == WINNER) ? (short) max_level : (short) level,
                    monst, player.t_ctype, thissys, login)
                ) {
                /* Send this update to the other systems in the network */
                int i, j;
                char cmd[256];  /* Command for remote execution */
                FILE *rmf, *popen();    /* For input to remote command */

                for (i=0; Network[i].system[0] != 0; i++)
                    if (Network[i].system[0] != '!' &&
                        strcmp(Network[i].system, thissys)) {
                        sprintf(cmd, NETCOMMAND,
                                Network[i].system, Network[i].rogue);

                        /* Execute the command */
                        if ((rmf=popen(cmd, "w")) != NULL) {
                            unsigned long temp; /* Temporary value */

                            /* Write out the parameters */
                            (void) netwrite((unsigned long) amount,
                                          sizeof(unsigned long), rmf);

                            (void) netwrite((unsigned long) monst,
                                          sizeof(short), rmf);

                            (void) netwrite((unsigned long) quest_item,
                                        sizeof(short), rmf);

                            (void) fwrite(whoami, 1, strlen(whoami), rmf);
                            for (j=strlen(whoami); j<NAMELEN; j++)
                                putc('\0', rmf);

                            (void) netwrite((unsigned long) flags,
                                          sizeof(short), rmf);

                            temp = (unsigned long)
                                (flags==WINNER ? max_level : level);
                            (void) netwrite(temp, sizeof(short), rmf);

                            (void) netwrite((unsigned long) player.t_ctype,
                                          sizeof(short), rmf);

                            (void) fwrite(thissys, 1,
                                                strlen(thissys), rmf);
                            for (j=strlen(thissys); j<SYSLEN; j++)
                                putc('\0', rmf);

                            (void) fwrite(login, 1, strlen(login), rmf);
                            for (j=strlen(login); j<LOGLEN; j++)
                                putc('\0', rmf);

                            /* Close off the command */
                            (void) pclose(rmf);
                        }
                    }
            }
        }
    }

    /*
     * SCOREIT -- rogue -s option.  Never started curses if this option.
     * UPDATE -- network scoring update.  Never started curses if this option.
     * EDITSCORE -- want to delete or change a score.
     */
/*   if (flags != SCOREIT && flags != UPDATE && prflags != EDITSCORE)
        endwin();       */

    if (flags != UPDATE) {
        if (flags != SCOREIT) {
            clear();
            refresh();
            endwin();
        }
        /*
        * Print the list
        */
        printf("\nTop %d Adventurers:\nRank     Score\tName\n",
                NUMSCORE);
        for (scp = top_ten; scp < &top_ten[NUMSCORE]; scp++) {
            char *class;

            if (scp->sc_score != 0) {
                class = char_class[scp->sc_ctype].name;

                /* Make sure we have an in-bound reason */
                if (scp->sc_flags > REASONLEN) scp->sc_flags = REASONLEN;

                printf("%3d %10lu\t%s (%s)", scp - top_ten + 1,
                    scp->sc_score, scp->sc_name, class);
                    
                if (prflags == REALLIFE) printf(" [in real life %.*s!%.*s]",
                                SYSLEN, scp->sc_system, LOGLEN, scp->sc_login);

                printf(":\n\t\t%s on level %d", reason[scp->sc_flags],
                            scp->sc_level);

                switch (scp->sc_flags) {
                    case KILLED:
                        printf(" by");
                        killer = killname(scp->sc_monster);
                        printf(" %s", killer);
                        break;

                    case WINNER:
                        printf(" with the %s",
                                rel_magic[scp->sc_quest].mi_name);
                        break;
                }

                if (prflags == EDITSCORE)
                {
                    fflush(stdout);
                    getstr(prbuf);
                    printf("\n");
                    if (prbuf[0] == 'd') {
                        for (sc2 = scp; sc2 < &top_ten[NUMSCORE-1]; sc2++)
                            *sc2 = *(sc2 + 1);
                        top_ten[NUMSCORE-1].sc_score = 0;
                        for (i = 0; i < NAMELEN; i++)
                            top_ten[NUMSCORE-1].sc_name[i] = rnd(255);
                        top_ten[NUMSCORE-1].sc_flags = RN;
                            top_ten[NUMSCORE-1].sc_level = RN;
                            top_ten[NUMSCORE-1].sc_monster = RN;
                            scp--;
                    }
                    else if (prbuf[0] == 'e') {
                        printf("Death type: ");
                        getstr(prbuf);
                        if (prbuf[0] == 'M' || prbuf[0] == 'm')
                            do {
                                scp->sc_monster =
                                    makemonster(TRUE, "choose");
                            } while (scp->sc_monster < 0); /* Force a choice */
                        else scp->sc_monster = getdeath();
                        clear();
                        refresh();
                    }
                }
                else printf("\n");
            }
        }
        if (flags != SCOREIT)
        {
            printf("\n[Press return to exit]");
            fflush(stdout);
            fgets(prbuf,80,stdin);
        }
/*      if (prflags == EDITSCORE) endwin();*/     /* End editing windowing */
    }
    fseek(outf, 0L, SEEK_SET);

    if (flags != SCOREIT)
        rs_write_scorefile(outf,top_ten,NUMSCORE);
    fclose(outf);
}

/*
 * showpack:
 *      Display the contents of the hero's pack
 */

void
showpack(char *howso)
{
        reg char *iname;
        reg int cnt, packnum;
        reg struct linked_list *item;
        reg struct object *obj;

        idenpack();
        cnt = 1;
        clear();
        mvprintw(0, 0, "Contents of your pack %s:\n",howso);
        packnum = 'a';
        for (item = pack; item != NULL; item = next(item)) {
                obj = OBJPTR(item);
                iname = inv_name(obj, FALSE);
                mvprintw(cnt, 0, "%c) %s\n",packnum++,iname);
                if (++cnt >= lines - 2 && 
                    next(item) != NULL) {
                        cnt = 1;
                        mvaddstr(lines - 1, 0, morestr);
                        refresh();
                        wait_for(' ');
                        clear();
                }
        }
        mvprintw(cnt + 1,0,"--- %ld  Gold Pieces ---",purse);
        refresh();
}

void
total_winner(void)
{
    register struct linked_list *item;
    register struct object *obj;
    register long worth;
    register char c;
    register long oldpurse;

    clear();
    standout();
    addstr("                                                               \n");
    addstr("  @   @               @   @           @          @@@  @     @  \n");
    addstr("  @   @               @@ @@           @           @   @     @  \n");
    addstr("  @   @  @@@  @   @   @ @ @  @@@   @@@@  @@@      @  @@@    @  \n");
    addstr("   @@@@ @   @ @   @   @   @     @ @   @ @   @     @   @     @  \n");
    addstr("      @ @   @ @   @   @   @  @@@@ @   @ @@@@@     @   @     @  \n");
    addstr("  @   @ @   @ @  @@   @   @ @   @ @   @ @         @   @  @     \n");
    addstr("   @@@   @@@   @@ @   @   @  @@@@  @@@@  @@@     @@@   @@   @  \n");
    addstr("                                                               \n");
    addstr("     Congratulations, you have made it to the light of day!    \n");
    standend();
    addstr("\nYou have joined the elite ranks of those who have escaped the\n");
    addstr("Dungeons of Doom alive.  You journey home and sell all your loot at\n");
    addstr("a great profit and are appointed ");
    switch (player.t_ctype) {
        case C_FIGHTER: addstr("Leader of the Fighter's Guild.\n");
        when C_RANGER:  addstr("King of the Northern Land.\n");
        when C_PALADIN: addstr("King of the Southern Land.\n");
        when C_MAGICIAN:addstr("High Wizard of the Sorcerer's Guild.\n");
        when C_CLERIC:  addstr("Bishop of the Monastery.\n");
        when C_THIEF:   addstr("Leader of the Thief's Guild.\n");
        when C_MONK:    addstr("Master of the Temple.\n");
        when C_ASSASSIN: addstr("Leader of the Assassin's Guild.\n");
        when C_DRUID:   addstr("High Priest of the Monastery.\n");
        otherwise:      addstr("Town Drunk in the Tavern.\n");
    }
    mvaddstr(lines - 1, 0, spacemsg);
    refresh();
    wait_for(' ');
    clear();
    mvaddstr(0, 0, "   Worth  Item");
    oldpurse = purse;
    for (c = 'a', item = pack; item != NULL; c++, item = next(item))
    {
        obj = OBJPTR(item);
        worth = get_worth(obj);
        if (obj->o_group == 0)
            worth *= obj->o_count;
        whatis(item);
        mvprintw(c-'a'+1, 0, "%c) %6ld  %s", c, worth, inv_name(obj, FALSE));
        purse += worth;
    }
    mvprintw(c - 'a' + 1, 0,"   %5ld  Gold Pieces          ", oldpurse);
    refresh();
    writelog(pstats.s_exp + (long) purse, WINNER, '\0');
    score(pstats.s_exp + (long) purse, WINNER, '\0');
    exit_game(EXIT_ENDWIN);
}


void
delete_score(struct sc_ent top_ten[NUMSCORE], int idx)
{
    for(;idx < NUMSCORE-1;idx++)
        top_ten[idx] = top_ten[idx+1];

    top_ten[NUMSCORE-1].sc_score = 0L;
}

int
insert_score(struct sc_ent top_ten[NUMSCORE], struct sc_ent *sc)
{
    int i,j;

    if (top_ten[NUMSCORE-1].sc_score > 0)
        return(-1); /* no room */
  
    for(i = 0; i < NUMSCORE; i++) {
        if (sc->sc_score > top_ten[i].sc_score) {
            for(j = NUMSCORE-1; j > i; j--)
                top_ten[j] = top_ten[j-1];
            top_ten[i] = *sc;
            return(i);
        }
    }

    return(-1);
}

/* PCS = player-class-system (used to determines uniqueness of player) */

bool
is_pcs_match(struct sc_ent *sc1, struct sc_ent *sc2)
{
    return( (strcmp(sc1->sc_name,sc2->sc_name) == 0) &&
         (sc1->sc_ctype == sc2->sc_ctype) &&
         (strcmp(sc1->sc_system, sc2->sc_system)==0) );
}

int
count_pcs_matches(struct sc_ent top_ten[NUMSCORE], struct sc_ent *sc, 
                  int *lowest)
{
    int i, matches = 0;

    *lowest = -1;

    for(i = 0; i < NUMSCORE; i++) {
        if (is_pcs_match(sc,&top_ten[i])) {
            matches++;
            *lowest = i;
        }
    }
    return(matches);
}

int
find_most_pcs_matches(struct sc_ent top_ten[NUMSCORE], struct sc_ent *sc, 
                      int *num, int *idx)
{
    int i, matches, max_match=0, max_match_idx=-1, lowest;
    
    for(i = NUMSCORE-1; i > 0; i--) {
        matches = count_pcs_matches(top_ten,&top_ten[i],&lowest);

        if (matches > max_match) {
            max_match     = matches;
            max_match_idx = lowest;
        }
    }        

    matches = count_pcs_matches(top_ten,sc,&lowest) + 1;

    if (matches > max_match) {
        *num = matches;
        *idx = lowest;
    }
    else {
        *num = max_match;
        *idx = max_match_idx;
    }

    return(0);
}


int
add_score(struct sc_ent top_ten[NUMSCORE], struct sc_ent *sc)
{
    int idx, count;
      
    if (insert_score(top_ten,sc) == -1) { 
    /* Simple insert if space available in table */

        find_most_pcs_matches(top_ten,sc,&count,&idx);

        /* EVERY ENTRY UNIQUE,                  */
        /* INSERT IF SCORE > LOWEST MATCH SCORE */
        if (count == 1) {                               
            if (sc->sc_score > top_ten[idx].sc_score) {
                delete_score(top_ten,idx);
                insert_score(top_ten,sc);
            }
        }
        /* CURRENT PCS HAS HIGHEST DUPE COUNT   */
        /* INSERT IF SCORE > LOWEST MATCH SCORE */
        else if (is_pcs_match(sc,&top_ten[idx])) {
            if (sc->sc_score > top_ten[idx].sc_score) {
                delete_score(top_ten,idx);
                insert_score(top_ten,sc);
            }
        }
        /* UNRELATED PCS HAS HIGHEST DUPE COUNT         */
        /* DELETE LOWEST DUPE TO MAKE ROOM AND INSERT   */
        else {                                          
            delete_score(top_ten,idx); 
            insert_score(top_ten,sc);
        }
    }    
}

int
update(struct sc_ent top_ten[], unsigned long amount, short quest, char *whoami,
       short flags, short level, short monst, short ctype, char *system, 
       char *login)
{
    struct sc_ent sc;

    sc.sc_score = amount;
    sc.sc_quest = quest;
    strncpy(sc.sc_name, whoami, NAMELEN);
    sc.sc_name[NAMELEN-1] = 0;
    sc.sc_flags = flags;
    sc.sc_level = level;
    sc.sc_monster = monst;
    sc.sc_ctype = ctype;
    strncpy(sc.sc_system, system, SYSLEN);
    sc.sc_system[SYSLEN - 1] = 0;
    strncpy(sc.sc_login, login, LOGLEN);
    sc.sc_login[LOGLEN-1] = 0;

    add_score(top_ten, &sc);
    return(1);
}

