/* interact.cpp
/*    An example of an interactive CAVE environment. The user is able to
/*   add spheres to the environment using the right wand button, to grab
/*   existing spheres using the left button, and to resize a grabbed sphere
/*   with the joystick. 
*/

#include <unistd.h>
#include <stdlib.h>
#include <cave_ogl.h>
#include <GL/glu.h>
#include <dms/dms.h>

using namespace dms;

/* Data for a single ball */
typedef struct _balldata
    {
    QuadricObject object;
    SimpleTransform xform;
    float radius;
    } balldata_t;

#define MAX_BALLS 1000

balldata_t *ballList[MAX_BALLS];
balldata_t *heldBall=0;
int numBalls = 0;

Material ballMaterial(Color::Green);
Light light;

void initialize(void);
void update(void);
void check_add(void);
balldata_t * update_held(balldata_t *ball);
balldata_t * check_grab(void);
void drawEverything(void);

main(int argc,char **argv)
    {
    CAVEConfigure(&argc,argv,NULL);
    CAVEInit();
    CAVEInitApplication(initialize, 0);
    CAVEFrameFunction(update, 0);
    CAVEDisplay(drawEverything, 0);
    while (!CAVEgetbutton(CAVE_ESCKEY))
        usleep(10000);
    CAVEExit();
    }


void initialize(void)
    {
    light.setInfinitePosition(0, 1, 1);
    }


void update(void)
    {
    check_add();
    if (heldBall)
        heldBall = update_held(heldBall);
    else
        heldBall = check_grab();
    }


void check_add(void)
    {
    if ((numBalls < MAX_BALLS) && (CAVEButtonChange(3) == -1))
        {
        balldata_t *ball = new balldata_t;
        Vector3 wandPos, wandFront;
        CAVEGetPosition(CAVE_WAND, wandPos.vec);
        CAVEGetVector(CAVE_WAND_FRONT, wandFront.vec);
        ball->xform.setTranslation(wandPos + wandFront * 2);
        ball->radius = 1;
        ball->object.setMaterial(ballMaterial);
        ball->object.setTransform(ball->xform);
        ball->object.makeSphere(ball->radius);
        ballList[numBalls] = ball;
        numBalls++;
        }
    }


balldata_t * update_held(balldata_t *ball)
    {
    if (!CAVEBUTTON1)
        return 0;
    
    Vector3 wandPos;
    CAVEGetPosition(CAVE_WAND, wandPos.vec);
    ball->xform.setTranslation(wandPos);
    
    if (CAVE_JOYSTICK_Y > .5)
        ball->radius *= 1.005;
    else if (CAVE_JOYSTICK_Y < -.5)
        ball->radius /= 1.005;
    ball->object.makeSphere(ball->radius);
    
    return ball;
    }


balldata_t * check_grab(void)
    {
    if (CAVEButtonChange(1) == 1)
        {
        Vector3 wandPos;
        CAVEGetPosition(CAVE_WAND, wandPos.vec);
        for (int i=0; i < numBalls; i++)
            {
            if (wandPos.distance(ballList[i]->xform.translation()) <
                   ballList[i]->radius)
                {
                return ballList[i];
                }
            }
        }
    return 0;
    }



void drawEverything(void)
    {
    glClearColor(0., 0., 0., 0.);
    glClear(GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT);

    light.apply();
  
    for (int i=0; i < numBalls; i++)
        ballList[i]->object.drawAll();
    }
