/* Reading 3D models into Performer
Original Performer code by Dave Pape.
Modifications for CAVERN by Jason Leigh. jleigh@eecs.uic.edu

This program gets the IRB setup to trigger on any new incoming data
on a key called MODEL_KEY.
When new data arrives it is exported to an external file and then
read in by Performer. The exporting is necessary because I don't
know of a way to get Performer to read a model directly from memory.

If an argument is given to this program it will attempt to open
a connection to a remote IRB. If new data is placed at the remote
IRB it will automatically be forwarded to this program for loading.
*/

#include "CAVERN.h++"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <Performer/pf/pfChannel.h>
#include <Performer/pf/pfEarthSky.h>
#include <Performer/pf/pfLightSource.h>
#include <Performer/pf/pfGroup.h>
#include <Performer/pfdu.h>
#include <Performer/pf/pfDCS.h>
#include <pfcave.h>

pfDCS* create_scene(pfChannel *chan);
pfDCS * load_objects(char *filename,pfDCS *parent);

int doLoad = 0;

CAVERN_irbChannel_c *g_Channel = NULL;
CAVERN_irbLink_c *g_Link = NULL;
CAVERN_irbKey_c *g_modelKey = NULL;

// Handle when a channel is broken.
void channelCB(CAVERN_irbChannel_c::CAVERN_channelEvent_t event,
   CAVERN_irbChannel_c *thisChannel,
   void* userData)
{
         if (event == CAVERN_irbChannel_c::BROKEN_CHANNEL) {
                  cvrnMesg(NULL,"CHANNEL BROKEN!");
             }
         void makeChannelAndLink(char *);
         makeChannelAndLink((char*) userData);
}

// Make a channel and a link to link the MODEL_KEY
void makeChannelAndLink(char *remoteHost)
{
         CAVERN_irbKeyId_c modelKeyId;
         modelKeyId.setName("MODEL_KEY");

         CAVERN_irbChannel_c::status_t channelRetStatus;

         CAVERN_irbId_c remoteIRBId;
         remoteIRBId.setAddress(remoteHost);

         do {
                  cvrnPrintf("Attempting to Connect...\n");
                  g_Channel->open(&remoteIRBId,NULL,CAVERN_irbChannel_c::RELIABLE, &channelRetStatus);
                  g_Channel->trigger(channelCB, (void*) remoteHost);
                sleep(2);
             } while(channelRetStatus != CAVERN_irbChannel_c::OK);

         cvrnPrintf("Connected\n");

         if (g_Channel) {

                  if (g_Link) delete g_Link;

                  /// Create a link to link the local key named MODEL_KEY with the remote key
                  /// also named MODEL_KEY.
                  CAVERN_linkAttrib_c linkAttribute;
                  g_Link = g_Channel->link(g_modelKey, &modelKeyId,&linkAttribute);
                  if (g_Link == NULL) {
                           printf("Yo dude! The link didn't work!\n");
                           fflush(stdout);
                      }

             }

}
 

// Callback for when a new model arrives.
void newModelCB(CAVERN_irbKey_c::CAVERN_irbKeyEvent_t event,
      CAVERN_irbKey_c *thisKey,
      void* userData)
{
         // pfDCS *mainGroup = (pfDCS*) userData;

         int size;
         CAVERN_irbKey_c::status_t status;
         thisKey->getSize(&size, &status);
         if (size) {
                  cvrnPrintf("DATA ARRIVED. EXPORTING\n");
                  thisKey->export("tempmodel.iv", &status);

                  // Ordinarily I would do a performer load right
                  // after the export but it seems something about Performer
                  // really barfs. So it works on an O2 but not an Onyx2.
                  // So instead I signal that the model has been exported
                  // and let the main loop do the loading.
                  cvrnPrintf("EXPORTING DONE\n");
                  doLoad = 1;
             }

}

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

         // Initialize Performer
         pfInit();

         // Equivalent to CAVE Config. Must be called after pfInit()
         pfCAVEConfig(&argc,argv,NULL);

         // Performer config
         pfConfig();

         // Craetes all the channels for performer
         pfCAVEInitChannels();

         // Create a scene composed of objects loaded from filenames specified
         // in the command line arguments.
         pfDCS *mainGroup;
         mainGroup = create_scene(pfCAVEMasterChan());

         /// Startup CAVERN.
         CAVERN_irb_c *personalIRB = CAVERNInit(&argc, &argv, NULL);
         if (!personalIRB) exit(1);

         /// Define a local key called MODEL_KEY
         CAVERN_irbKeyId_c modelKeyId;
         modelKeyId.setName("MODEL_KEY");

         CAVERN_irb_c::status_t irbStatus;
         g_modelKey = personalIRB->define(&modelKeyId, NULL, &irbStatus);
         g_modelKey->trigger(newModelCB, (void *) mainGroup);

         if (argc > 1) {
                  /// Open the channel to a remote IRB specified on the command line.
                  g_Channel = personalIRB->createChannel();
                  makeChannelAndLink(argv[1]);
             }

         float rotInc = 0;

         // Place the objects near the center front of the CAVE
         mainGroup->setTrans(0,4,4);

         while (!CAVEgetbutton(CAVE_ESCKEY))
         {
              // Constantly spin the objects that are loaded in.
              mainGroup->setRot(0,0,rotInc++);

              // If object is ready to load do it...
              if (doLoad) {
                       pfDCS* theDCSToAdd;

                       cvrnPrintf("PERFORMER LOAD\n");
                       theDCSToAdd = load_objects("tempmodel.iv",mainGroup);
                       if (theDCSToAdd)
                       mainGroup->addChild(theDCSToAdd);
                       doLoad = 0;
                       cvrnPrintf("PERFORMER DONE LOAD\n");

                  }

              // Required by performer to synch things to a certain frame rate.
              pfSync();

              // Updates tracker data and sets up new projects for each channel.
              pfCAVEPreFrame();

              // Required by performer to trigger cull and draw
              pfFrame();

              // Updates non latency critical data.
              pfCAVEPostFrame();
         }

         CAVEHalt();
         pfExit();
         return 0;

}

pfDCS * create_scene(pfChannel *chan)
{
         /* Create a pfScene */
         pfScene *scene = new pfScene;
         pfGeoState *gstate = new pfGeoState;
         pfDCS *group = new pfDCS;

         /* Set up the default GeoState to specify the how the geometry is to
            be displayed.
            */
         gstate->setMode(PFSTATE_ENLIGHTING, PF_ON);
         gstate->setMode(PFSTATE_CULLFACE, PFCF_OFF);
         scene->setGState(gstate);

         /* Add a light source to the scene as well as the dcs */
         pfLightSource *myLightSource = new pfLightSource;
         myLightSource->setPos(10,10,10,1);

         scene->addChild(myLightSource);

         /* Add a light with default settings */
         scene->addChild(new pfLightSource);

         /* Add the group, and load the objects under it */
         scene->addChild(group);
         // load_objects(argc,argv,group);

         /* Assign the scene to the display channel */
         chan->setScene(scene);

         return group;

}
 

pfDCS * load_objects(char *filename,pfDCS *parent)
{
         static float i=0;
         pfNode *obj;
         if (obj = pfdLoadFile(filename)) {
                  pfDCS *newDCS = new pfDCS;
                  newDCS->addChild(obj);
                  newDCS->setTrans(0,0,i+=1);
                  //  parent->addChild(newDCS);
                  return newDCS;
             }
         return NULL;
}