/*
 * SvB score file
 */
/*
 * Copyright 1992 David Lemke and Network Computing Devices
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of Network Computing Devices not be
 * used in advertising or publicity pertaining to distribution of the
 * software without specific, written prior permission.  Network Computing
 * Devices makes no representations about the suitability of this software
 * for any purpose.  It is provided "as is" without express or implied
 * warranty.
 *
 * NETWORK COMPUTING DEVICES DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
 * IN NO EVENT SHALL NETWORK COMPUTING DEVICES BE LIABLE FOR ANY SPECIAL,
 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
 * OR PERFORMANCE OF THIS SOFTWARE.
 *
 * Author:
 *		Dave Lemke
 *		lemke@ncd.com
 *
 *		Network Computing Devices, Inc
 *		350 North Bernardo Ave
 *		Mountain View, CA 94043
 *
 *	@(#)score.c	1.3	92/01/20
 *
 */

#include	<stdio.h>
#include	<X11/Xos.h>
#include	<X11/Xlib.h>
#include	<X11/Xutil.h>
#include	<sys/file.h>

extern Display *dpy;
extern Window game_win;
extern GC   text_gc;

extern char user_name[];
extern int  highscore;

extern char get_key();

static void find_personal_best();
static void draw_scores();

void        update_scores();

#define	NUM_TOP_SCORES	25
#define	SCORES_PER_USER	5

#define	SCORE_Y_INC	15
#define	TOP_SCORE_Y	30
#define	PERSONAL_SCORE_Y	(3 * TOP_SCORE_Y + (NUM_TOP_SCORES * SCORE_Y_INC))
#define	SCORE_X		10

#define	MESSAGE_Y	(PERSONAL_SCORE_Y + (SCORES_PER_USER + 1) * SCORE_Y_INC)

#ifndef SCOREFILE
#define	SCOREFILE	"/tmp/svb.scores"
#endif

typedef struct _score {
    char        name[40];
    int         score;
    int         ledge;
}           Score;

Score       top_scores[NUM_TOP_SCORES];
Score       personal[SCORES_PER_USER];
Bool        read_scores = False;

static void
wait_for_key()
{
    XEvent      ev;
    char        c;

    while (1) {
	XNextEvent(dpy, &ev);
	if (ev.type == Expose && ev.xexpose.count == 0)
	    draw_scores();
	else if (ev.type == KeyPress) {
	    c = get_key((XKeyPressedEvent *) & ev);
	    if (c == 'q')
		exit(0);
	    else if (c == 'h') {
		update_scores();
		draw_scores();
	    } else if (c == ' ' || c == 's')
		return;
	}
    }
}

static void
draw_scores()
{
    int         i,
                y;
    char        scorebuf[256];

    XClearWindow(dpy, game_win);
    sprintf(scorebuf, "%40s", "Top Scores");
    XDrawImageString(dpy, game_win, text_gc, SCORE_X, 15,
		     scorebuf, strlen(scorebuf));
    y = TOP_SCORE_Y;
    for (i = 0; i < NUM_TOP_SCORES; i++) {
	sprintf(scorebuf, "          %2d  %30s  %7d   Ledge %3d",
		i + 1, top_scores[i].name, top_scores[i].score,
		top_scores[i].ledge);
	XDrawImageString(dpy, game_win, text_gc, SCORE_X, y,
			 scorebuf, strlen(scorebuf));
	y += SCORE_Y_INC;
    }
    sprintf(scorebuf, "%40s", "Personal Top Scores");
    y = PERSONAL_SCORE_Y;
    XDrawImageString(dpy, game_win, text_gc, SCORE_X, y - 2 * SCORE_Y_INC,
		     scorebuf, strlen(scorebuf));
    for (i = 0; i < SCORES_PER_USER; i++) {
	sprintf(scorebuf, "          %2d  %30s  %7d   Ledge %3d",
		i + 1, personal[i].name, personal[i].score,
		personal[i].ledge);
	XDrawImageString(dpy, game_win, text_gc, SCORE_X, y,
			 scorebuf, strlen(scorebuf));
	y += SCORE_Y_INC;
    }

    sprintf(scorebuf, "          'q' to Quit, 's' to Start");
    XDrawImageString(dpy, game_win, text_gc, SCORE_X, MESSAGE_Y,
		     scorebuf, strlen(scorebuf));
}

static void
find_personal_best()
{
    int         i,
                j;

    for (i = 0; i < SCORES_PER_USER; i++) {
	strcpy(personal[i].name, user_name);
	personal[i].score = 0;
	personal[i].ledge = 0;
    }
    for (i = 0, j = 0; i < NUM_TOP_SCORES; i++) {
	if (strcmp(top_scores[i].name, user_name) == 0) {
	    personal[j].score = top_scores[i].score;
	    personal[j].ledge = top_scores[i].ledge;
	    j++;
	    if (j == SCORES_PER_USER)
		return;;
	}
    }
}

/*
 * reads/inits score file
 */
void
update_scores()
{
    FILE       *fp;
    int         i;
    char        buf[256];

    fp = fopen(SCOREFILE, "r");
    if (fp) {
	flock(fileno(fp), LOCK_EX);
	i = 0;
	while (fgets(buf, 256, fp)) {
	    sscanf(buf, "%s %d %d", top_scores[i].name, &top_scores[i].score,
		   &top_scores[i].ledge);
	    i++;
	}
	if (!read_scores)
	    find_personal_best();
	fclose(fp);
    } else {
	/* do reinit if already running and unreadable file */
	if (read_scores)
	    return;
	for (i = 0; i < NUM_TOP_SCORES; i++) {
	    strcpy(top_scores[i].name, "Nobody");
	    top_scores[i].score = 0;
	    top_scores[i].ledge = 0;
	}
	for (i = 0; i < SCORES_PER_USER; i++) {
	    strcpy(personal[i].name, user_name);
	    personal[i].score = 0;
	    personal[i].ledge = 0;
	}
    }
    read_scores = True;
    highscore = personal[0].score;
}

/*
 * displays all current scores
 */
void
show_scores()
{
    update_scores();
    draw_scores();
    wait_for_key();
}

static void
write_scores()
{
    FILE       *fp;
    int         i;

    fp = fopen(SCOREFILE, "w");
    if (fp) {
	flock(fileno(fp), LOCK_EX);
	for (i = 0; i < NUM_TOP_SCORES; i++)
	    fprintf(fp, "%s %d %d\n", top_scores[i].name,
		    top_scores[i].score, top_scores[i].ledge);
	fclose(fp);
    }
}

/*
 * adds score to table
 */
add_score(sc, ledge)
    int         sc;
    int         ledge;
{
    int         i,
                j;
    int         user_count = 0;
    int         replace;
    int         low_user = 0;

    update_scores();		/* make sure they're up-to-date */
    for (i = 0; i < NUM_TOP_SCORES; i++) {
	if (strcmp(top_scores[i].name, user_name) == 0) {
	    user_count++;
	    low_user = top_scores[i].score;
	    replace = i;
	}
    }

    /*
     * punt if we have the full number of scores && the new one is too low to
     * care about
     */
    if (user_count >= SCORES_PER_USER) {
	if (sc <= low_user) {
	    return;
	}
    } else {
	replace = NUM_TOP_SCORES - 1;
    }

    for (i = 0; i < NUM_TOP_SCORES; i++) {
	if (sc > top_scores[i].score) {
	    for (j = replace; j > i; j--) {
		strcpy(top_scores[j].name, top_scores[j - 1].name);
		top_scores[j].score = top_scores[j - 1].score;
		top_scores[j].ledge = top_scores[j - 1].ledge;
	    }
	    strcpy(top_scores[i].name, user_name);
	    top_scores[i].score = sc;
	    top_scores[i].ledge = ledge;
	    break;
	}
    }
    for (i = 0; i < SCORES_PER_USER; i++) {
	if (sc > personal[i].score) {
	    for (j = SCORES_PER_USER - 1; j > i; j--) {
		personal[j].score = personal[j - 1].score;
		personal[j].ledge = personal[j - 1].ledge;
	    }
	    personal[i].score = sc;
	    personal[i].ledge = ledge;
	    break;
	}
    }
    highscore = personal[0].score;
    write_scores();
}
