#ifdef USE_ELUMENS

#ifdef WIN32
# include "winjunk.h"
#endif

#include "Gview.H"
#include "partiview.H"
#include "partiviewc.h"
#include <math.h>

#define SPILIB_API 1
#include "spiclopsExt.h"

#if !defined(GLX_VERSION_1_2) && defined(unix)
  /* Hack -- elumens spiclops library needs glXGetCurrentDisplay() */
#include <FL/x.H>
extern "C" {
 Display *glXGetCurrentDisplay() {
  if(fl_display == 0) fl_open_display();
  return fl_display;
 }
}
#endif /*!GLX_VERSION_1_2*/

extern "C" {
  extern void pp_spi_init();
}

struct spichan {
  int code;		/* SPI_1C_FRONT, etc. */
  int pfcode;		/* SPI_PF_1_CHAN, etc. */
  float fovx, fovy;	/* angular field of view */
  int tess;		/* mesh tessellation */
};

struct spistuff {
  void *ctx;
  int on;	// if not, just show normal view
  int fast;	// fast, pbuffered graphics?
  Point Eye;	// offAxisEye
  Point Lens;	// offAxisLens
  int haschanxy, haschanwh;
  float chanxy[2], chanwh[2];
  struct spichan chan[4];
  int nchan;
  int refresh;
};

static struct spistuff spi;

unsigned int nextpow2( int v ) {
  if(v <= 0) return 1;
  unsigned int i;
  for(i = 2; i < v && i != 0; i <<= 1)
    ;
  return i;
}

void pp_spi_predraw( Fl_Gview *view, int passno ) {
  if(!spi.on) return;

  int w = view->w(), h = view->h(), x = view->x(), y = view->y();

  if(!spi.haschanwh && (w != spi.chanwh[0] || h != spi.chanwh[1])) {
    spi.refresh = 1;
    spi.chanwh[0] = w, spi.chanwh[1] = h;
  }

  if(!spi.haschanxy && (x != spi.chanxy[0] || y != spi.chanxy[1])) {
    spi.refresh = 1;
    spi.chanxy[0] = x, spi.chanxy[1] = y;
  }

  if(spi.refresh) {
    int i, allpfchans = 0, allchans = 0;
    for(i = 0; i < spi.nchan; i++) {
	allpfchans |= spi.chan[i].pfcode;
	allchans |= spi.chan[i].code;
    }
    if(spi.ctx == NULL) {
	spi.ctx = spiInitialize( NULL, allpfchans |
			(spi.fast ? SPI_PF_AUTO : SPI_PF_BACKBUFFER) );
    }
    spi.refresh = 0;
    if(spi.fast)
	spiSetPBufferSize( spi.ctx, nextpow2(spi.chanwh[0]), nextpow2(spi.chanwh[1]) );

    spiSetChanOrigin( spi.ctx, allchans, spi.chanxy[0], spi.chanxy[1] );
    spiSetChanSize( spi.ctx, allchans, spi.chanwh[0], spi.chanwh[1] );

    for(i = 0; i < spi.nchan; i++) {
	spiSetChanFOV( spi.ctx, spi.chan[i].code,
				spi.chan[i].fovx, spi.chan[i].fovy );
	spiSetChanTessLevel( spi.ctx, spi.chan[i].code,
				spi.chan[i].tess );
    }
    spiSetChanEyePosition ( spi.ctx, allchans,
	spi.Eye.x[0], spi.Eye.x[1], spi.Eye.x[2] );
    spiSetChanLensPosition( spi.ctx, allchans,
	spi.Lens.x[0], spi.Lens.x[1], spi.Lens.x[2] );
  }

  spiBegin( spi.ctx );
  spiPreRender( spi.ctx, spi.chan[passno].code );
}

int pp_spi_postdraw( Fl_Gview *view, int passno ) {

  if(!spi.on) return 0;

  spiPostRender( spi.ctx, spi.chan[passno].code );
  spiEnd( spi.ctx );

  if(passno < spi.nchan-1)
    return passno+1;

  /* OK, we've rendered all passes, now let spi library make real image */

  glClearColor( 0, 0, .1, 1 );
  glClear( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT );
  spiFlush( spi.ctx, SPI_ALL_CHAN );
  /* tell caller that we want no more rendering passes */
  return 0;
}

int pp_spi_parse_args( struct stuff **, int argc, char *argv[], char *fname, void * ) {
  if(argc < 1 || (strncmp(argv[0], "elumens", 3)
			&& strcmp(argv[0], "spi")))
    return 0;

  char *subcmd = argc>1 ? argv[1] : "";

  if(!strcmp(subcmd, "on")) {
    spi.on = 1;

  } else if(!strcmp(subcmd, "off")) {
    spi.on = 0;

  } else if(!strcmp(subcmd, "fast")) {
    spi.fast = getbool( argv[2], 1 );
    msg("fast (pbuffered graphics) %s", spi.fast?"on":"off");

  } else if(!strcmp(subcmd, "eye")) {
    getfloats( &spi.Eye.x[0], 3, 2, argc, argv );
    msg("spi eye %g %g %g", spi.Eye.x[0], spi.Eye.x[1], spi.Eye.x[2]);

  } else if(!strcmp(subcmd, "lens")) {
    getfloats( &spi.Lens.x[0], 3, 2, argc, argv );
    msg("spi lens %g %g %g", spi.Lens.x[0], spi.Lens.x[1], spi.Lens.x[2]);

  } else if(!strcmp(subcmd, "chan")) {
    int cno = 0;
    struct spichan *cp;
    if(argc<=2) {
	for(cno = 0; cno < spi.nchan; cno++) {
	    cp = &spi.chan[cno];
	    msg("spi chan %d  %g(fovx) %g(fovy)  %d(tess)  %d(code) %d(pfcode)",
		cno, cp->fovx, cp->fovy, cp->tess, cp->code, cp->pfcode);
	}
    } else {
	cno = atoi(argv[2]);
	if(cno < 0 || cno > COUNT(spi.chan)) {
	    msg("spi chan: must be in range 0..3");
	    return 1;
	}
	while(spi.nchan <= cno) {
	    cp = &spi.chan[cno];
	    cp->fovx = cp->fovy = 90;  cp->tess = 21;
	    cp->code = 0; cp->pfcode = 0;
	}
	cp = &spi.chan[cno];
	if(argc>3) sscanf(argv[3], "%f", &cp->fovx);
	if(argc>4) sscanf(argv[4], "%f", &cp->fovy);
	if(argc>5) sscanf(argv[5], "%d", &cp->tess);
	if(argc>6) sscanf(argv[6], "%d", &cp->code);
	if(argc>7) sscanf(argv[7], "%d", &cp->pfcode);
	msg("spi chan %d  %g(fovx) %g(fovy)  %d(tess)  %d(code) %d(pfcode)",
	    cno, cp->fovx, cp->fovy, cp->tess, cp->code, cp->pfcode);
    }

  } else if(!strcmp(subcmd, "chans")) {
    int cno = argc>2 ? atoi(argv[2]) : spi.nchan;
    if(cno > COUNT(spi.chan) || cno < 1) {
	msg("spi chans %d: must be in range 1..%d", cno, spi.nchan);
    } else {
	spi.nchan = cno;
    }
  } else if(!strcmp(subcmd, "xy")) {
    if(argc>2&&!strcmp(argv[2],"off")) {
	spi.haschanxy = 0;
    } else if(getfloats( &spi.chanxy[0], 2, 2, argc, argv ) == 2) {
	spi.haschanxy = 1;
    }
    msg(spi.haschanxy ? "spi xy %.0f %.0f" : "spi xy off (%.0f %.0f)",
	spi.chanxy[0], spi.chanxy[1]);

  } else if(!strcmp(subcmd, "wh")) {
    if(argc>2&&!strcmp(argv[2],"off")) {
	spi.haschanwh = 0;
    } else if(getfloats( &spi.chanwh[0], 2, 2, argc, argv ) == 2) {
	spi.haschanwh = 1;
    }
    msg(spi.haschanwh ? "spi wh %.0f %.0f" : "spi wh off (%.0f %.0f)",
	spi.chanwh[0], spi.chanwh[1]);

  } else {
    msg("spi {on|off|eye|lens|fovy|xy|wh}");
    return 1;
  }

  spi.refresh = 1;
  parti_redraw();
  return 1;
}

void pp_spi_init() {

  spi.ctx = NULL;
  spi.on = 0;

  spi.haschanwh = 0;
  spi.haschanxy = 0;

  /* Set defaults for VisionSTATION */
  spi.nchan = 2;

  struct spichan *cp;

  cp = &spi.chan[0];
  cp->pfcode = SPI_PF_1_CHAN;
  cp->code = SPI_1C_FRONT;
  cp->fovx = cp->fovy = 150;
  cp->tess = 31;

  cp = &spi.chan[1];
  cp->pfcode = SPI_PF_O_CHAN;
  cp->code = SPI_OC_FRONT;
  cp->fovx = cp->fovy = 65;
  cp->tess = 21;

  spi.Eye.x[0] = 0; spi.Eye.x[1] = 0.42; spi.Eye.x[2] = 0.38;
  spi.Lens.x[0] = 0; spi.Lens.x[1] = -0.19; spi.Lens.x[2] = 0;

  parti_add_commands( pp_spi_parse_args, "spi", NULL );
  if(ppui.view) {
    ppui.view->predraw = pp_spi_predraw;
    ppui.view->postdraw = pp_spi_postdraw;
  }
}

#else /* no USE_ELUMENS */

void pp_spi_init() { }
  
#endif /*USE_ELUMENS*/