/*
 * This file is a product of Sun Microsystems, Inc. and is provided for
 * unrestricted use provided that this legend is included on all tape
 * media and as a part of the software program in whole or part.  Users
 * may copy or modify this file without charge, but are not authorized to
 * license or distribute it to anyone else except as part of a product
 * or program developed by the user.
 * 
 * THIS FILE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
 * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
 * 
 * This file is provided with no support and without any obligation on the
 * part of Sun Microsystems, Inc. to assist in its use, correction,
 * modification or enhancement.
 * 
 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS FILE
 * OR ANY PART THEREOF.
 * 
 * In no event will Sun Microsystems, Inc. be liable for any lost revenue
 * or profits or other special, indirect and consequential damages, even
 * if Sun has been advised of the possibility of such damages.
 * 
 * Sun Microsystems, Inc.
 * 2550 Garcia Avenue
 * Mountain View, California  94043
 */
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xos.h>
#include <stdio.h>
#include "audio.h"

#define w_WIDTH 400
#define w_HEIGHT 200
#define BUT_HEIGHT	30
#define BUT_WIDTH	80
#define MARGIN 20
#define NUM_LEVELS 22
#define RIGHT_EDGE  (w_WIDTH - MARGIN - BUT_WIDTH)
#define V_SIZE  ((w_WIDTH - MARGIN - MARGIN)/NUM_LEVELS)

#define TRUE    1

XFontStruct	*xfont = NULL ;
int		waiting_for_reply = 1;
int		talking = 0, quit_now = 0, same = 0;
extern		char	* malloc();
XRectangle	*vol_rects = NULL ;
XRectangle	reply_rect= {MARGIN, MARGIN, BUT_WIDTH,BUT_HEIGHT};
XRectangle	quit_rect= {RIGHT_EDGE, MARGIN, BUT_WIDTH,BUT_HEIGHT};

typedef	struct	{
	Display		*xdisplay;
	Window		xwindow;
	unsigned        xscreen;
	GC              gc, gc_event;
	int		play_vol, rec_vol;
}	Windata;
Windata		remote,local;
char	mesg_string[100];
char		*localname, *remotename;

main(argc, argv)
int     argc;
char    **argv;
{
int	num_sec;
int	loop;
char	*getenv();
	if (argc > 1)
	{
		if (!(remote.xdisplay = XOpenDisplay( argv[1] ))) {
			(void)printf("Open Display Error:");
			(void)printf("Cannot open %s\n",argv[1]);
			exit(1);
		}
		remotename = argv[1];
	}
	else
	{
		(void)printf("must specify a remote host\n");
		(void)printf("opening default host %s.\n",
				getenv("DISPLAY"));
		if (!(remote.xdisplay = XOpenDisplay( (char *)NULL ))) {
			(void)printf("OPEN DISPLAY ERROR\N");
			exit(1);
		}
		same= 1;
		remotename = getenv("DISPLAY");
	}
	localname = getenv("DISPLAY");
        local.xdisplay = XOpenDisplay((char *)0);
	sprintf(mesg_string,"message from %s",localname);
	open_window(&remote, mesg_string);
	init_window(&remote);
	start_audio();	/* opens both displays and set up audio*/
	sleep(1);
	num_sec = ring(remote.xdisplay);
	num_sec = ring(local.xdisplay);
        (void)fprintf(stderr,"will ring for %d seconds.\n",num_sec);
	while ((waiting_for_reply)&&( num_sec--))
	{
		(void)printf("waiting_for_reply =%d.\n",num_sec);
		for (loop = 0; loop < 1000000 ; loop += 50000)
		{
			wait_events(remote);
			usleep(50000);
		}
		if (quit_now)
			break;
	}
        XAudioPlayOff(remote.xdisplay,1);
	XAudioPlayOff(local.xdisplay,1);
	if (!waiting_for_reply)
	{
		(void)fprintf(stderr,"ringing answered.\n");
		reply_window();
		rec_n_play(local, remote);	/* also checks on events */
		XAudioPlayOff(local.xdisplay,1);
		XAudioPlayOff(remote.xdisplay,1);
	}
	if (!same)
		clean_up(local.xdisplay);
	clean_up(remote.xdisplay);
	free(vol_rects);
	if (!waiting_for_reply)	/* answered */
		quit_x(local);
	quit_x(remote);
}

/* clean up the graphics */
quit_x(xwin)
Windata	 xwin;
{
	XFlush(xwin.xdisplay);
	XFreeGC(xwin.xdisplay,xwin.gc);
	XFreeGC(xwin.xdisplay,xwin.gc_event);
	XCloseDisplay(xwin.xdisplay);
}

/*
	open a window with events activated
	and fonts loaded
*/
open_window(xwin, mesg)
Windata	 *xwin;
char	*mesg;
{
    XSetWindowAttributes window_attrb;
    unsigned long        select_events;
    XSizeHints           shints;
    XWMHints		wmhints;

    window_attrb.event_mask = ExposureMask;

    shints.flags = PPosition | PSize;
    shints.x = 300;
    shints.y = 300;
    shints.width = w_WIDTH;
    shints.height = w_HEIGHT;

	xwin->xscreen = XDefaultScreen( xwin->xdisplay );
    xwin->xwindow = XCreateWindow( xwin->xdisplay, RootWindow( xwin->xdisplay, xwin->xscreen ),
			     shints.x, shints.y,
			     shints.width, shints.height,
			     1, 0,
			     CopyFromParent,
			     CopyFromParent,
			     NULL,
			     &window_attrb);

    XSetNormalHints(xwin->xdisplay, xwin->xwindow, &shints);

    XStoreName(xwin->xdisplay, xwin->xwindow,mesg);
    XMapWindow (xwin->xdisplay,xwin->xwindow);

    wmhints.flags = InputHint;
    wmhints.input = True;
    XSetWMHints(xwin->xdisplay, xwin->xwindow, &wmhints);
    XFlush(xwin->xdisplay);
    /* init graphics inputs */
    select_events = PointerMotionMask |
		      ExposureMask |
		      ButtonPressMask |
		      ButtonReleaseMask | 
		      ResizeRedirectMask | 
		      KeyPressMask |
		      FocusChangeMask;

    XSelectInput( xwin->xdisplay, xwin->xwindow, select_events);
    load_font(xwin->xdisplay);
}

/*
	look at the pending events
*/
wait_events(xwin)
Windata	xwin;
{
    XEvent               xmessage;

        if (!XCheckWindowEvent (xwin.xdisplay, xwin.xwindow,
			ButtonPressMask|ExposureMask ,
			&xmessage))
		return ;
	if (xmessage.xany.window != xwin.xwindow)
	    (void)printf("Event not in window\n");
	switch (xmessage.type) {
	    case ButtonPress:
		Check_event(xwin,xmessage);
		break;
	    case ButtonRelease:
		break;
	    case KeyPress:
		break;
	    case MotionNotify:
		break;
	    case Expose:
		make_rects(xwin);
		break;
	    case NoExpose:
		break;
	    case ResizeRequest:
		(void)printf("Resize\n");
		break;
            case FocusIn:
		break;
	    case FocusOut:
		break;
            default:
		break;
        }
}

/*
	Set up graphics for each window
*/
init_window(xwin)
Windata	*xwin;
{
XColor screen_def_blue, exact_def_blue;
XColor screen_def_red, exact_def_red;
int	i,j;

	xwin->gc = XCreateGC( xwin->xdisplay, xwin->xwindow, 0,0);
	XAllocNamedColor(xwin->xdisplay, DefaultColormap(xwin->xdisplay, DefaultScreen(xwin->xdisplay)), "blue",
		&screen_def_blue,  &exact_def_blue);
	XAllocNamedColor(xwin->xdisplay, DefaultColormap(xwin->xdisplay, DefaultScreen(xwin->xdisplay)), "red",
		&screen_def_red,  &exact_def_red);
	XSetForeground(xwin->xdisplay, xwin->gc, screen_def_red.pixel);
	XSetBackground(xwin->xdisplay, xwin->gc, screen_def_blue.pixel);

	xwin->gc_event = XCreateGC( xwin->xdisplay, xwin->xwindow, 0,0);
	XSetBackground(xwin->xdisplay, xwin->gc_event, screen_def_red.pixel);
	XSetForeground(xwin->xdisplay, xwin->gc_event, screen_def_blue.pixel);
	if (!vol_rects)
		vol_rects = (XRectangle *)malloc((NUM_LEVELS * sizeof(XRectangle)));
	for(i = MARGIN,j=0 ; j < NUM_LEVELS; i += V_SIZE,j++ ) 
	{
		vol_rects[j].x = i;
		vol_rects[j].y = 0;
		vol_rects[j].width = V_SIZE -1;
		vol_rects[j].height = V_SIZE;
	}
	XFlush(xwin->xdisplay);
}

/*
	draw the window
*/
make_rects(xwin)
Windata	xwin;
{
int	y_pos;
	draw_reply_rect(xwin, talking);
	draw_quit_rect(xwin, 0);
			/* play volume */
	y_pos = draw_vol_rects(xwin,0, xwin.play_vol - MIN_VOLUME );
	draw_text(xwin, "play (local)", w_WIDTH/2 , y_pos - MARGIN/2   );
			/* record volume */
	y_pos = draw_vol_rects(xwin,1, xwin.rec_vol - MIN_VOLUME );
	draw_text(xwin, "record (remote)", w_WIDTH/2 , y_pos - MARGIN/2 );

	draw_text(xwin, "soft", MARGIN + 5, y_pos + V_SIZE + MARGIN/2 );
	draw_text(xwin, "loud", RIGHT_EDGE +25, y_pos + V_SIZE + MARGIN/2 );
}

draw_reply_rect(xwin,i)
Windata	xwin;
{
	if (!i)
		XDrawRectangles(xwin.xdisplay, xwin.xwindow, xwin.gc, reply_rect,1);
	else
		XFillRectangles(xwin.xdisplay, xwin.xwindow, xwin.gc_event, reply_rect,1);
	draw_text(xwin,"reply", reply_rect.x + 15,reply_rect.y + 15 );
}

draw_quit_rect(xwin,i)
Windata	xwin;
{
	if (!i)
		XDrawRectangles(xwin.xdisplay, xwin.xwindow, xwin.gc, quit_rect,1);
	else
		XFillRectangles(xwin.xdisplay, xwin.xwindow, xwin.gc_event, quit_rect,1);
	draw_text(xwin,"quit", quit_rect.x + 15,quit_rect.y + 15 );
}

/* 
draw the row of volume leds for both record and play.
*/
draw_vol_rects(xwin,rec,i)
Windata	xwin;
{
int	j;
int     y_pos;
	if (rec)
	{
	y_pos = (V_SIZE + (MARGIN << 2 )+ BUT_HEIGHT);
	for(j = 0; j< NUM_LEVELS;j++)
		vol_rects[j].y = y_pos;
	XFillRectangles(xwin.xdisplay, xwin.xwindow, xwin.gc, vol_rects, NUM_LEVELS);
	XFillRectangle(xwin.xdisplay, xwin.xwindow, xwin.gc_event,
		vol_rects[i].x,
		vol_rects[i].y,
		vol_rects[i].width,
		vol_rects[i].height);
	}
	else
	{
		y_pos = ( MARGIN << 1) + BUT_HEIGHT ;
		for(j = 0; j< NUM_LEVELS;j++)
			vol_rects[j].y = y_pos;
		XFillRectangles(xwin.xdisplay, xwin.xwindow, xwin.gc,
			vol_rects, NUM_LEVELS);
		XFillRectangle(xwin.xdisplay, xwin.xwindow, xwin.gc_event,
			vol_rects[i].x,
			vol_rects[i].y,
			vol_rects[i].width,
			vol_rects[i].height);
	}
	return(y_pos);
}

load_font(xdisplay)
Display *xdisplay;
{
	char	*fontname = "9x15";
	if ((xfont= XLoadQueryFont (xdisplay, fontname)) == NULL)
	{
		(void)fprintf(stderr,"can't open font %s.\n", fontname);
		exit(0);
	}
}

draw_text(xwin,string,x,y)
char	*string;
Windata	xwin;
{
	XDrawString( xwin.xdisplay, xwin.xwindow, xwin.gc, x,y, string,strlen(string));
}

/* xmessage = event from remote host,
	change attributes of remote host as apropriate .
*/
Check_event(xwin,xmessage)
Windata		xwin;
XEvent		xmessage;
{
int	i, y_pos;
	/* reply */	
	if ((xmessage.xbutton.y > MARGIN )&&
		(xmessage.xbutton.y < MARGIN + BUT_HEIGHT))
	{
		if ((xmessage.xbutton.x > MARGIN)&& 
			(xmessage.xbutton.x < MARGIN+ BUT_WIDTH) )
		{
			if (talking) /* only once */
				return ;
			talking = 1;
			waiting_for_reply = 0;
			draw_reply_rect(xwin, talking);
			return ;
		}
		else	 /* quit */	
		if ((xmessage.xbutton.x > RIGHT_EDGE)&& 
			(xmessage.xbutton.x < RIGHT_EDGE + BUT_WIDTH) )
		{
			(void)fprintf(stderr,"quit!\n");
			talking = 0;
			draw_quit_rect(xwin,1);
			quit_now = 1;
			return ;
		}
	}

	/* play volume */	
	y_pos = MARGIN + MARGIN + BUT_HEIGHT ;
	if ((xmessage.xbutton.y > y_pos )&&
		(xmessage.xbutton.y < y_pos + V_SIZE ))
	{
		for (i=0;i<NUM_LEVELS; i++)
		{
		if ((xmessage.xbutton.x > vol_rects[i].x )&&
			(xmessage.xbutton.x < vol_rects[i].x + V_SIZE))
			{
				change_volume( xwin,0,i);
				break;
			}
		}
	}

	/* record volume */	
	y_pos += (V_SIZE + MARGIN + MARGIN);
	if ((xmessage.xbutton.y > y_pos )&&
		(xmessage.xbutton.y < y_pos + V_SIZE ))
	{
		for (i=0;i<NUM_LEVELS; i++)
		{
		if ((xmessage.xbutton.x > vol_rects[i].x )&&
			(xmessage.xbutton.x < vol_rects[i].x + V_SIZE))
			{
				change_volume( xwin,1,i);
				break;
			}
		}
	}
}

change_volume(xwin,rec,i)
Windata	xwin;
{
	if (rec)
	{
	Windata	*xrem;
		xrem = ( xwin.xdisplay == remote.xdisplay )?&local:&remote;
		xrem->rec_vol = i + MIN_VOLUME ;
		XAudioSetVolume(xrem->xdisplay, xrem->play_vol, xrem->rec_vol);
	}
	else
	{
		xwin.play_vol = i  + MIN_VOLUME;
		XAudioSetVolume(xwin.xdisplay, xwin.play_vol, xwin.rec_vol);
	}
	(void)draw_vol_rects(xwin, rec, i);
}

start_audio()
{
        /* remote host */
        if (XAudioOpen(remote.xdisplay) < 1)
        {
                (void)fprintf(stderr,"cannot open host audio\n");
                exit(-1);
        }
        XAudioSetVolume(remote.xdisplay,remote.play_vol,remote.rec_vol);

        /* local host */
        if (XAudioOpen(local.xdisplay) < 1)
        {
                (void)fprintf(stderr,"cannot open remote host audio\n");
                exit(-1);
        }
        XAudioSetVolume(local.xdisplay,local.play_vol,local.rec_vol);
        XFlush(remote.xdisplay);
        XFlush(local.xdisplay);
}

/*
	rec and play audio and both directions.
	It is an infinite loop until user says quit.
*/
rec_n_play(talk, listen)
Windata		talk, listen;
{
        int     sizeofbuffer,gather=0;
        char    buffer[WRITE_SIZE];
	int	st = 0;
	/*
		There should be a mechanism to regulate buffer size,
		i.e. longer waits if buffer too small.
	*/
 
        while (talking)
        {
                sizeofbuffer = XAudioRecord(talk.xdisplay,buffer);
                if (sizeofbuffer)
		{
                        st=XAudioPlay(listen.xdisplay,buffer,sizeofbuffer);
			if (st>0)
				usleep(5000); /* too much data- flooding */
		}
		for (gather =0; gather< 50000; gather += 5000)
		{
			usleep(5000);
			wait_events(talk);
			wait_events(listen);
		}
		usleep(5000);
                sizeofbuffer = XAudioRecord(listen.xdisplay,buffer);
                if (sizeofbuffer)
		{
                        st=XAudioPlay(talk.xdisplay,buffer,sizeofbuffer);
			if (st>0)
				usleep(5000); /* too much data- flooding */
		}
		for (gather =0; gather< 50000; gather += 5000)
		{
			usleep(5000);
			wait_events(talk);
			wait_events(listen);
		}
        }
}

/* ring to notify the user
*/
ring(xdisplay)
Display	*xdisplay;
{
int     f;
int     size, total_size = 0 ;
char    *ring;
                /* custom ring once */
        if (( f = open("/home/sug/demo/lib/sounds/ring_tone.au",O_RDONLY)) < 0 )
        {
                if (( f = open("/home/sug/demo/lib/sounds/buzz.au",O_RDONLY)) < 0 )
                {
                        (void)fprintf(stderr,"cannot open buzz.au");
                        return(0);
                }
        }
        ring = (char *)malloc(WRITE_SIZE); /* limit */
        total_size = size = read(f,ring,WRITE_SIZE);
        while(size)
        {
                XAudioPlay(xdisplay,ring,size);
                size = read(f,ring,WRITE_SIZE);
                total_size += size;
        }
        (void)close(f);
        free(ring);
        return(total_size/8192); /* number of secs it will go for */
}

/* clean up the audio stuff that has been opened */
clean_up(xdisplay)
Display	*xdisplay;
{
int     closed;
	while( !( closed = XAudioClose(xdisplay)))
	{
		(void)fprintf(stderr,"waiting closed = %d.\n",closed);
		sleep(1);
	}
}

/* local.xdisplay */
reply_window()
{
int	i= 2;
	sprintf(mesg_string,"answer from %s",remotename);
	open_window(&local, mesg_string);
	init_window(&local);
	make_rects(local);
	XFlush(local.xdisplay);
	while(i--)
	{
		wait_events(local);
		sleep(1);
	}
}
