/*
 * weight.c : Routines for ordering hosts by "weight"
 *
 * George Ferguson, ferguson@cs.rochester.edu, 23 Apr 1993.
 * Based on a suggestion from Peter J Nilsson (pjn@ida.liu.se).
 * 13 May 1993: Add "org" to US weights and make sure others have proper
 *		US lists.
 *  7 Jun 1993: Added weights for Norway, from janl@ifi.uio.no.
 * 30 Jun 1993: Added weights for France, from Hugues.Leroy@irisa.fr
 *              Added weights for Germany, Volker.Zink@inf-wiss.uni-konstanz.de
 * 27 Jul 1993: Added weights for Israel, from amoss@cs.huji.ac.il
 */

#include <stdio.h>
#include "sysdefs.h"
#include "stringdefs.h"
#include "xtypes.h"
#include "appres.h"
#include "weight.h"
#include "hostname.h"
#include "debug.h"

#define Number(arr) (sizeof(arr) / sizeof(arr[0]))

#define ISSPACE(C) ((C) == ' ' || (C) == '\t' || (C) == '\n')
#define ISDIGIT(C) ((C) >= '0' && (C) <= '9')

#define UNKNOWN_WEIGHT 99

/*
 * Functions defined here:
 */
void initHostWeights(),reinitHostWeights();
int hostWeight();

static int parseHostWeights();

/*
 * Data defined here:
 */
static HostWeightRec *hostWeightList;
static int hostWeightListLen;

/*
 * These weight lists are used if the hostWeights resource isn't given.
 * We attempt to determine a useful one by looking at the last component
 * of the hostname, if there is one.
 * The formatting is ugly so we don't have extra spaces on the Settings
 * panel.
 */
static struct _HostWeightDefault {
    char *domain;
    char *weights;
} defaults[] = {
    /* North America, from gf (also used as default) */
    { "us", "1   edu com net gov mil us org\n\
2   ca\n\
3   uk de nl fi fr eu.net\n\
100 au nz jp" },
/* For Sweden, from Peter J Nilsson (pjn@ida.liu.se) */
    { "se", "1   se\n\
2   fi dk no\n\
10  eu.net nl fr de\n\
20  edu com gov net mil us org\n\
100 au nz jp" },
/* For Norway, from gf (based on se) */
    { "no", "1   no\n\
2   fi dk se\n\
10  eu.net nl fr de\n\
20  edu com gov net mil us org\n\
100 au nz jp" },
/* For Finland, from gf (based on se) */
    { "fi", "1   si\n\
2   se dk no\n\
10  eu.net nl fr de\n\
20  edu com gov net mil us org\n\
100 au nz jp" },
/* For the UK, from gf */
    { "uk", "1   uk\n\
2   se fi dk no nl fr de eu.net\n\
20  edu com gov net mil us org\n\
100 au nz jp" },
/* For New Zealand, from Andy.Linton@comp.vuw.ac.nz */
    { "nz", "1   nz\n\
10  edu com gov net mil us org jp\n\
20  se fi dk no nl fr de eu.net uk au" },
/* For Australia, from gf (based on nz) */
    { "au", "1   au\n\
10  edu com gov net mil us org jp\n\
20  se fi dk no nl fr de eu.net uk nz" },
/* For Japan, from gf (a total guess) */
    { "jp", "1   jp\n\
10  edu com gov net mil us org\n\
20  au nz\
30  se fi dk no nl fr de eu.net uk" },
/* For Norway, from janl@ifi.uio.no */
    { "no", "1   no\n\
2   fi dk se\n\
10  eu.net nl fr\n\
20  edu com gov net de\n\
100 au nz jp" },
/* For France, from Hugues.Leroy@irisa.fr */
    { "fr", "1   fr\n\
2   se fi dk no nl uk de eu.net\n\
20  edu com gov net mil us org\n\
100 au nz jp" },
/* For Germany, from Volker.Zink@inf-wiss.uni-konstanz.de, modified with
   comments from liebe@hrz.th-darmstadt.de. */
    { "de", "1   de\n\
2   ch uk dk nl fr eu.net\n\
10  se fi no\n\
20  edu com gov net mil us\n\
100 au nz jp" },
/* For Israel, from amoss@cs.huji.ac.il */
    { "il", "1  il\n\
2   ch it\n\
10  uk dk nl fr fi se no eu.net\n\
20  de cs es\n\
50  edu com gov net mil us ca\n\
100 au nz jp" },
};


/*	-	-	-	-	-	-	-	-	*/
void
initHostWeights()
{
    char *hostname,*dot;
    int i,found;

    if (appResources.hostWeights == NULL) {
	found = 0;	/* default if none matches */
	hostname = GetHostname();
	if ((dot=rindex(hostname,'.')) != NULL) {
	    dot += 1;
	    for (i=0; i < Number(defaults); i++) {
		if (strcasecmp(dot,defaults[i].domain) == 0) {
		    found = i;
		    break;
		}
	    }
	}
	appResources.hostWeights = XtNewString(defaults[found].weights);
    }
    reinitHostWeights();
}


/*
 * This function is called whenever the weight list changes, eg, on
 * the Settings panel as well as at startup.
 */

void
reinitHostWeights()
{
    /* Count how many entries there are */
    hostWeightListLen = parseHostWeights(appResources.hostWeights,0);
    /* Allocate the array to store them */
    if (hostWeightList != NULL)
	free((char*)hostWeightList);
    hostWeightList = (HostWeightRec *)calloc(hostWeightListLen,
					     sizeof(HostWeightRec));
    /* Rescan and parse for real */
    (void)parseHostWeights(appResources.hostWeights,1);
}

/*
 * Classes are of the form "[weight] host [host] [host] {,\n\0}"
 */

/* Each class of hosts is separated by this: */
#define ISCLASSSEP(C)	((C) == ',' || (C) == '\n')
/* Within classes, each host is separated by this: */
#define ISHOSTSEP(C)	ISSPACE(C)

static int
parseHostWeights(s,saveit)
char *s;
int saveit;
{
    char buf[256];
    int i,n,weight;

    n = 0;
    weight = 0;
    while (*s) {
	/* Skip spaces and empty classes */
	while (ISSPACE(*s) || ISCLASSSEP(*s))
	    s += 1;
	/* Get the weight, if given */
	i = 0;
	while (ISDIGIT(*s))
	    buf[i++] = *s++;
	buf[i] = '\0';
	if (saveit && i > 0) {
	    weight = atoi(buf);
	}
	/* Get the hosts in the class */
	while (*s && !ISCLASSSEP(*s)) {
	    /* Skip spaces, if any */
	    while (ISSPACE(*s) || ISHOSTSEP(*s))
		s += 1;
	    /* Get the host string */
	    i = 0;
	    while (*s && !ISHOSTSEP(*s) && !ISCLASSSEP(*s))
		buf[i++] = *s++;
	    buf[i] = '\0';
	    /* Save this host */
	    if (saveit && i > 0) {
		XtFree(hostWeightList[n].name);
		hostWeightList[n].name = XtMalloc(strlen(buf)+1);
		strcpy(hostWeightList[n].name,buf);
		hostWeightList[n].weight = weight;
		DEBUG2("Set weight of \"%s\" = %d\n",buf,weight);
	    }
	    /* Keep track of how many */
	    n += 1;
	}
    }
    /* Return total hosts */
    return(n);
}

int
hostWeight(host)
char *host;
{
    int i,offs,hostlen;

    hostlen = strlen(host);
    for (i=0; i < hostWeightListLen; i++) {
	if ((offs=hostlen-strlen(hostWeightList[i].name)) < 0)
	    offs = 0;
	if (!strcasecmp(host+offs,hostWeightList[i].name)) {
	    DEBUG3("Weight of \"%s\" (\"%s\") = %d\n",
		   host,host+offs,hostWeightList[i].weight);
	    return(hostWeightList[i].weight);
	}
    }
    return(UNKNOWN_WEIGHT);
}
