/* Author: Jason Leigh
 * (C) 1997 Electronic Visualization Laboratory - Univ Illinois at Chicago
 */
// Begin by reading main() and in particular the sections marked CAVERN.
// This program is based on Dave Pape's Performer flame demo.

#include "CAVERN.h++"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <gl/device.h>
#include <Performer/pf/pfChannel.h>
#include <Performer/pf/pfLightSource.h>
#include <Performer/pf/pfDCS.h>
#include <Performer/pf/pfGeode.h>
#include <Performer/pfutil.h>
#include <Performer/pr/pfTexture.h>
#include <pfcave.h>

#include <pthread.h>
 

pfuSmoke *smoke;
pfVec3 origin, dir;
pfGeode *node;
pfSphere bsph;
 

pfDCS * create_scene(pfChannel *chan);
void create_smoke(pfGroup *parent);
int smoke_draw(pfTraverser *trav,void *data);
 

/// -------------------------------------------------------------
/// CAVERN CAVERN CAVERN CAVERN CAVERN CAVERN CAVERN CAVERN CAVERN
/// -------------------------------------------------------------
/// Callback that fires when A_KEY gets filled with new data.

void A_keyCB(CAVERN_irbKey_c::CAVERN_irbKeyEvent_t event,
      CAVERN_irbKey_c *thisKey,
      void* userData)
{

         int size;
         CAVERN_irbKey_c::status_t status;

         // Grab the data from the key.
         // Recall userData is really a reference to the "movement" variable
         // declared in main(). You passed it in as a parameter to trigger().

         // Fill size with the amount of data we want to grab from the key.
         size = sizeof(float);

         // Get the data. userData is filled with the data. size returns
         // with the amount of data grabbed.
        thisKey->get((char*) userData, &size, &status);
}

int main(int argc,char **argv)
{

         if (argc < 2) {
                  printf("usage: %s ipAddress\n",argv[0]);
                  exit(1);
             }
         pfDCS *nav_dcs;
         pfInit();
 
         pfCAVEConfig(&argc,argv,NULL);

         int arg;

         CAVEFar = 1000;
         pfuInitSmokes();
         pfConfig();
 
        pfCAVEInitChannels();
 
        nav_dcs = create_scene(pfCAVEMasterChan());
 

/// -------------------------------------------------------------
/// CAVERN CAVERN CAVERN CAVERN CAVERN CAVERN CAVERN CAVERN CAVERN
/// -------------------------------------------------------------

         CAVERN_irb_c::status_t irbStatus;
         CAVERN_irbId_c remoteIRBId;
         CAVERN_irbChannel_c::status_t channelRetStatus;
         CAVERN_irbKey_c::status_t keyStatus;

         /// Startup CAVERN.
         CAVERN_irb_c *personalIRB = CAVERNInit(&argc, &argv, NULL);
         if (!personalIRB) {
                  CAVEHalt();
                  pfExit();
                  return 0;
            }

         /// Create a channel over which communication will occur.
         CAVERN_irbChannel_c *aChannel = personalIRB->createChannel();

         /// Open the channel to a remote IRB specified on the command line.
         remoteIRBId.setAddress(argv[1]);
         aChannel->open(&remoteIRBId,NULL,CAVERN_irbChannel_c::RELIABLE, &channelRetStatus);

         /// Define the local key (called A_KEY)
         CAVERN_irbKeyId_c aKeyId, remote_aKeyId;
         aKeyId.setName("A_KEY");
         remote_aKeyId.setName("A_KEY");

         CAVERN_irbKey_c *aKey = personalIRB->define(&aKeyId, NULL, &irbStatus);

         /// Notify me when A_KEY gets changed by a remote client.
         float movement = 0;
         aKey->trigger(A_keyCB,(void*) &movement);

         /// Create a link to link the local key named A_KEY with the remote key
         /// also named A_KEY.
         CAVERN_linkAttrib_c linkAttribute;
         CAVERN_irbLink_c *aLink = aChannel->link(aKey, &remote_aKeyId, &linkAttribute);
         if (aLink == NULL) {
                  printf("Yo dude! The link didn't work!\n");
                  fflush(stdout);
            }
 /// -------------------------------------------------------------

 
         while (!CAVEgetbutton(CAVE_ESCKEY)){

                  pfSync();
                  pfCAVEPreFrame();
                  pfFrame();
                  pfCAVEPostFrame();

                  pfVec3 eye;

                  pfCAVEGetPosition(CAVE_WAND,eye.vec);
                  eye.vec[0] = movement;
                  pfuSmokeOrigin(smoke,eye,0.25f);

         }

         CAVEHalt();
         pfExit();
         return 0;

}
 

int smoke_draw(pfTraverser *trav,void *data)
{

         pfVec3 eye;

         // pfCAVEGetPosition(CAVE_WAND,eye.vec);
         CAVEGetPosition(CAVEEye,eye.vec);
         pfuDrawSmokes(eye);
         return PFTRAV_CONT;
}

#define SPEED 2.0f
 
pfDCS * create_scene(pfChannel *chan)
{

         pfScene *scene = new pfScene;
         pfGeoState *gstate = new pfGeoState;
         pfDCS *dcs = new pfDCS;
         gstate->setMode(PFSTATE_ENLIGHTING, PF_ON);
         gstate->setMode(PFSTATE_CULLFACE, PFCF_OFF);
         scene->setGState(gstate);
         scene->addChild(new pfLightSource);
         scene->addChild(dcs);
         chan->setScene(scene);
         create_smoke(dcs);
         return dcs;
}

void create_smoke(pfGroup *parent)
{
         origin[0] = origin[2] = 0; origin[1] = 5;
         dir[0] = dir[2] = 1; dir[1] = 0;
         smoke = pfuNewSmoke();
         pfuSmokeType(smoke,PFUSMOKE_SMOKE);
         pfuSmokeOrigin(smoke,origin,1.0f);
         pfuSmokeVelocity(smoke,3.0f,0.2f);
         pfuSmokeDir(smoke,dir);
         pfuSmokeMode(smoke,PFUSMOKE_START);
         pfuSmokeDuration(smoke,50000.0f);

         pfuSmokeDensity(smoke,1,1,0.5);

         node = new pfGeode;
         parent->addChild(node);
         bsph.radius = 100.0f;
         bsph.center = origin;
         node->setBound(&bsph,PFBOUND_STATIC);
         node->setTravFuncs(PFTRAV_DRAW,smoke_draw,NULL);
         node->setTravData(PFTRAV_DRAW,smoke);
}