#include <stdio.h>
#include <string.h>
#include "lint.h"
#include "wiz_list.h"
#include "interpret.h"

/*
 * Maintain the wizards high score list about most popular castle.
 */

extern char *string_copy PROT((char *)), *xalloc PROT((int));

struct wiz_list *all_wiz;

/*
 * Sort the wiz list in ascending order.
 */

static struct wiz_list *insert(w, wl)
    struct wiz_list *w, *wl;
{
    if (wl == 0) {
	w->next = 0;
	return w;
    }
    if (w->score > wl->score) {
	wl->next = insert(w, wl->next);
	return wl;
    }
    w->next = wl;
    return w;
}

static void rebuild_list() {
    struct wiz_list *wl, *w, *new_list = 0;

    for (w = all_wiz; w; w = wl) {
	wl = w->next;
	new_list = insert(w, new_list);
    }
    all_wiz = new_list;
}

/*
 * Find the data, if it exists.
 */
static struct wiz_list *find_wiz(name)
    char *name;
{
    int length;
    struct wiz_list *wl;

    length = strlen(name);
    for (wl = all_wiz; wl; wl = wl->next)
        if (wl->length == length && strcmp(wl->name, name) == 0)
	    return wl;
    return 0;
}

/*
 * Check that a name exists. Add it, if it doesn't.
 */
struct wiz_list *add_name(str)
    char *str;
{
    struct wiz_list *wl;

    wl = find_wiz(str);
    if (wl)
        return wl;
    wl = (struct wiz_list *)xalloc(sizeof (struct wiz_list));
    str = string_copy(str);
    wl->name = str;
    wl->length = strlen(str);
    wl->score = 0;
    wl->cost = 0;
    wl->heart_beats = 0;
    wl->total_worth = 0;
    wl->next = all_wiz;
    wl->size_array = 0;
    wl->call_outs = 0;
    wl->file_name = 0;
    wl->error_message = 0;
    all_wiz = wl;
    return wl;
}

/*
 * Add score to an existing name.
 */
void add_score(name, score)
    char *name;
    int score;
{
    struct wiz_list *wl;

    wl = find_wiz(name);
    if (!wl)
        fatal("Add_score: could not find wizard %s\n", name);
    wl->score += score;
}

/*
 * This one is called at every complete walkaround of reset.
 */
void wiz_decay() {
    struct wiz_list *wl;

    for (wl = all_wiz; wl; wl = wl->next) {
        wl->score = wl->score * 99 / 100;
	wl->total_worth = wl->total_worth * 99 / 100;
	wl->cost = wl->cost * 9 / 10;
	wl->heart_beats = wl->heart_beats * 9 / 10;
    }
}

/*
 * Load the wizlist file.
 */
void load_wiz_file()
{
    char buff[1000];		/* I hate not knowing how much I need. */
    FILE *f;

    f = fopen("WIZLIST", "r");
    if (f == NULL)
        return;
    while(fgets(buff, sizeof buff, f) != NULL) {
        char *p;
	int score;

	p = strchr(buff, ' ');
	if (p == 0) {
	    fprintf(stderr, "Bad WIZLIST file.\n");
	    break;
	}
	*p = '\0';
	p++;
	if (*p == '\0') {
	    fprintf(stderr, "Bad WIZLIST file.\n");
	    break;
	}
	score = atoi(p);
	if (score > 0) {
	    (void)add_name(buff);
	    add_score(buff, atoi(p));
	}
    }
    fclose(f);
}

/*
 * Save the wizlist file.
 */
void save_wiz_file()
{
    struct wiz_list *wl;
    FILE *f;

    f = fopen("WIZLIST", "w");
    if (f == NULL) {
        fprintf(stderr, "Could not open WIZLIST for write\n");
        return;
    }
    for (wl = all_wiz; wl; wl = wl->next)
        fprintf(f, "%s %d %d\n", wl->name, wl->score, wl->total_worth);
    fclose(f);
}

void wizlist(v)
    struct value *v;
{
    struct wiz_list *wl, *this_wiz;
    int total = 0, num = 0, this_pos, total_wizards;
    extern struct object *command_giver;
    int all = 0;

    if (!command_giver)
	return;
    if (v == 0)
	v = apply("query_real_name", command_giver, 0);
    if (!v || v->type != T_STRING)
	return;
    if (strcmp(v->u.string, "ALL") == 0)
	all = 1;
    this_wiz = find_wiz(v->u.string);
    rebuild_list();
    for (num = 0, wl = all_wiz; wl; wl = wl->next) {
        total += wl->score;
	num++;
	if (wl == this_wiz)
	    this_pos = num;
    }
    total_wizards = num;
    add_message("\nWizard top score list\n\n");
    this_pos = num - this_pos + 1;
    if (total == 0)
	total = 1;
    for (wl = all_wiz; wl; wl = wl->next) {
	if (!all && num > 15 && (num < this_pos - 2 || num > this_pos + 2))
		;
	else
	    add_message("%-15s %5d %2d%% (%d)\t[%4dk,%5d %5d] %6d %d\n", wl->name,
			wl->score, wl->score * 100 / total, num,
			wl->cost / 1000,
			wl->heart_beats, wl->call_outs, wl->total_worth, wl->size_array);
	num--;
    }
    add_message("\nTotal         %7d     (%d)\n\n", total, total_wizards);
}

void remove_wiz_list() {
    struct wiz_list *wl, *w;

    for (w = all_wiz; w; w = wl) {
	free(w->name);
	wl = w->next;
	free((char *)w);
    }
}

void save_error(msg, file, line)
    char *msg;
    char *file;
    int line;
{
    struct wiz_list *wl;
    char name[100];
    char *p;
    int len;

    if (sscanf(file, "players/%s/", name) != 1)
	return;
    
    p = strchr(name, '/');
    if (p)
	*p = '\0';
    wl = add_name(name);
    if (wl->file_name)
	free(wl->file_name);
    len = strlen(file);
    wl->file_name = xalloc(len + 3); /* May add .c plus the null byte */
    strcpy(wl->file_name, file);
    /*
     * If it is a cloned object, we have to find out what the file
     * name is, and add '.c'.
     */
    p = strrchr(wl->file_name, '#');
    if (p) {
	p[0] = '.';
	p[1] = 'c';
	p[2] = '\0';
	len = p - wl->file_name + 2;
    }
    if (wl->file_name[len-1] != 'c' || wl->file_name[len-2] != '.')
	strcat(wl->file_name, ".c");
    if (wl->error_message)
	free(wl->error_message);
    wl->error_message = string_copy(msg);
    wl->line_number = line;
}

char *get_error_file(name)
    char *name;
{
    struct value *v;
    extern struct object *command_giver;
    struct wiz_list *wl;

    wl = add_name(name);
    /*
     * The error_message is used as a flag if there has been any error.
     */
    if (wl->error_message == 0) {
	add_message("No error.\n");
	return 0;
    }
    add_message("%s line %d: %s\n", wl->file_name, wl->line_number,
		wl->error_message);
    free(wl->error_message);
    wl->error_message = 0;
    return wl->file_name;
}