#include <X11/IntrinsicP.h>
#include <X11/StringDefs.h>
#include "LoadFaceP.h"
#include <stdio.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <math.h>

#include "bitmap/idle.icon"
#include "bitmap/run.icon"
#include "bitmap/busy.icon"
#include "bitmap/heavy.icon"
#include "bitmap/fever.icon"
#include "bitmap/crazy.icon"
#include "bitmap/down.icon"

#ifdef PACKET_LE
#define NTOHL(x)	ntonl(x)
#else
#define NTOHL(x)	(x)
#endif

static
AiFaceData DefaultFaceData[] = {
	idle_bits, idle_width, idle_height, 0,
	run_bits,  run_width,  run_height,  0,
	busy_bits, busy_width, busy_height, 0,
	heavy_bits,heavy_width,heavy_height,0,
	fever_bits,fever_width,fever_height,0,
	crazy_bits,crazy_width,crazy_height,0,
	down_bits, down_width, down_height, 0,
};

static void Redisplay(),Initialize(),Destroy(),
            LoadFaceClassPartInitialize();

static XtTimerCallbackProc updateLoad();

/* Private Data */
#define LFOffset(tag)	XtOffset(LoadFaceWidget, load_face.tag)

static XtResource resources[] = {
{AiNbitmapFileDirectory, AiCBitmapFileDirectory, XtRString,sizeof(String),
	 LFOffset(bitmap_file_directory), XtRString, NULL},
{AiNwidgetSpecificBitmap, AiCWidgetSpecificBitmap, XtRBoolean,sizeof(Boolean),
	 LFOffset(widget_specific_bitmap), XtRString, (caddr_t)"FALSE"},
{AiNinterval, AiCInterval, XtRInt, sizeof(int),
	 LFOffset(interval), XtRString, (caddr_t)"60"},
{AiNwhodDirectory, AiCWhodDirectory, XtRString,sizeof(String),
	 LFOffset(whod_directory), XtRString, (caddr_t)"/usr/spool/rwho"},
{AiNshuffle, AiCShuffle, XtRBoolean,sizeof(Boolean),
	 LFOffset(shuffle), XtRString, (caddr_t)"TRUE"},
{AiNloadBound, AiCLoadBound, AiRLoadBound, sizeof(AiLoadBound),
	 LFOffset(load_bound), XtRString, "0.1:0.9:1.8:4.5:9.0"},
};

LoadFaceClassRec loadFaceClassRec = {
{
/* core_class fields */	
    /* superclass	  	*/	(WidgetClass)&labeledPictureClassRec,
    /* class_name	  	*/	"LoadFace",
    /* widget_size	  	*/	sizeof(LoadFaceRec),
    /* class_initialize   	*/	NULL,
    /* class_part_initialize	*/	LoadFaceClassPartInitialize,
    /* class_inited       	*/	FALSE,
    /* initialize	  	*/	Initialize,
    /* initialize_hook		*/	NULL,
    /* realize		  	*/	XtInheritRealize,
    /* actions		  	*/	NULL,
    /* num_actions	  	*/	0,
    /* resources	  	*/	resources,
    /* num_resources	  	*/	XtNumber(resources),
    /* xrm_class	  	*/	NULLQUARK,
    /* compress_motion	  	*/	TRUE,
    /* compress_exposure  	*/	TRUE,
    /* compress_enterleave	*/	TRUE,
    /* visible_interest	  	*/	FALSE,
    /* destroy		  	*/	Destroy,
    /* resize		  	*/	XtInheritResize,
    /* expose		  	*/	Redisplay,
    /* set_values	  	*/	NULL,
    /* set_values_hook		*/	NULL,
    /* set_values_almost	*/	XtInheritSetValuesAlmost,
    /* get_values_hook		*/	NULL,
    /* accept_focus	 	*/	NULL,
    /* version			*/	XtVersion,
    /* callback_private   	*/	NULL,
    /* tm_table		   	*/	NULL,
    /* query_geometry		*/	NULL,
  },
};

WidgetClass loadFaceWidgetClass = (WidgetClass)&loadFaceClassRec;

#define DOWN(w)	(time(0) - NTOHL((w)->wd_recvtime) >= MIN5)
#define	MIN5	(5*60)		/* 5 minutes in second */
#define	MIN15	(15*60)		/* 15 minutes in second */
#define HOUR1	(60*60)		/* 1 hour in second    */
#define HOUR3	(60*60*3)	/* 3 hour in second    */
#define	DAY5	(5*24*60*60)	/* 5 days in second    */

#define FACE(w,n) (&((LoadFaceClassRec*)XtClass(w))->load_face_class.faces_data[n])

#define ACCESS_SUPER(w,tag) (XtSuperclass(w)->core_class.tag)
#define CALL_SUPER(w,cmd,arg) (*ACCESS_SUPER(w,cmd))arg
#define CALL_SELF(w,cmd,arg) (*XtClass(w)->core_class.cmd)arg

static int
loadLevel(w,wd)
LoadFaceWidget w;
struct whod *wd;
{
	int l;
	
	if (DOWN(wd)) {
		return AiDownFace;
	}
	
	l = NTOHL(wd->wd_loadav[0]);
	if (l < w->load_face.load_bound.value[0])
		return AiIdleFace;
	else if (l < w->load_face.load_bound.value[1])
		return AiRunFace;
	else if (l < w->load_face.load_bound.value[2])
		return AiBusyFace;
	else if (l < w->load_face.load_bound.value[3])
		return AiHeavyFace;
	else if (l < w->load_face.load_bound.value[4])
		return AiFeverFace;
	else
		return AiCrazyFace;
}

/*
 * Converter
 */
static void
convertStr2LoadBound(args,numargs,from,to)
XrmValue *args;
Cardinal *numargs;
XrmValue *from;
XrmValue *to;
{
	int i;
	char buf[32];
	char *p = from->addr;
	char *q;
	AiLoadBound *loadBound;

	loadBound = (AiLoadBound*)XtMalloc(sizeof(AiLoadBound));
	for (i = 0; i < AiLoadBoundNum; i++) {
		for (q = buf; *p != ':' && *p != '\0'; p++) {
			*(q++) = *p;
		}
		*q = '\0';
		loadBound->value[i] = atof(buf)*100;
		if (*p == ':') p++;
	}
	to->size = sizeof(AiLoadBound);
	to->addr = (caddr_t)loadBound;
}

static void
Redisplay(new)
Widget new;
{
	LoadFaceWidget w = (LoadFaceWidget)new;

	CALL_SUPER(w,expose,(new));
}

#define HERE(w,tag) (w)->core_class.tag
#define SUPER(w,tag) (w)->core_class.superclass->core_class.tag

static void
LoadFaceClassPartInitialize(wc)
LoadFaceWidgetClass wc;
{
	/* Inherit some properties */
	if (HERE(wc,actions) == NULL) {
		HERE(wc,actions) = SUPER(wc,actions);
		HERE(wc,num_actions) = SUPER(wc,num_actions);
		HERE(wc,tm_table) = SUPER(wc,tm_table);
	}
	srandom(time(0));

	XtAddConverter(XtRString, AiRLoadBound, convertStr2LoadBound, NULL, 0);
}

static void
Initialize(request,new)
Widget request,new;
{
	LoadFaceWidget w = (LoadFaceWidget)new;
	LoadFaceClassPart *class;
	Pixmap picture;
	int firstInterval;

	class = &((LoadFaceClassRec*)XtClass(w))->load_face_class;

	if (w->load_face.widget_specific_bitmap) {
		AiInitializeFaceData(w,NULL);
		picture = w->load_face.faces_data[0].pixmap;
	}
	else {
		if (!class->faces_data_initialized)
			AiInitializeFaceData(w,NULL);
		picture = class->faces_data[0].pixmap;
	}
	AiChangePixmap(w,picture,False,True);
	CALL_SUPER(new,initialize,(request,new));
	if (w->load_face.interval > 0) {
		if (w->load_face.shuffle)
			firstInterval = random()%(w->load_face.interval*2000);
		else
			firstInterval = w->load_face.interval*1000;
		w->load_face.interval_id =
			XtAppAddTimeOut(XtWidgetToApplicationContext(w),
					firstInterval,
					updateLoad,
					(caddr_t)w);
	}
	w->load_face.site_data.wd_sendtime = 0; /* It shows that this data is not set. */
}

static void
Destroy(new)
Widget new;
{
	LoadFaceWidget w = (LoadFaceWidget)new;
	int i;
	Pixmap p;

	if (w->load_face.widget_specific_bitmap) {
		for (i = 0; i < AiFaceNum; i++) {
			p = w->load_face.faces_data[i].pixmap;
			if (p)
			    XFreePixmap(XtDisplay(w),p);
		}
	}
	CALL_SUPER(w,destroy, (new));
}

static XtTimerCallbackProc
updateLoad(w)
LoadFaceWidget w;
{	FILE	*f;
	int	j;
	char	whodfile[256];
	struct whod wd;

	sprintf(whodfile,"%s/whod.%s",
		w->load_face.whod_directory,
		w->load_face.site_data.wd_hostname);
	for (j = 0; j < 1024/sizeof(struct whoent); j++)
		wd.wd_we[j].we_utmp.out_line[0] = '\0';
	if (f = fopen(whodfile,"r")) {
		fread(&wd,sizeof(struct whod),1,f);
		fclose(f);
	}
	else
		wd.wd_sendtime = wd.wd_recvtime = 0;
	AiChangeLoad(w,&wd);
	w->load_face.interval_id =
		XtAppAddTimeOut(XtWidgetToApplicationContext(w),
				w->load_face.interval*1000,
				updateLoad,
				(caddr_t)w);
}


/**** functions ****/

void
AiChangeLoad(w,loaddata)
LoadFaceWidget w;
struct whod *loaddata;
{
	int oldload = 0;
	int newload;
	AiFaceData *newFace;

	if (w->load_face.site_data.wd_sendtime) /* Is this data initialized? */
		oldload = loadLevel(w,&w->load_face.site_data);
	w->load_face.site_data = *loaddata;
	newload = loadLevel(w,loaddata);
	if (w->load_face.widget_specific_bitmap)
		newFace = &w->load_face.faces_data[newload];
	else
		newFace = FACE(w,newload);
	AiChangePixmap(w,newFace->pixmap,False,False);

	if (XtIsRealized(w) && oldload != newload)
		CALL_SELF(w, expose, (w));
}

static void
AiInitBitmapFromFile(w)
LoadFaceWidget w;
{
	static char *iconfile[] = {
		"idle.icon",
		"run.icon",
		"busy.icon",
		"heavy.icon",
		"fever.icon",
		"crazy.icon",
		"down.icon",
	};
	int i;
	int width,height,xhot,yhot;
	char fnbuf[256];
	Pixmap p;
	AiFaceData *fd;

	if (w->load_face.widget_specific_bitmap == True) {
		fd = w->load_face.faces_data;
	}
	else {
		fd = ((LoadFaceClassRec*)XtClass(w))->load_face_class.faces_data;
		((LoadFaceClassRec*)XtClass(w))->load_face_class.faces_data_initialized = True;
	}
	for (i = 0; i < AiFaceNum; i++) {
		sprintf(fnbuf,"%s/%s",w->load_face.bitmap_file_directory,iconfile[i]);
		if (XReadBitmapFile(XtDisplay(w),
				    DefaultRootWindow(XtDisplay(w)),
				    fnbuf,
				    &width,&height,
				    &p,
				    &xhot,&yhot) != BitmapSuccess) {
			sprintf(fnbuf,"%s/%s: Can't open or Invalid file",
				w->load_face.bitmap_file_directory,
				iconfile[i]);
			XtError(fnbuf);
		}
		fd[i].bit = NULL;
		fd[i].width = width;
		fd[i].height = height;
		fd[i].pixmap = p;
	}
}

void
AiInitializeFaceData(w,fd)
LoadFaceWidget w;
AiFaceData *fd;
{
	int i;
	AiFaceData *ifd;

	if (fd == NULL) {
		if (w->load_face.bitmap_file_directory != NULL) {
			AiInitBitmapFromFile(w);
			return;
		}
		fd = DefaultFaceData;
	}	
	if (w->load_face.widget_specific_bitmap == True)
		ifd = w->load_face.faces_data;
	else {
		ifd = ((LoadFaceClassRec*)XtClass(w))->load_face_class.faces_data;
		((LoadFaceClassRec*)XtClass(w))->load_face_class.faces_data_initialized = True;
	}
	for (i = 0; i < AiFaceNum; i++) {
		fd[i].pixmap = XCreateBitmapFromData(XtDisplay(w),
						     DefaultRootWindow(XtDisplay(w)),
						     fd[i].bit,
						     fd[i].width,
						     fd[i].height);
		ifd[i] = fd[i];
	}
}

void
AiCleanupLoadFace(d)
Display *d;
{
	int i;
	Pixmap p;

	for (i = 0; i < AiFaceNum; i++) {
		p = loadFaceClassRec.load_face_class.faces_data[i].pixmap;
		if (p)
		    XFreePixmap(d,p);
	}
}

struct whod *
AiLookupLoad(w)
LoadFaceWidget w;
{
	return &w->load_face.site_data;
}

AiFaceData *
AiGetDefaultFaceData(wclass)
LoadFaceWidgetClass wclass;
{
	if (!wclass->load_face_class.faces_data_initialized)
		return NULL;
	return wclass->load_face_class.faces_data;
}

AiFaceData *
AiGetFaceData(w)
LoadFaceWidget w;
{
	if (w->load_face.widget_specific_bitmap)
		return w->load_face.faces_data;
	return AiGetDefaultFaceData(XtClass(w));
}

AiLoadBound *
AiGetLoadBound(w)
LoadFaceWidget w;
{
	return &w->load_face.load_bound;
}


syntax highlighted by Code2HTML, v. 0.9.1