/*
 *			D M - I R I S . C
 *
 *  Uses library -lgl2
 *
 *  NON-displaylist version.
 *
 *  MGED display manager for the IRIS.
 *  Based on dm-mer.c
 *
 *  Display structure -
 *	ROOT LIST = { wrld }
 *	wrld = { face, Mvie, Medi }
 *	face = { faceplate stuff, 2d lines and text }
 *	Mvie = { model2view matrix, view }
 *	view = { (all non-edit solids) }
 *	Medi = { model2objview matrix, edit }
 *	edit = { (all edit solids) }
 *
 *  Authors -
 *	Michael John Muuss
 *	Terry Slattery
 *	Charles M. Kennedy
 *  
 *  Source -
 *	SECAD/VLD Computing Consortium, Bldg 394
 *	The U. S. Army Ballistic Research Laboratory
 *	Aberdeen Proving Ground, Maryland  21005
 *  
 *  Copyright Notice -
 *	This software is Copyright (C) 1985 by the United States Army.
 *	All rights reserved.
 */
#ifndef lint
static char RCSid[] = "@(#)$Header: dm-ir.c,v 1.4 86/06/11 23:30:25 mike Exp $ (BRL)";
#endif

#include <stdio.h>
#include <time.h>
#include <termio.h>

#include "./machine.h"	/* special copy */
#include "../h/vmath.h"
#include "ged.h"
#include "dm.h"
#include "solid.h"
#include "../h/mater.h"

#include <gl2/gl.h>		/* SGI IRIS library */
#include <gl2/device.h>	/* SGI IRIS library */

/* Display Manager package interface */

#define IRBOUND	4095.9	/* Max magnification in Rot matrix */
int	Ir_open();
void	Ir_close();
int	Ir_input();
void	Ir_prolog(), Ir_epilog();
void	Ir_normal(), Ir_newrot();
void	Ir_update();
void	Ir_puts(), Ir_2d_line(), Ir_light();
int	Ir_object();
unsigned Ir_cvtvecs(), Ir_load();
void	Ir_statechange(), Ir_viewchange(), Ir_colorchange();
void	Ir_window(), Ir_debug();

struct timeval	{			/* needed for select() */
	long	tv_sec;			/* seconds */
	long	tv_usec;		/* microseconds */
};

struct dm dm_Ir = {
	Ir_open, Ir_close,
	Ir_input,
	Ir_prolog, Ir_epilog,
	Ir_normal, Ir_newrot,
	Ir_update,
	Ir_puts, Ir_2d_line,
	Ir_light,
	Ir_object,
	Ir_cvtvecs, Ir_load,
	Ir_statechange,
	Ir_viewchange,
	Ir_colorchange,
	Ir_window, Ir_debug,
	0,			/* no "displaylist", per. se. */
	IRBOUND,
	"ir", "SGI IRIS 2400"
};

extern struct device_values dm_values;	/* values read from devices */

static int	ir_debug;		/* 2 for basic, 3 for full */

/*
 *  Defines for dealing with SGI Graphics Engine Pipeline
 */
union gepipe {
	short	s;
	long	l;
	float	f;
};

/*#define MC_68010 xx	/* not a turbo */
#ifdef MC_68010
#define GEPIPE	((union gepipe *)0X00FD5000)
#else
#define GEPIPE	((union gepipe *)0X60001000)
#endif
#define GEP_END(p)	((union gepipe *)(((char *)p)-0x1000))	/* 68000 efficient 0xFd4000 */


/* Map +/-2048 GED space into -1.0..+1.0 :: x/2048*/
#define GED2IRIS(x)	(((float)(x))*0.00048828125)
static int
irisX2ged(x)
register int x;
{
	x -= 1023-767;
	if( x <= 0 )  return(-2048);
	if( x >= 766 )  return(2047);
	x = (((float)x)/766.0)*4096.0;
	x -= 2048;
	return(x);
}
static int
irisY2ged(y)
register int y;
{
	y = (((float)y)/766.0)*4096.0;
	y -= 2048;
	return(y);
}

/*
 *			I R _ O P E N
 *
 * Fire up the display manager, and the display processor.
 *
 */
int
Ir_open()
{

	/* Protect ourselves. */
	sync();

	ginit();
	onemap();		/* one color map */
#define DBUF 1
#ifdef DBUF
	doublebuffer();		/* half of whatever we have */
	gconfig();
	color(DM_BLACK);
	frontbuffer(1);
	clear();
	frontbuffer(0);
#endif
	color(DM_BLACK);
	clear();		/* whole screen */
	textport( 0, 1023-767, 0, 767 );
	viewport( 1023-767, 1023, 0, 767 );
	qdevice(LEFTMOUSE);
	qdevice(MIDDLEMOUSE);
	qdevice(RIGHTMOUSE);
	tie(MIDDLEMOUSE, MOUSEX, MOUSEY);
	while( getbutton(LEFTMOUSE)||getbutton(MIDDLEMOUSE)||getbutton(RIGHTMOUSE) )  {
		printf("IRIS_open:  mouse button stuck\n");
		sleep(1);
	}

	/* Line style 0 is solid.  Program line style 1 as dot-dashed */
	deflinestyle( 1, 0xCF33 );
	setlinestyle( 0 );

	return(0);			/* OK */
}

/*
 *  			I R _ C L O S E
 *  
 *  Gracefully release the display.
 */
void
Ir_close()
{
	textinit();	/* restore default text window */
	ginit();	/* back to the defaults */
	clear();	/* whole screen */
	cursoff();	/* its a nuisance */
	gexit();
}

/*
 *			I R _ P R O L O G
 *
 * Define the world, and include in it instances of all the
 * important things.  Most important of all is the object "faceplate",
 * which is built between dmr_normal() and dmr_epilog()
 * by dmr_puts and dmr_2d_line calls from adcursor() and dotitles().
 */
void
Ir_prolog()
{
	ortho2( -1.0,1.0, -1.0,1.0);	/* L R Bot Top */
	if( !dmaflag )
		return;
	cursoff();
#ifndef DBUF
	color( DM_BLACK );
	clear();
#endif
}

/*
 *			I R _ N O R M A L
 *
 * Restore the display processor to a normal mode of operation
 * (ie, not scaled, rotated, displaced, etc).
 * Turns off windowing.
 */
void
Ir_normal()
{
	color(DM_BLACK);
	ortho2( -1.0,1.0, -1.0,1.0);	/* L R Bot Top */
}

/*
 *			I R _ E P I L O G
 */
void
Ir_epilog()
{
	/*
	 * A Point, in the Center of the Screen.
	 * This is drawn last, to always come out on top.
	 */
	Ir_2d_line( 0, 0, 0, 0, 0 );
	/* End of faceplate */

	curson();			/* re-enable cursor */
#ifdef DBUF
	swapbuffers();
	/* give Graphics pipe time to work */
	color(DM_BLACK);
	clear();
#endif
}

/*
 *  			I R _ N E W R O T
 *  Stub.
 */
void
Ir_newrot(mat)
mat_t mat;
{
}

/*
 *  			I R _ O B J E C T
 *  
 *  Set up for an object, transformed as indicated, and with an
 *  object center as specified.  The ratio of object to screen size
 *  is passed in as a convienience.  Mat is model2view.
 *
 *  Returns 0 if object could be drawn, !0 if object was omitted.
 */
int
Ir_object( sp, m, ratio, white )
register struct solid *sp;
register fastf_t *m;
double ratio;
{
	register struct veclist *vp;
	register union gepipe *hole = GEPIPE;
	register int nvec;

	if( sp->s_soldash )  {
		hole->l = 0x02080020;	/* set linestyle and repeat */
		hole->s = 2;		/* #bits written per linestyle bit */
		hole->s = 0xCF33;	/* line style */
	}

#ifdef never
	/* Phony pseudo-color, for now */
	nvec = white ? DM_WHITE : ((ndrawn>>2)%13)+2 ;	/* HACK!! */
#else
	if( white || sp->s_materp == (char *)0 )
		nvec = DM_WHITE;
	else
		nvec = ((struct mater *)sp->s_materp)->mt_dm_int;
#endif
	hole->l = 0x01080014;	/* set color */
	hole->s = nvec;		/* color map entry # */

	hole->s = 0xFF08;	/* completion */
	hole->s = 1;		/* load matrix */
	hole->f = *m++;
	hole->f = *m++;
	hole->f = *m++;
	hole->f = *m++;
	hole->f = *m++;
	hole->f = *m++;
	hole->f = *m++;
	hole->f = *m++;
	hole->f = *m++;
	hole->f = *m++;
	hole->f = *m++;
	hole->f = *m++;
	hole->f = *m++;
	hole->f = *m++;
	hole->f = *m++;
	hole->f = *m;

	nvec = sp->s_vlen;
	for( vp = sp->s_vlist; nvec-- > 0; vp++ )  {
		/* Viewing region is from -1.0 to +1.0 */
		if( vp->vl_pen == PEN_UP )  {
			/* Move, not draw */
			hole->s = 0x210;
		}  else  {
			/* draw */
			hole->s = 0x211;
		}
		hole->f = vp->vl_pnt[X];
		hole->f = vp->vl_pnt[Y];
		hole->f = vp->vl_pnt[Z];
	}

	hole->l = 0x02080020;	/* set line style and repeat */
	hole->s = 1;		/* #bits written per linestyle bit */
	GEP_END(hole)->s = 0xFFFF;	/* line style */

	return(1);	/* OK */
}

/*
 *			I R _ U P D A T E
 *
 * Transmit accumulated displaylist to the display processor.
 * Last routine called in refresh cycle.
 */
void
Ir_update()
{
	color( DM_WHITE );		/* leave other windows white! */
	gflush();		/* Flush any pending output */
	if( !dmaflag )
		return;
}

/*
 *			I R _ P U T S
 *
 * Output a string.
 * The starting position of the beam is as specified.
 */
void
Ir_puts( str, x, y, size, colour )
register char *str;
{
	register union gepipe *hole = GEPIPE;

	hole->l = 0x0008001A;
	hole->s = 0x112;		/* cmov2 */
	hole->f = GED2IRIS(x);
	hole->f = GED2IRIS(y-8);
	hole->s = 0xFF08;		/* completion? */

	hole->l = 0x01080014;	/* set color */
	hole->s = colour;	/* color map entry # */

	charstr( str );
}

/*
 *			I R _ 2 D _ L I N E
 *
 */
void
Ir_2d_line( x1, y1, x2, y2, dashed )
int x1, y1;
int x2, y2;
int dashed;
{
	register union gepipe *hole = GEPIPE;

	hole->l = 0x01080014;	/* set color */
	hole->s = DM_YELLOW;		/* color map entry # */
	if( dashed )  {
		hole->l = 0x02080020;	/* set linestyle and repeat */
		hole->s = 2;		/* #bits written per linestyle bit */
		hole->s = 0xCF33;	/* line style */
	}
	hole->s = 0x110;		/* move2 */
	hole->f = GED2IRIS(x1);
	hole->f = GED2IRIS(y1);
	hole->s = 0x111;		/* draw2 */
	hole->f = GED2IRIS(x2);
	GEP_END(hole)->f = GED2IRIS(y2);
	if( dashed )  {
		hole->l = 0x02080020;	/* set line style and repeat */
		hole->s = 1;		/* #bits written per linestyle bit */
		GEP_END(hole)->s = 0xFFFF;	/* line style */
	}
}

/*
 *			I R L I M I T
 *
 * Because the dials are so sensitive, setting them exactly to
 * zero is very difficult.  This function can be used to extend the
 * location of "zero" into "the dead zone".
 */
static float
irlimit(i)
double i;
{
#define NOISE	0.1

	if( i > NOISE )
		return( i-NOISE );
	if( i < -NOISE )
		return( i+NOISE );
	return(0.0);
}

/*
 *			I R _ I N P U T
 *
 * Execution must suspend in this routine until a significant event
 * has occured on either the command stream, or a device event has
 * occured, unless "noblock" is set.
 *
 * The GED "generic input" structure is filled in.
 *
 * Returns:
 *	0 if no command waiting to be read,
 *	1 if command is waiting to be read.
 */
Ir_input( cmd_fd, rateflg )
{
	static long readfds;
	static struct timeval timeout;
	static int cnt;
	register int i;

	/*
	 * Check for input on the keyboard or on the polled registers.
	 *
	 * Suspend execution until either
	 *  1)  User types a full line
	 *  2)  A change in peripheral status occurs
	 *  3)  The timelimit on SELECT has expired
	 *
	 * If a RATE operation is in progress (zoom, rotate, slew)
	 * in which the peripherals (rate setting) may not be changed,
	 * but we still have to update the display,
	 * do not suspend execution.
	 */
#ifdef BSD42
	if( rateflg )  {
		/* Don't flood the sluggish matrix update */
		timeout.tv_sec = 0;
		timeout.tv_usec = 300000;
	}  else  {
		timeout.tv_sec = 1000;
		timeout.tv_usec = 0;
	}

	readfds = (1<<cmd_fd);
	i = select( 32, &readfds, 0L, 0L, &timeout );
#else
	/* Frigging System V on IRIS defines select() for something else! */
	do  {
		cnt = 0;
		i = qtest();
		if( i != 0 )
			break;
		(void)ioctl( cmd_fd, FIONREAD, &cnt );
		if( cnt != 0 )
			break;
		/* Well, select() would be nice, but
		 * we can't expect too much from system V.
		 * If Rate operation, return immed., else spin, waiting.
		 */
	} while( rateflg == 0 );
#endif

	/*
	 * Set device interface structure for GED to "rest" state.
	 * First, process any messages that came in.
	 */
	dm_values.dv_buttonpress = 0;
	dm_values.dv_flagadc = 0;
	dm_values.dv_penpress = 0;

	if( i != 0 )
		checkmouse();

	if( cnt > 0 )
		return(1);		/* command awaits */
	else
		return(0);		/* just peripheral stuff */
}

checkmouse()  {
#define NVAL 48
	unsigned short values[NVAL];
	register unsigned short *valp;
	register int ret;
	register int n;

	n = blkqread( values, NVAL );	/* n is # of shorts returned */
	if( ir_debug ) printf("blkqread gave %d\n", n);
	for( valp = values; n > 0; n -= 2, valp += 2 )  {
		ret = *valp;
		if( ir_debug ) printf("qread ret=%d, val=%d\n", ret, valp[1]);
		switch( ret )  {
		case LEFTMOUSE:
			if( valp[1] && dm_values.dv_penpress != DV_PICK )
				dm_values.dv_penpress = DV_OUTZOOM;
			break;
		case MIDDLEMOUSE:
			if( valp[1] )
				dm_values.dv_penpress = DV_PICK;
			/* Will also get MOUSEX and MOUSEY hits */
			break;
		case RIGHTMOUSE:
			if( valp[1] && dm_values.dv_penpress != DV_PICK )
				dm_values.dv_penpress = DV_INZOOM;
			break;
		case MOUSEX:
			dm_values.dv_xpen = irisX2ged( valp[1] );
			break;
		case MOUSEY:
			dm_values.dv_ypen = irisY2ged( valp[1] );
			break;
		case WMREPLY:
			/* This guy speaks, but has nothing to say */
			break;
		default:
			printf("IRIS device %d gave %d?\n", ret, valp[1]);
			break;
		}
	}
}

/* 
 *			I R _ L I G H T
 *
 * This function must keep both the light hardware, and the software
 * copy of the lights up to date.  Note that requests for light changes
 * may not actually cause the lights< to be changed, depending on
 * whether the buttons are being used for "view" or "edit" functions
 * (although this is not done in the present code).
 */
void
Ir_light( cmd, func )
int cmd;
int func;			/* BE_ or BV_ function */
{
}

/*
 *			I R _ C V T V E C S
 *
 */
unsigned
Ir_cvtvecs( sp )
register struct solid *sp;
{
	return( 0 );	/* No "displaylist" consumed */
}

/*
 * Loads displaylist from storage[]
 */
unsigned
Ir_load( addr, count )
unsigned addr, count;
{
	return( 0 );		/* FLAG:  error */
}

void
Ir_statechange( a, b )
{
	if( ir_debug ) printf("statechange %d %d\n", a, b );
	/*
	 *  Based upon new state, possibly do extra stuff,
	 *  including enabling continuous tablet tracking,
	 *  object highlighting
	 */
	switch( b )  {
	case ST_VIEW:
		unqdevice( MOUSEY );	/* constant tracking OFF */
		break;
		
	case ST_S_PICK:
	case ST_O_PICK:
	case ST_O_PATH:
		qdevice( MOUSEY );	/* constant tracking ON */
		break;
	case ST_O_EDIT:
	case ST_S_EDIT:
		unqdevice( MOUSEY );	/* constant tracking OFF */
		break;
	default:
		(void)printf("Ir_statechange: unknown state %s\n", state_str[b]);
		break;
	}
	Ir_viewchange( DM_CHGV_REDO, SOLID_NULL );
}

void
Ir_viewchange( cmd, sp )
register int cmd;
register struct solid *sp;
{
	if( ir_debug ) printf("viewchange( %d, x%x )\n", cmd, sp );
	switch( cmd )  {
	case DM_CHGV_ADD:
		break;
	case DM_CHGV_REDO:
		break;
	case DM_CHGV_DEL:
		break;
	case DM_CHGV_REPL:
		return;
	case DM_CHGV_ILLUM:
		break;
	}
}

void
Ir_debug(lvl)
{
	ir_debug = lvl;
}

void
Ir_window(w)
int w[];
{
}


/*
 * Color Map table
 */
#define NSLOTS		256
static int ir_nslots=0;		/* how many we have, <= NSLOTS */
static int slotsused;		/* how many actually used */
static struct rgbtab {
	unsigned char	r;
	unsigned char	g;
	unsigned char	b;
} ir_rgbtab[NSLOTS];

/*
 *  			I R _ C O L O R C H A N G E
 *  
 *  Go through the mater table, and allocate color map slots.
 *	8 bit system gives 4 or 8,
 *	24 bit system gives 12 or 24.
 */
void
Ir_colorchange()
{
	register struct mater *mp;
	register int i;

	if( !ir_nslots )  {
		ir_nslots = getplanes();
		ir_nslots = 1<<ir_nslots;
		if( ir_nslots > NSLOTS )  ir_nslots = NSLOTS;
	}
	if( ir_debug )  printf("colorchange\n");
	ir_rgbtab[0].r=0; ir_rgbtab[0].g=0; ir_rgbtab[0].b=0;/* Black */
	ir_rgbtab[1].r=255; ir_rgbtab[1].g=0; ir_rgbtab[1].b=0;/* Red */
	ir_rgbtab[2].r=0; ir_rgbtab[2].g=0; ir_rgbtab[2].b=255;/* Blue */
	ir_rgbtab[3].r=255; ir_rgbtab[3].g=255;ir_rgbtab[3].b=0;/*Yellow */
	ir_rgbtab[4].r = ir_rgbtab[4].g = ir_rgbtab[4].b = 255; /* White */
	ir_rgbtab[5].g = 255; /* Green */
	ir_rgbtab[6].r = ir_rgbtab[6].g = ir_rgbtab[6].b = 128; /* Grey */
	/* #7 Reserved by IRIS display */
	ir_rgbtab[7].r = ir_rgbtab[7].g = ir_rgbtab[7].b = 255; /* White TEXT*/

	slotsused = 8;
	for( mp = MaterHead; mp != MATER_NULL; mp = mp->mt_forw )
		ir_colorit( mp );

	color_soltab();		/* apply colors to the solid table */

	for( i=0; i < slotsused; i++ )  {
		mapcolor( i, ir_rgbtab[i].r, ir_rgbtab[i].g, ir_rgbtab[i].b);
	}
	gconfig();
	/* Re-send the tree, with the new colors attached */
	Ir_viewchange( DM_CHGV_REDO, SOLID_NULL );
	color( DM_WHITE );	/* undefinied after gconfig() */
}

int
ir_colorit( mp )
struct mater *mp;
{
	register struct rgbtab *rgb;
	register int i;
	register int r,g,b;

	r = mp->mt_r;
	g = mp->mt_g;
	b = mp->mt_b;
	if( (r == 255 && g == 255 && b == 255) ||
	    (r == 0 && g == 0 && b == 0) )  {
		mp->mt_dm_int = DM_WHITE;
		return;
	}

	/* First, see if this matches an existing color map entry */
	rgb = ir_rgbtab;
	for( i = 0; i < slotsused; i++, rgb++ )  {
		if( rgb->r == r && rgb->g == g && rgb->b == b )  {
			 mp->mt_dm_int = i;
			 return;
		}
	}

	/* If slots left, create a new color map entry, first-come basis */
	if( slotsused < ir_nslots )  {
		rgb = &ir_rgbtab[i=(slotsused++)];
		rgb->r = r;
		rgb->g = g;
		rgb->b = b;
		mp->mt_dm_int = i;
		return;
	}
	mp->mt_dm_int = DM_YELLOW;	/* Default color */
}
