/*
 * Copyright (C) 2001-2005, R3vis Corporation.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA,
 * or visit http://www.gnu.org/copyleft/gpl.html.
 *
 * Contributor(s):
 *   Wes Bethel, R3vis Corporation, Marin County, California
 *
 * The OpenRM project is located at http://openrm.sourceforge.net/.
 */
/*
 * $Id: pickListTest.c,v 1.9 2005/06/08 17:07:13 wes Exp $
 * $Revision: 1.9 $
 * $Name: OpenRM-1-6-0-2-c $
 * $Log: pickListTest.c,v $
 * Revision 1.9  2005/06/08 17:07:13  wes
 * Code cleanup to eliminate compiler warnings.
 *
 * Revision 1.8  2004/02/23 02:56:51  wes
 * Fixed minor buglet in parseArgs().
 *
 * Revision 1.7  2003/04/13 18:13:23  wes
 * Updated copyright dates.
 *
 * Revision 1.6  2003/01/27 05:07:07  wes
 * Changes to RMpipe initialization sequence APIs. Tested for GLX, but not WGL.
 *
 * Revision 1.5  2003/01/16 22:22:45  wes
 * Updated all source files to reflect new organization of header files: all
 * headers that were formerly located in include/rmaux, include/rmv and
 * include/rmi are now located in include/rm.
 *
 * Revision 1.4  2002/09/05 15:02:12  wes
 * Removed temporary calls to test routines in the component manager. These
 * are no longer needed when dynamic object pools are working.
 *
 * Revision 1.3  2002/08/19 00:53:22  wes
 * Removed reference to RM_COMPONENT_POOL_MANAGER, which was previously
 * a global variable, and replaced it with a new, experimental API call
 * to query the size of the component pool.
 *
 * Revision 1.2  2002/08/19 00:24:41  wes
 * RM_COMPONENT_POOL_SIZE is now a global variable, added needed reference.
 * This should eventually go away, to be replaced with an OpenRM API that
 * will allow query of the component manager's capabilities.
 *
 * Revision 1.1  2002/06/15 17:05:58  wes
 * Initial entry.
 *
 *
 */

/*
 * usage: pickListTest [-w imgWidthPixels -h imgHeightPixels]
 *        [-n NN (NN is number of spheres, default is 10,
 *          max is RM_COMPONENT_POOL_SIZE/2 )]
 */

#include <stdio.h>
#ifndef RM_WIN
#include <unistd.h>
#endif
#include <rm/rm.h>   
#include <rm/rmaux.h>
#include "procmode.h"

static RMpipe *myPipe=NULL;

#define DEFAULT_IMAGE_WIDTH 640
#define DEFAULT_IMAGE_HEIGHT 525
static int nSpheres = 10;

static int imgWidth, imgHeight;
static RMenum showText = RM_TRUE;


int
myPickFunc(RMpipe *p,
	   int ix, int iy)
{
    RMpick *pickResults;
    int nPickedObjects;
    static RMnode *pickedNode = NULL;
    RMcolor4D pickColor={0.9, 0.9, 0.1, 1.0F};

    /* this code uses rmFramePickList */

    nPickedObjects = rmFramePickList(p, rmRootNode(), ix,iy, &pickResults);

    /* turn off pick color from last time. */
    if (pickedNode != NULL)
      rmNodeSetUnlitColor(pickedNode, NULL);

    if (nPickedObjects > 0)
    {
	int i;
	char buf[1024];
	char *bigBuf = (char *)malloc(sizeof(char)*nPickedObjects*1024);
	char *work;

	memset(bigBuf, 0, sizeof(char)*nPickedObjects*1024);
	
	sprintf(buf," there were %d objects picked. They are:\n", nPickedObjects);
	strncpy(bigBuf, buf, strlen(buf));
	work = bigBuf + strlen(buf);
	
	for (i=0;i<nPickedObjects;i++)
	{
	    sprintf(buf, " name of object %d is %s, primitive # %d at zval=%f\n",i, rmPickedNodeName(pickResults+i), rmPickedPrimitive(pickResults+i), rmPickedPrimitiveZval(pickResults+i));
	    work = strncpy(work, buf, strlen(buf));
	    work += strlen(buf);
	}
	
	sprintf(buf," name of closest picked object is %s \n",rmPickedNodeName(pickResults));
	strncpy(work, buf, strlen(buf));
	work += strlen(buf);
	
	rmNotice(bigBuf);


	pickedNode = rmPickedNode(pickResults);
	rmNodeSetUnlitColor(pickedNode, &pickColor);
	rmFrame(p, rmRootNode());
	rmPickDelete(pickResults);
    }
    else
    {
	rmNotice("no objects picked. try again!");
	pickedNode = NULL;
    }
    
    return(1);				 /* necessary, otherwise the
					  event loop will exit*/
}

#define DIVISOR 1./32767.

void
ToVertex(int r1,
	 int r2,
	 RMvertex3D *v)
{
    double azimuth, elevation;
    double ca,sa,se,ce;

    /* input rand numbers in range 0..32767 */

    /* convert to range 0..1 */
    azimuth = (double)r1 * DIVISOR;
    elevation = (double)r2 * DIVISOR;

    /* convert azimuth to range 0..2pi */
    azimuth *= RM_PI * 2.0;
    
    /* convert elevation to range -pi/2..pi/2 */
    elevation = (elevation - 0.5) * RM_PI;

    ca = cos(azimuth);
    sa = sin(azimuth);
    se = sin(elevation);
    ce = cos(elevation);

    v->y = se;
    v->x = ca * ce;
    v->z = sa * ce;

    /* MJV */

    v->x *= 20;
    v->y *= 2;
    v->z *= 2;
}

void
myInitFunc(RMpipe *p,
	   RMnode *addTo)
{
  RMnode *locRoot;
  RMvertex3D *v;
  int i;
  float radius = 0.2;		/* spheres will be positioned on the
				   surface of a hypothetical sphere centered
				at the origin, with radius 1.0. this radius
				value is 1/100, and is used as the radius 
				value for all the spheres generated in this
				routine. */
  float bigRadius = 0.5;

  locRoot = rmNodeNew("objRoot",RM_RENDERPASS_3D, RM_RENDERPASS_OPAQUE);

  v = rmVertex3DNew(nSpheres);
  /*  srand(time(NULL)); */
  srand(0);
  
  /* generate random points on a the surface of a sphere. these
     points will be used as the centers of a cloud of spheres.  */

  for (i=0;i<nSpheres;i++)
  {
     int r1,r2;
     r1 = rand();
     r2 = rand();
     ToVertex(r1,r2,v+i);
  }

  for (i=0;i<nSpheres;i++)
  {
     /*  */
     RMnode *n;
     RMprimitive *p;
     char buf[32];

     sprintf(buf,"sphere-%d",i);
     n = rmNodeNew(buf,RM_RENDERPASS_3D, RM_RENDERPASS_OPAQUE);
     p = rmPrimitiveNew(RM_SPHERES);

     /* MJV */
     if (showText == RM_TRUE)
     {
       RMprimitive *prim;
       char *s[1];

       if ((prim = rmPrimitiveNew(RM_TEXT)) == NULL)
	 fprintf(stderr, "prim failed!\n");

       s[0] = buf;
       rmPrimitiveSetText(prim, 1, s);
       rmPrimitiveSetVertex3D(prim, 1, v+i, RM_COPY_DATA, NULL);
       rmNodeAddPrimitive (n, prim);
     }

     rmPrimitiveSetVertex3D(p,1,v+i,RM_COPY_DATA,NULL);
     if ((i == 0) || (i == nSpheres-1))
       rmPrimitiveSetRadii(p,1,&bigRadius,RM_COPY_DATA,NULL);
     else
       rmPrimitiveSetRadii(p,1,&radius,RM_COPY_DATA,NULL);

     rmNodeAddPrimitive (n, p);

     rmNodeComputeBoundingBox(n);
     rmNodeAddChild(locRoot, n);
  }

  rmNodeAddChild(addTo, locRoot);
  rmNodeUnionAllBoxes(addTo);

  rmVertex3DDelete(v);

  /* create a camera, assign it to addTo */
  {
     RMcamera3D *c = rmCamera3DNew();
     RMcolor4D bgColor={.1, .1, .4, 1.};

     rmCamera3DComputeViewFromGeometry(c, locRoot, imgWidth, imgHeight); 
     rmCamera3DSetHither(c, 0.1); /* MJV */
     rmNodeSetSceneCamera3D(locRoot, c);
     rmNodeSetSceneBackgroundColor(locRoot, &bgColor);
     rmDefaultLighting(addTo);
  }

  rmauxSetGeomTransform(locRoot,p);
  rmauxSetCamera3DTransform(locRoot, p);
  rmauxSetButtonDownFunc(RM_BUTTON1,RM_NO_MODIFIER, myPickFunc);
  rmauxSetButtonMotionFunc(RM_BUTTON1,RM_NO_MODIFIER, NULL);
  rmauxSetButtonUpFunc(RM_BUTTON1,RM_NO_MODIFIER, NULL);

  rmFrame(p, addTo);
}

void
myRenderFunc(RMpipe *myPipe,
	     RMnode *subTree)
{
    /* insert code to call frame-based renderer here */
    rmFrame(myPipe, subTree);
}


void
usage(char *s)
{
    printf("usage: %s [-w imgWidthPixels ] [-h imgHeightPixels] [-n nSpheres (default is %d)] [-notext (turns off text display, default is to display text)] \n",s, nSpheres);
}

void
parseArgs(int ac,
	  char *av[],
	  int *imgWidth,
	  int *imgHeight,
	  int *nSpheres)
{
    int i;

    i = 1;
    while (i < ac)
    {
	if (strcmp(av[i],"-w") == 0)
	{
	    i++;
	    sscanf(av[i],"%d", imgWidth);
	}
	else if (strcmp(av[i],"-h") == 0)
	{
	    i++;
	    sscanf(av[i],"%d", imgHeight);
	}
	else if (strcmp(av[i],"-n") == 0)
	{
	    i++;
	    sscanf(av[i],"%d", nSpheres);
	}
	else if (strcmp(av[i],"-notext") == 0)
	{
	    showText = RM_FALSE;
	}
	else
	{
	    usage(av[0]);
	    exit(-1);
	}
	i++;
    }
}

#ifdef RM_WIN
int WINAPI WinMain (HINSTANCE hInstance,
		    HINSTANCE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
{
    MSG      msg; 
    HWND     hWnd; 
    void *fptr;
    
    RMenum processingMode = DEFAULT_PROCESSING_MODE; /* in procmode.h */
    RMenum targetPlatform = RM_PIPE_WGL;

    imgWidth = DEFAULT_IMAGE_WIDTH;
    imgHeight = DEFAULT_IMAGE_HEIGHT;
    
    parseArgs(__argc, __argv, &imgWidth, &imgHeight, &nSpheres);
    
#else  /* assume RM_X */
int
main(int argc,
     char *argv[])
{
    void *msg;			/* needed for rmauxEventLoop
				 win32/unix API consistency */
    RMenum processingMode = DEFAULT_PROCESSING_MODE; /* in procmode.h */
    RMenum targetPlatform = RM_PIPE_GLX;

    imgWidth = DEFAULT_IMAGE_WIDTH;
    imgHeight = DEFAULT_IMAGE_HEIGHT;
    
    parseArgs(argc, argv, &imgWidth, &imgHeight, &nSpheres);
#endif

    /* 
     * first stage of RM initialization.
     */
    rmInit();

    /* 
     * create the rendering pipe. this step is required in both
     * Win32 and X.
     */
    myPipe = rmPipeNew(targetPlatform);
    
    processingMode = RM_PIPE_MULTISTAGE;
    rmPipeSetProcessingMode(myPipe, processingMode);

#ifdef RM_WIN
    {
        /*
	 * Win32: when a window is created, we have to tell windows the
	 * name of the "WndProc," the procedure that gets called by
	 * windows with events (the event loop) (contrast to the X model 
	 * where the name of the event loop is not part of the window). 
	 * Since we're using RMaux, we know about the event handling 
	 * procedure named "rmauxWndProc" and we provide that here. 
	 */

        fptr = (void *)(rmauxWndProc);
	hWnd = rmauxCreateW32Window(myPipe,
			       NULL, /* no parent window */
			       20,20,imgWidth,imgHeight,"RM for Windows",
			       hInstance,fptr);
	if (hWnd == 0)
	  exit(-1);

	/* 
	 * assign the new window handle to the rendering pipe.
	 */
	rmPipeSetWindow(myPipe,hWnd, imgWidth, imgHeight);
    }
#endif
#ifdef RM_X
    {
	Window w;

	w = rmauxCreateXWindow(myPipe,
			       (Window)NULL, /* parent window */
			       0,0,imgWidth,imgHeight,
			       "RM for X-Windows","RM for X-Windows",RM_TRUE);

	/* 
	 * assign the window to the rendering pipe.
	 */
	rmPipeSetWindow(myPipe,w,imgWidth,imgHeight);
    }
#endif

    /* 
     * X-ism: once the window is created and assigned to the 
     * rendering pipe, rmPipeMakeCurrent makes the OpenGL rendering context
     * current for the pipe+window combination. 
     *
     * this step is required for X. in these demo programs, it is not 
     * strictly required by Win32, as the newly created context is made
     * current as part of the OpenGL initialization sequence.
     */
    rmPipeMakeCurrent(myPipe);	

    rmauxSetInitFunc(myInitFunc);
    rmauxSetRenderFunc(myRenderFunc);
    
    /*
     * set key handler function so this prog will exit on "q" key.
     */
    rmauxSetKeyFunc(myPipe, rmauxDefaultKeyFunc);
    
    rmauxEventLoop(myPipe,rmRootNode(), &msg);

    rmPipeDelete(myPipe);
    rmFinish();

    return(1);
}
/* EOF */
