#include "alive.h"
#include "xvars.h"

#ifdef X11R5
#include <X11/Xaw/Label.h>
#include <X11/Xaw/Command.h>
#include <X11/Xaw/Box.h>
#include <X11/Xaw/Paned.h>
#else
#include <X11/Label.h>
#include <X11/Command.h>
#include <X11/Box.h>
#include <X11/VPaned.h>
#endif
#include <X11/Shell.h>

#include "LabeledPicture.h"
#include "LoadFace.h"
#include "ArrangeBox.h"
#include "MachineInfo.h"
#include <varargs.h>
#include "version.h"
#include <math.h>
#include <X11/Xresource.h>
#include <X11/StringDefs.h>

#define DEFAULT_RATIO 3.0

#ifdef BITMAP_ICON
#include "bitmap/icon.icon"
#endif

#ifndef XtRDouble
#define XtRDouble "Double"
#endif

static Widget title,vpane,exitButton,topbar;
static Widget popdownButton;
static Widget helpButton,helpScreen;
static Widget helpPane,loadBox[AiFaceNum];
static Widget helpTitle;

static double windowRatio = DEFAULT_RATIO;
extern char *OrgFile;

static AiFaceData *HelpFaces;
static AiLoadBound *HelpBound;
static AiLoadBound defaultLoadBound = {10,90,180,450,900};

static void exitDisplay(),popdown(),showHelp(),popdownHelp();

Widget mkWidget();

static XrmOptionDescRec optionTable[] = {
{"-interval",	"*interval",		XrmoptionSepArg,(caddr_t)NULL},
{"-bitmapdir",	"*bitmapFileDirectory",	XrmoptionSepArg,(caddr_t)NULL},
{"-titlefont",	"*title.font",		XrmoptionSepArg,(caddr_t)NULL},
{"-windowratio","*windowRatio",		XrmoptionSepArg,(caddr_t)NULL},
{"-nosort",	"*sortHosts",		XrmoptionNoArg, (caddr_t)"False"},
{"-loadbound",  "*loadBound",           XrmoptionSepArg,(caddr_t)NULL},
{"-org",        "*orgFile",             XrmoptionSepArg,(caddr_t)NULL},
};

static char *loadHelpText[] = {
	"Idle  ( - %4.2f)",
	"Run   ( - %4.2f)",
	"Busy  ( - %4.2f)",
	"Heavy ( - %4.2f)",
	"Fever ( - %4.2f)",
	"Crazy (%4.2f - )",
	"Down",
};

/*
 * converter from String to Double
 */
static void
convertStr2Dbl(args,numargs,from,to)
XrmValue *args;
Cardinal *numargs;
XrmValue *from;
XrmValue *to;
{
	double r;
	to->size = sizeof(double);
	r = atof(from->addr);
	to->addr = XtMalloc(sizeof(double));
	*(double*)to->addr = r;
}

/*
 * makeToplevel(): creare toplevel widget
 */
makeToplevel(argc,argv)
int *argc;
char *argv[];
{
	XtAppContext app;
	XrmDatabase db;
	XrmValue val,realVal;
	char rname[64],cname[64];
	char *type;
	char *argv0;
	
	/***** Top level *****/
	Toplevel = XtInitialize("main",
				"xloadface",
				optionTable,
				XtNumber(optionTable),
				argc,argv);
	app = XtWidgetToApplicationContext(Toplevel);
	XtAppAddConverter(app,XtRString,XtRDouble,
			  convertStr2Dbl,NULL,0);
	db = XtDatabase(XtDisplay(Toplevel));

	for (argv0 = argv[0]+strlen(argv[0])-1;
	     argv0 >= argv[0] && *argv0 != '/';
	     argv0--);
	argv0++;
	sprintf(rname,"%s.windowRatio",argv0);
	sprintf(cname,"%s.Ratio",argv0);
	if (XrmGetResource(db,rname,cname,&type,&val)) {
		XtConvert(Toplevel,type,&val,XtRDouble,&realVal);
		windowRatio = *(double*)realVal.addr;
		if (windowRatio <= 0) {
			XtAppWarning(app,"Invalid window ratio; using default value.");
			windowRatio = DEFAULT_RATIO;
		}
	}

	sprintf(rname,"%s.sortHosts",argv0);
	sprintf(cname,"%s.Sort",argv0);
	if (XrmGetResource(db,rname,cname,&type,&val)) {
		XtConvert(Toplevel,type,&val,XtRBoolean,&realVal);
		SortHosts = *(Boolean*)realVal.addr;
	}

	sprintf(rname,"%s.orgFile",argv0);
	sprintf(cname,"%s.OrgFile",argv0);
	if (XrmGetResource(db,rname,cname,&type,&val)) {
#if 0
		if (type != XtRString) {
			XtAppWarning(app,"OrgFile type is not XtRString");
		}
		else {
			OrgFile = (String)val.addr;
		}
#endif
		XtConvert(Toplevel, type, &val, XtRString, &realVal);
		OrgFile = (String)realVal.addr;
	}
}
	
/*
 * initializeWidgets(): Initialize almost all widgets and pixmaps
 */
initializeWidgets()
{
	int i;
	static char titleString[60];
	static XtCallbackRec exitCallback[] = {
		exitDisplay,	NULL,
		NULL,		NULL,
	};
	static XtCallbackRec popdownCallback[] = {
		popdown,	NULL,
		NULL,		NULL,
	};
	static XtCallbackRec helpCallback[] = {
		showHelp,	(caddr_t)&helpButton,
		NULL,		NULL,
	};
	static XtCallbackRec helpPopdownCallback[] = {
		popdownHelp,	NULL,
		NULL,		NULL,
	};
	char widgetname[64];
	char loadtext[32];
	

	/***** Pane *****/
#ifdef X11R5
	vpane = mkWidget("pane",panedWidgetClass,Toplevel,
			 XtNallowResize,	(XtArgVal)True,
			 NULL);
#else
	vpane = mkWidget("pane",vPanedWidgetClass,Toplevel,
			 XtNallowResize,	(XtArgVal)True,
			 NULL);
#endif
	
	topbar = mkWidget("topbar",boxWidgetClass,vpane,
			  NULL);

	/***** Title *****/
	strcpy(titleString,"Hosts in ");

	for (i = 0; i < DisplayOrganNum; i++) {
		strcat(titleString,DisplayOrgans[i]);
		if (i != DisplayOrganNum-1) {
			strcat(titleString,",");
		}
	}
	title = mkWidget("title",labelWidgetClass,topbar,
			 XtNlabel,titleString,
			 XtNborderWidth, 0,
			 NULL);

	/***** Buttons *****/
	exitButton = mkWidget("exitButton",commandWidgetClass,topbar,
			      XtNlabel,"Exit",
			      XtNcallback,exitCallback,
			      NULL);

	helpButton = mkWidget("helpButton",commandWidgetClass,topbar,
			      XtNlabel,"Help",
			      XtNcallback,helpCallback,
			      NULL);

	/***** Face Field *****/

	FaceField = mkWidget("Faces",boxWidgetClass,vpane,
			     NULL);

	/***** Popup Info *****/
	PopupInfo = XtCreatePopupShell("PopupInfo",transientShellWidgetClass,
				       Toplevel,NULL,NULL);
	PopupPane = mkWidget("InfoPane",arrangeBoxWidgetClass,PopupInfo,
			     AiNvertNum, 1,
			     AiNresizeParent, True,
			     NULL);
	popdownButton = mkWidget("PopdownButton",commandWidgetClass,PopupPane,
				 XtNlabel,"Push here to popdown",
				 XtNcallback,popdownCallback,
				 NULL);

	/***** Popup Help *****/
	helpScreen = XtCreatePopupShell("Help",transientShellWidgetClass,
					Toplevel,NULL,NULL);

	addFaces();
	adjustWindowSize();
	XtRealizeWidget(Toplevel);

	/***** Set Help Screen *****/
#define HELPWIDTH 200

	helpPane = mkWidget("HelpPane",boxWidgetClass, helpScreen,
			    NULL);
	sprintf(widgetname,"xloadface version %s",XLOADFACE_VERSION);
	helpTitle = mkWidget("HelpTitle",commandWidgetClass, helpPane,
			     XtNlabel, widgetname,
			     XtNwidth, HELPWIDTH,
			     XtNreverseVideo, True,
			     XtNcallback, helpPopdownCallback,
			     NULL);
	HelpFaces = AiGetDefaultFaceData(loadFaceWidgetClass);
	HelpBound = &defaultLoadBound;
	
	for (i = 0; i < AiFaceNum; i++) {
		char *label;
		sprintf(widgetname,"LoadBox%d",i);
		if (i < AiLoadBoundNum)
			sprintf(loadtext,
				loadHelpText[i],
				(double)HelpBound->value[i]/100);
		else
			sprintf(loadtext,
				loadHelpText[i],
				(double)HelpBound->value[AiLoadBoundNum-1]/100);
		label = XtMalloc(strlen(loadtext)+10);
		strcpy(label,loadtext);
		loadBox[i] = mkWidget(widgetname,labeledPictureWidgetClass,helpPane, 
				      XtNborderWidth, 0,
				      XtNwidth, HELPWIDTH,
				      AiNtextPosition, right,
				      AiNpixmap, HelpFaces[i].pixmap,
				      AiNlabel, label,
				      AiNsensitive, False,
				      NULL);
	}
}



#ifdef BITMAP_ICON
static Pixmap iconBitmap;
#endif

initializeBitmaps(w)
Widget w;
{
	int i;
	XWMHints hint;

#ifdef BITMAP_ICON
	iconBitmap = XCreateBitmapFromData(XtDisplay(w),XtWindow(w),
					   icon_bits,icon_width,icon_height);
	hint.flags = IconPixmapHint;
	hint.icon_pixmap = iconBitmap;
	XSetWMHints(XtDisplay(w),XtWindow(w),&hint);
#endif
}

freeBitmaps(w)
Widget w;
{
#ifdef BITMAP_ICON
	XFreePixmap(XtDisplay(w),iconBitmap);
#endif
}

static void popupInformation();

/*
 * Create new host widget
 */
Widget
makeHostWidget(sitedata,parent)
struct whod *sitedata;
Widget parent;
{
	static XtCallbackRec popupit[] = {
		popupInformation,	NULL,
		NULL,			NULL,
	};
	Widget newone;

	newone = mkWidget(sitedata->wd_hostname,
			  loadFaceWidgetClass,parent,
			  XtNcallback,popupit,
			  XtNborderWidth,0,
			  NULL);
	AiChangeLoad(newone,sitedata);
	return newone;
}

/*
 * Add face widgets
 */
addFaces()
{
	int i;
	Widget w;

	for (i = 0; i < WhNum; i++) {
		if ((w = AiFindWidgetByName(FaceField,Whodata[i].wd_hostname)) == NULL) {
			makeHostWidget(&Whodata[i],FaceField);
		}
		else
			AiChangeLoad(w,&Whodata[i]);
	}
}

/*
 * Resize window.
 */
adjustWindowSize()
{
	int width,height,xheight;
	int awidth,aheight;
	int w,h,vnum,hnum;
	int hsp,vsp,thsp,tvsp;
	int wd;
	int num = queryNumberOfChild(FaceField);
	int maxwidth = DisplayWidth(XtDisplay(Toplevel),DefaultScreen(XtDisplay(Toplevel)));
	int minwidth;
	WidgetList queryChild(), children;
	int i;

	if (num == 0)
		return;
	hnum = ceil(sqrt((double)num*windowRatio));
	vnum = ceil((double)num/hnum);
	queryBoxSpace(FaceField,&hsp,&vsp);
	queryBoxSpace(topbar,&thsp,&tvsp);
	children = queryChild(FaceField);
	width = height = 0;
	for (i = 0; i < num; i++) {
		queryFigure(children[i], NULL, NULL, &awidth, &aheight);
		width += awidth;
		height += aheight;
	}
	width /= num;
	height /= num;
	w = (width+hsp)*hnum+hsp;
	if (w > maxwidth) {
		hnum = (maxwidth-hsp)/(width+hsp);
		w = (width+hsp)*hnum+hsp;
	}

	queryFigure(title,NULL,NULL,&minwidth,NULL);
	queryFigure(exitButton,NULL,NULL,&awidth,NULL);
	minwidth += awidth+thsp*2;
	queryFigure(helpButton,NULL,NULL,&awidth,NULL);
	minwidth += awidth+thsp;

	if (w < minwidth) {
		hnum = ceil((double)(minwidth-hsp)/(width+hsp));
		w = (width+hsp)*hnum+hsp;
	}

	vnum = 1;
	wd = hsp;
	for (i = 0; i < num; i++) {
		queryFigure(children[i], NULL, NULL, &awidth, &aheight);
		if (wd+awidth > w) {
			wd = hsp;
			vnum++;
		}
		wd += awidth + hsp;
	}
	h = (height+tvsp)*vnum+tvsp;
	queryFigure(Toplevel, NULL, NULL, &width, &height);
	if (w < width)
		return;
	queryFigure(title,NULL,NULL,NULL,&height);
	queryFigure(exitButton, NULL,NULL,NULL,&xheight);
	if (height < xheight) height = xheight;
	queryFigure(helpButton, NULL,NULL,NULL,&xheight);
	if (height < xheight) height = xheight;
	XtResizeWidget(Toplevel, w, h+height+1+tvsp*2,
		       queryBorderWidth(Toplevel));
}

/*
 * Main Loop
 */

stay()
{
	XtMainLoop();
}


/*
 * Destroy all.
 */
static void
exitDisplay(w)
Widget w;
{
	AiCleanupLoadFace(XtDisplay(Toplevel));
	freeBitmaps(Toplevel);
	XtDestroyWidget(Toplevel);
	exit(0);
}

static Widget popupInfo;
/*
 * Popup Info Window.
 */
static void
popupInformation(w,client_data,event)
Widget w;
caddr_t client_data;
XEvent *event;
{
	XButtonEvent *bev = (XButtonEvent*)event;
	int dw = DisplayWidth(XtDisplay(w),DefaultScreen(XtDisplay(w)));
	int dh = DisplayHeight(XtDisplay(w),DefaultScreen(XtDisplay(w)));
	int x,y,i;
	static Arg popupInfoArg[2];
	XtWidgetGeometry geo;
	AiFaceData *facedata;
	AiLoadBound *loadbound;
	char *label;

	XtSetArg(popupInfoArg[0],XtNborderWidth,0);
	XtSetArg(popupInfoArg[1],AiNsiteInfo, AiLookupLoad(w));
	popupInfo = XtCreateManagedWidget("ZHOSTNAME",
					  machineInfoWidgetClass,
					  PopupPane,
					  popupInfoArg,
					  XtNumber(popupInfoArg));
	XtQueryGeometry(PopupInfo,NULL,&geo);
	if (bev->x_root+geo.width < dw)
		x = bev->x_root;
	else
		x = dw-geo.width;
	if (bev->y_root+geo.height < dh)
		y = bev->y_root;
	else
		y = dh-geo.height;
	XtMoveWidget(PopupInfo, x, y);
	XtPopup(PopupInfo, XtGrabExclusive);

	facedata = AiGetFaceData(w);
	loadbound = AiGetLoadBound(w);
	if (facedata != HelpFaces) {
		for (i = 0; i < AiFaceNum; i++)
			AiChangePixmap(loadBox[i],facedata[i].pixmap);
		HelpFaces = facedata;
	}
	for (i = 0; i < AiFaceNum; i++) {
		label = AiGetLabel(loadBox[i]);
		if (i < AiLoadBoundNum)
			sprintf(label,
				loadHelpText[i],
				(double)loadbound->value[i]/100);
		else
			sprintf(label,
				loadHelpText[i],
				(double)loadbound->value[AiLoadBoundNum-1]/100);
		AiChangeLabel(loadBox[i],label);
	}
	HelpBound = loadbound;
}

/*
 * Popdown Info Window.
 */
static void
popdown(w)
Widget w;
{
	XtDestroyWidget(popupInfo);
	XtPopdown(PopupInfo);
}

/*
 * Popup Help Window.
 */
static void
showHelp(w,client_data,event)
Widget w;
caddr_t client_data;
XEvent *event;
{
	Widget parent = *(Widget*)client_data;
	int dw = DisplayWidth(XtDisplay(w),DefaultScreen(XtDisplay(w)));
	int dh = DisplayHeight(XtDisplay(w),DefaultScreen(XtDisplay(w)));
	int x,y;
	int px,py,cw,ch;
	
	queryFigure(parent,&px,&py,NULL,NULL);
	while (parent = XtParent(parent)) {
		queryFigure(parent,&x,&y,NULL,NULL);
		px += x;
		py += y;
	}
	    
	queryFigure(helpScreen,NULL,NULL,&cw,&ch);
	
	if (px+cw < dw)
		x = px;
	else
		x = dw-cw;
	if (py+ch < dh)
		y = py;
	else
		y = dh-ch;
	XtMoveWidget(helpScreen, x, y);
	XtPopup(helpScreen, XtGrabExclusive);
}

/*
 * Popdown Info Window.
 */
static void
popdownHelp(w)
Widget w;
{
	XtPopdown(helpScreen);
}

/*
 * Create Managed Widget
 *
 * usage: mkWidget(name, class, parent,
 *                 resource-name, resource, ... , NULL);
 */
Widget
mkWidget(va_alist)
va_dcl
{
	va_list arg;
	char *name;
	WidgetClass class;
	Widget parent;
	String argname;
	XtArgVal argval;
	int n = 0;
	Arg args[20];
	
	va_start(arg);
	name   = va_arg(arg,char*);
	class  = va_arg(arg,WidgetClass);
	parent = va_arg(arg,Widget);

	while (argname = va_arg(arg,String)) {
		argval = va_arg(arg,XtArgVal);
		XtSetArg(args[n],argname,argval);
		n++;
	}
	va_end(arg);
	if (n)
		return XtCreateManagedWidget(name,class,parent,args,n);
	else
		return XtCreateManagedWidget(name,class,parent,NULL,0);
}


syntax highlighted by Code2HTML, v. 0.9.1