#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>
#include<sys/time.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<X11/Intrinsic.h>
#include<X11/StringDefs.h>
#include<X11/Shell.h>
#include <X11/Xaw/Command.h>
#include <X11/Xaw/Label.h>
#include <X11/Xaw/Form.h>
#include <X11/Xaw/AsciiText.h>

/* Copyright 1997 Tommy Johnson All Rights Reserved.
**
** Permission is hereby granted to copy, reproduce, redistribute or otherwise
** use this software as long as: there is no monetary profit gained
** specifically from the use or reproduction of this software, it is not
** sold, rented, traded or otherwise marketed, and this copyright notice is
** included prominently in any copy made.
**
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. ANY USE OF THIS
** SOFTWARE IS AT THE USER'S OWN RISK.
*/

char *XtMalloc(Cardinal);
char *XtRealloc(char *,Cardinal);

static char *fallback[] = {
	"mandel.title: Parallel Mandelbrott",
	"mandel.main.xcoordl.label: Real Coord:",
	"mandel.main.ycoordl.label: Imaginary Coord:",
	"mandel.main.scoordl.label: Side:",
	"mandel.main.maxintl.label: Max Interation:",
	"mandel.main.zoomin.label: Zoom In",
	"mandel.main.zoomout.label: Zoom Out",
	"mandel.main.zoomhome.label: Return to origin",
	"mandel.main.xcoord.string: -2.0",
	"mandel.main.ycoord.string: -2.0",
	"mandel.main.scoord.string: 4.0",
	"mandel.main.maxint.string: 2000",
	"mandel.main.timel.label: Time to compute:",
	"mandel.main.time.label: ------",
	NULL
	};

XtAppContext app;
Widget shell,form,image,zoomin,zoomout,zoomhome,start,quitw;
Widget xcoord,xcoordl,ycoord,ycoordl,scoord,scoordl,mi,mil,timew,timewl;
GC gc=0,boxgc=0;
XImage *ximage=NULL;
Display *display;

float sx,sy,ss;
float bx,by,bs;
int maxint,siz;
unsigned int *rawimage=NULL;
unsigned char *imagedat=NULL;
pid_t kid;
int thinking=0;

void (*convertimage)(unsigned int *,unsigned char *,int);

int compute[2];   /* pipe to the master parallel program */

int mstate;   /* mouse state, for drags, etc. */
int mx,my;
#define MUP 0
#define MSQUARE 1

struct timeval starttime;

void rawto32(unsigned int *raw, unsigned char *img,int len);
void rawto24(unsigned int *raw, unsigned char *img,int len);
void rawto16(unsigned int *raw, unsigned char *img,int len);
void rawto8(unsigned int *raw, unsigned char *img,int len);
void redraw_event(Widget dummyw,Widget wig,XExposeEvent *ev);

void press_event(Widget w,void *dat,XButtonEvent *ev);
void release_event(Widget w,void *dat,XButtonEvent *ev);
void motion_event(Widget w,void *dat,XButtonEvent *ev);

void press_event(Widget w,void *dat,XButtonEvent *ev)
{
	mstate=MSQUARE;
	mx=ev->x;
	my=ev->y;
}

void release_event(Widget w,void *dat,XButtonEvent *ev)
{
	int x1,y1,x2,y2,t;
	XGCValues gcval;
	Pixel bg,fg;

	if (!boxgc)
	{
		boxgc=XCreateGC(display,XtWindow(image),0,0);
		XtVaGetValues(image,XtNbackground,&bg,XtNforeground,&fg,NULL);
		XSetForeground(display,gc,bg);
		XSetBackground(display,gc,fg);
		XSetFunction(display,gc,GXcopy);
	}

	mstate=MUP;
	x1=mx;
	y1=my;
	x2=ev->x;
	y2=ev->y;

	if (x1>x2)
	{
		t=x2;
		x2=x1;
		x1=t;
	}
	if (y1>y2)
	{
		t=y2;
		y2=y1;
		y1=t;
	}

	y2=y1+(x2-x1);

	XDrawRectangle(display,XtWindow(image),boxgc,x1,y1,x2-x1,y2-y1);

	bx=sx+(ss*((double)x1/(double)siz));
	by=sy+(ss*((double)y1/(double)siz));
	bs=ss*((double)(x2-x1)/(double)siz);
}

void motion_event(Widget w,void *dat,XButtonEvent *ev)
{
}


void quit()
{
	char buff[1024];
	int i;

	if (gc)
		XFreeGC(display,gc);
	if (boxgc)
		XFreeGC(display,boxgc);
	XDestroyImage(ximage);

	sprintf(buff,"0 %d %d %f %f %f",siz,maxint,sx,sy,ss);
	write(compute[1],buff,strlen(buff));

	sleep(5);
	kill(kid,SIGINT);
	wait(&i);
	exit(0);
}

void startcalc()
{
	char buff[1024];
	char *p;
	struct timezone tzp={0,0};

	if (thinking)
		return;
	thinking=1;

	XtVaGetValues(mi,XtNstring,&p,NULL);
	sscanf(p,"%d",&maxint);
	if (maxint<1)
		maxint=200;

	bx=sx;
	by=sy;
	bs=ss;

	sprintf(buff,"%f",sx);
	XtVaSetValues(xcoord,XtNstring,buff,NULL);
	sprintf(buff,"%f",sy);
	XtVaSetValues(ycoord,XtNstring,buff,NULL);
	sprintf(buff,"%f",ss);
	XtVaSetValues(scoord,XtNstring,buff,NULL);
	sprintf(buff,"%d",maxint);
	XtVaSetValues(mi,XtNstring,buff,NULL);
	sprintf(buff,"1 %d %d %f %f %f\n",siz,maxint,sx,sy,ss);
	write(compute[1],buff,strlen(buff));
	gettimeofday(&starttime,&tzp);
}

void start_proc()
{
	char *p;
	XtVaGetValues(xcoord,XtNstring,&p,NULL);
	sscanf(p,"%f",&sx);
	XtVaGetValues(ycoord,XtNstring,&p,NULL);
	sscanf(p,"%f",&sy);
	XtVaGetValues(scoord,XtNstring,&p,NULL);
	sscanf(p,"%f",&ss);
	startcalc();
}

void zoomin_proc()
{
	sx=bx;
	sy=by;
	ss=bs;
	startcalc();
}

void zoomout_proc()
{
	sx=sx-(bx-sx)*(ss/bs);
	sx=sy-(by-sy)*(ss/bs);
	ss=ss*(ss/bs);
	startcalc();
}

void zoomhome_proc()
{
	sx=bx=-2.0;
	sy=by=-2.0;
	ss=bs=4.0;
	startcalc();
}

void gotimagedat()
{
	int len,x,y,i,l;
	unsigned char buff[4096];
	struct timeval fintime;
	struct timezone tzp={0,0};
	float d;

	read(compute[0],buff,3*4);   /* header... */
	y=(buff[0*4]<<24)|(buff[0*4+1]<<16)|(buff[0*4+2]<<8)|(buff[0*4+3]);
	x=(buff[1*4]<<24)|(buff[1*4+1]<<16)|(buff[1*4+2]<<8)|(buff[1*4+3]);
	len=(buff[2*4]<<24)|(buff[2*4+1]<<16)|(buff[2*4+2]<<8)|(buff[2*4+3]);

	if (x==-1)
	{
		gettimeofday(&fintime,&tzp);
		d=((double)fintime.tv_sec+(double)fintime.tv_usec/1000000.0)-((double)starttime.tv_sec+(double)starttime.tv_usec/1000000.0);
		sprintf(buff,"%f",d);
		XtVaSetValues(timew,XtNlabel,buff,NULL);
		redraw_event(NULL,NULL,NULL);
		thinking=0;
	}
	while(len)
	{
		l=read(compute[0],buff,((len*4)>sizeof(buff))?sizeof(buff):(len*4));
		l=l/4;
		for(i=0;i<l;i++)
		{
			rawimage[y*siz+x]=(buff[i*4]<<24)|(buff[i*4+1]<<16)|(buff[i*4+2]<<8)|(buff[i*4+3]);
			x++;
		}
		len-=l;
	}
	/*
	redraw_event(NULL,NULL,NULL);
	*/
}

void init_image()
{
	static int gcexist=0;
	int x,y;
	unsigned int w,h,b,d;
	Window rootwin;

	if (!gcexist)
	{
		gc=XCreateGC(display,XtWindow(image),0,0);
		gcexist=1;
	}
	XGetGeometry(display,XtWindow(image),&rootwin,&x,&y,&w,&h,&b,&d);
	siz=w;
	if (siz<h)
		siz=h;
	XtResizeWidget(image,siz,siz,b);
	XGetGeometry(display,XtWindow(image),&rootwin,&x,&y,&w,&h,&b,&d);
	siz=w;
	if (siz>h)
		siz=h;
	if (ximage)
		XDestroyImage(ximage);
	rawimage=(int *)XtRealloc((char*)rawimage,siz*siz*sizeof(int));
	imagedat=XtMalloc(siz*siz*4);   /* not realloc, XDestroyImage does the free */
	ximage=XCreateImage(display,None,d,ZPixmap,0,imagedat,siz,siz,8,0);
	fprintf(stderr,"%d bpp\n",d);
	switch(d)
	{
		case 32: convertimage=rawto32;  break;
		case 24: convertimage=rawto24;  break;
		case 16: convertimage=rawto16;  break;
		case 8:   convertimage=rawto8;  break;
		default:
			fprintf(stderr,"Unsupported visual!\n");
	}
}

/* Note that this is also called by other callbacks, with all NULL args */
void redraw_event(Widget dummyw,Widget wig,XExposeEvent *ev)
{
	static int inited=0;

	if (!inited)
	{
		inited=1;
		init_image();
	}

	if (ev)
	{
		if ((ev->type==Expose) && (ev->count==0))
		{
			(*convertimage)(rawimage,imagedat,siz*siz);
			XPutImage(display,XtWindow(image),gc,ximage,0,0,0,0,siz,siz);
		}
		if (ev->type==ConfigureNotify)
		{
			init_image();
			(*convertimage)(rawimage,imagedat,siz*siz);
			XPutImage(display,XtWindow(image),gc,ximage,0,0,0,0,siz,siz);
		}
	}
	else   /* else ev==NULL, and its an internal redraw command */
	{    
		(*convertimage)(rawimage,imagedat,siz*siz);
		XPutImage(display,XtWindow(image),gc,ximage,0,0,0,0,siz,siz);
	}
}

int main(int argc, char *argv[])
{
	int i;
	int red[2],writ[2];

	bx=sx=-2.0;
	by=sy=-2.0;
	bs=ss=4.0;
	siz=640;
	maxint=2000;

	pipe(red);
	pipe(writ);

	compute[0]=writ[0];
	compute[1]=red[1];
	switch (kid=fork())
	{
		case 0:
			close(0);
			dup(red[0]);
			close(1);
			dup(writ[1]);
			close(red[0]);
			close(red[1]);
			close(writ[0]);
			close(writ[1]);
			i=execlp("mpirun","mpirun","-np","8","compute",NULL);
			perror("execlp:");
			/* not reached...  hopefully */
			exit(1);
			break;
		case -1:
			fprintf(stderr,"fork failed\n");
			exit(1);
			break;
	}

	rawimage=(int*)XtMalloc(siz*siz*sizeof(int));

	for(i=0;i<siz*siz;i++)
		rawimage[i]=i;

	XtSetLanguageProc(NULL,NULL,NULL);
	shell=XtVaOpenApplication(&app,"mandel",0,0,&argc,argv,fallback,topLevelShellWidgetClass,(String)0);	
	form=XtVaCreateManagedWidget("main",formWidgetClass,shell,NULL);
	xcoordl=XtVaCreateManagedWidget("xcoordl",labelWidgetClass,form,NULL);
	xcoord=XtVaCreateManagedWidget("xcoord",asciiTextWidgetClass,form,XtNfromHoriz,xcoordl,XtNeditType,XawtextEdit,XtNresize,XawtextResizeWidth,NULL);
	ycoordl=XtVaCreateManagedWidget("ycoordl",labelWidgetClass,form,XtNfromHoriz,xcoord,NULL);
	ycoord=XtVaCreateManagedWidget("ycoord",asciiTextWidgetClass,form,XtNfromHoriz,ycoordl,XtNeditType,XawtextEdit,XtNresize,XawtextResizeWidth,NULL);
	scoordl=XtVaCreateManagedWidget("scoordl",labelWidgetClass,form,XtNfromHoriz,ycoord,NULL);
	scoord=XtVaCreateManagedWidget("scoord",asciiTextWidgetClass,form,XtNfromHoriz,scoordl,XtNeditType,XawtextEdit,XtNresize,XawtextResizeWidth,NULL);
	mil=XtVaCreateManagedWidget("maxintl",labelWidgetClass,form,XtNfromHoriz,scoord,NULL);
	mi=XtVaCreateManagedWidget("maxint",asciiTextWidgetClass,form,XtNfromHoriz,mil,XtNeditType,XawtextEdit,XtNresize,XawtextResizeWidth,NULL);
	timewl=XtVaCreateManagedWidget("timel",labelWidgetClass,form,XtNfromHoriz,mi,NULL);
	timew=XtVaCreateManagedWidget("time",labelWidgetClass,form,XtNfromHoriz,timewl,NULL);

	image=XtVaCreateManagedWidget("image",coreWidgetClass,form,XtNwidth,siz,XtNheight,siz,XtNfromVert,xcoordl,NULL);
	zoomin=XtVaCreateManagedWidget("zoomin",commandWidgetClass,form,XtNfromVert,image,NULL);
	zoomout=XtVaCreateManagedWidget("zoomout",commandWidgetClass,form,XtNfromVert,image,XtNfromHoriz,zoomin,NULL);
	zoomhome=XtVaCreateManagedWidget("zoomhome",commandWidgetClass,form,XtNfromVert,image,XtNfromHoriz,zoomout,NULL);
	start=XtVaCreateManagedWidget("start",commandWidgetClass,form,XtNfromVert,image,XtNfromHoriz,zoomhome,NULL);
	quitw=XtVaCreateManagedWidget("quit",commandWidgetClass,form,XtNfromVert,zoomin,NULL);

	XtAddCallback(quitw,XtNcallback,(XtCallbackProc)quit,NULL);
	XtAddCallback(start,XtNcallback,(XtCallbackProc)start_proc,NULL);
	XtAddCallback(zoomin,XtNcallback,(XtCallbackProc)zoomin_proc,NULL);
	XtAddCallback(zoomout,XtNcallback,(XtCallbackProc)zoomout_proc,NULL);
	XtAddCallback(zoomhome,XtNcallback,(XtCallbackProc)zoomhome_proc,NULL);

	display=XtDisplay(image);
	XtAddEventHandler(image,ExposureMask|StructureNotifyMask,False,(XtEventHandler)redraw_event,NULL);
	XtAddEventHandler(image,ButtonPressMask,FALSE,(XtEventHandler)press_event,NULL);
	XtAddEventHandler(image,ButtonReleaseMask,FALSE,(XtEventHandler)release_event,NULL);
	XtAddEventHandler(image,ButtonMotionMask,FALSE,(XtEventHandler)motion_event,NULL);

	XtAppAddInput(app,compute[0],(XtPointer)(XtInputReadMask),gotimagedat,NULL);

	XtRealizeWidget(shell);
	XtAppMainLoop(app);
	/* never reached...   */
	return 0;
}



void rawto32(unsigned int *raw, unsigned char *img,int len)
{
	int i;

	for(i=0;i<len;i++)
	{
		img[i*4]=raw[i]&0xff;
		img[i*4+1]=raw[i]>>8;
		img[i*4+2]=raw[i]>>16;
		img[i*4+3]=0;
	}
}

void rawto24(unsigned int *raw, unsigned char *img,int len)
{
	int i;

	for(i=0;i<len;i++)
	{
		img[i*3]=raw[i]&0xff;
		img[i*3+1]=raw[i]>>8;
		img[i*3+2]=0xff;
	}
}

void rawto16(unsigned int *raw, unsigned char *img,int len)
{
	int i;

	for(i=0;i<len;i++)
	{
		img[i*2]=255-(raw[i]&0xff);
		img[i*2+1]=((raw[i]>>3)&0x70)|((raw[i]&1)?0x1F:0);
	}
}

void rawto8(unsigned int *raw, unsigned char *img,int len)
{
	int i;

	for(i=0;i<len;i++)
	{
		img[i]=(raw[i]&0x01<<7)|raw[i];
	}
}
