/* Modified to show live data CMU/PSC/KEE 11/9/2006 */

/*=========================================================================

  Program:   Visualization Toolkit
  Module:    $RCSfile: vtkAnimationScene.cxx,v $

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/

#include "vtkAnimationScene.h"
#include "vtkObjectFactory.h"
#include "vtkCollection.h"
#include "vtkCollectionIterator.h"
#include "vtkTimerLog.h"

vtkCxxRevisionMacro(vtkAnimationScene, "$Revision: 1.8 $");
vtkStandardNewMacro(vtkAnimationScene);

//----------------------------------------------------------------------------
vtkAnimationScene::vtkAnimationScene()
{
  this->PlayMode = PLAYMODE_SEQUENCE;
  this->FrameRate = 10.0;
  this->Loop = 0;
  this->InPlay = 0;
  this->StopPlay = 0;
  this->AnimationTime = 0.0;

  this->AnimationCues = vtkCollection::New();
  this->AnimationCuesIterator = this->AnimationCues->NewIterator();
  this->AnimationTimer = vtkTimerLog::New();

  this->onGlobalLD = 1;   // Locks out livedata feature if F
  this->onLD = this->numPeLD = this->numLoopStepsLD = 0;
  this->runDirLD = this->timeDirBaseLD = this->fileNameBaseLD = 0;
  this->fileExtLD = 0;
  this->initFileNameLD = "livedata.txt";
}

//----------------------------------------------------------------------------
vtkAnimationScene::~vtkAnimationScene()
{
  if (this->InPlay)
    {
    this->Stop();
    }
  this->AnimationCues->Delete();
  this->AnimationCuesIterator->Delete();
  this->AnimationTimer->Delete();

   if( this->runDirLD ) free( this->runDirLD );
   if( this->timeDirBaseLD ) free( this->timeDirBaseLD );
   if( this->fileNameBaseLD ) free( this->fileNameBaseLD );
   if( this->fileExtLD ) free( this->fileExtLD );
}

//----------------------------------------------------------------------------
void vtkAnimationScene::AddCue(vtkAnimationCue* cue)
{
  if (this->AnimationCues->IsItemPresent(cue))
    {
    vtkErrorMacro("Animation cue already present in the scene");
    return;
    }
  if (this->TimeMode == vtkAnimationCue::TIMEMODE_NORMALIZED &&
    cue->GetTimeMode() != vtkAnimationCue::TIMEMODE_NORMALIZED)
    {
    vtkErrorMacro("A cue with relative time mode cannot be added to a scene "
      "with normalized time mode.");
    return;
    }
  this->AnimationCues->AddItem(cue);
}

//----------------------------------------------------------------------------
void vtkAnimationScene::RemoveCue(vtkAnimationCue* cue)
{
  this->AnimationCues->RemoveItem(cue);
}

//----------------------------------------------------------------------------
void vtkAnimationScene::SetTimeMode(int mode)
{
  if (mode == vtkAnimationCue::TIMEMODE_NORMALIZED)
    {
    // If noralized time mode is being set on the scene,
    // ensure that none of the contained cues need relative times.
    vtkCollectionIterator *it = this->AnimationCuesIterator;
    for (it->InitTraversal(); !it->IsDoneWithTraversal(); it->GoToNextItem())
      {
      vtkAnimationCue* cue = 
        vtkAnimationCue::SafeDownCast(it->GetCurrentObject());
      if (cue && cue->GetTimeMode() != vtkAnimationCue::TIMEMODE_NORMALIZED)
        {
        vtkErrorMacro("Scene contains a cue in relative mode. It must be removed "
          "or chaged to normalized mode before changing the scene time mode");
        return;
        }
      }
    }
  this->Superclass::SetTimeMode(mode);
}

//----------------------------------------------------------------------------
void vtkAnimationScene::InitializeChildren()
{
  // run thr all the cues and init them.
  vtkCollectionIterator *it = this->AnimationCuesIterator;
  for (it->InitTraversal(); !it->IsDoneWithTraversal(); it->GoToNextItem())
    {
    vtkAnimationCue* cue = 
      vtkAnimationCue::SafeDownCast(it->GetCurrentObject());
    if (cue)
      {
      cue->Initialize();
      }
    }
}

//----------------------------------------------------------------------------
void vtkAnimationScene::FinalizeChildren()
{
  vtkCollectionIterator *it = this->AnimationCuesIterator;
  for (it->InitTraversal(); !it->IsDoneWithTraversal(); it->GoToNextItem())
    {
    vtkAnimationCue* cue = 
      vtkAnimationCue::SafeDownCast(it->GetCurrentObject());
    if (cue)
      {
      cue->Finalize();
      }
    }
}

//----------------------------------------------------------------------------
void vtkAnimationScene::Play()
{
   int endCurrentPass;  // End current pass through the animation when True
  if (this->InPlay)
    {
    return;
    }
  
  if (this->TimeMode == vtkAnimationCue::TIMEMODE_NORMALIZED)
    {
    vtkErrorMacro("Cannot play a scene with normalized time mode");
    return;
    }
  if (this->EndTime <= this->StartTime)
    {
    vtkErrorMacro("Scene start and end times are not suitable for playing");
    return;
    }

  this->InPlay = 1;
  this->StopPlay = 0;
  this->FrameRate = (!this->FrameRate)? 1.0 : this->FrameRate;
  // the actual play loop, check for StopPlay flag.
  double deltatime = 0.0;
  double currenttime = this->AnimationTime;
  double span = this->EndTime - this->StartTime;
  
  // adjust currenttime to a valid time.
  currenttime = (currenttime < this->StartTime || currenttime >= this->EndTime)?
    this->StartTime : currenttime;
  double STime = currenttime;
  double clocktime = currenttime;
  double oldclocktime = clocktime;
  double time_adjustment = 0;
  this->initLD();
  this->AnimationTimer->StartTimer();
  do
    {
    this->Initialize(); // Set the Scene in unintialized mode.
    // deltatime = 0.0;
    endCurrentPass = 0;

     if( this->onLD ) {
        this->setTimeLD( &time_adjustment, &clocktime );
     }

     do
     {
     currenttime = clocktime - time_adjustment;

     if( this->onLD ) {
        if( nextLD( currenttime ) )
           endCurrentPass = 1;
        else
           this->Tick(currenttime, deltatime);
     }
     else {
        this->Tick(currenttime, deltatime);
     }


      oldclocktime = clocktime;
      if (this->PlayMode == PLAYMODE_REALTIME)
        {
        this->AnimationTimer->StopTimer();
        clocktime = this->AnimationTimer->GetElapsedTime() + 
          STime;
        }
      else if (this->PlayMode == PLAYMODE_SEQUENCE)
        {
        clocktime += 1.0 / this->FrameRate;
        }
      else
        {
        vtkErrorMacro("Invalid Play Mode");
        this->StopPlay = 1;
        break;
        }
      deltatime = clocktime - oldclocktime;
      deltatime = (deltatime < 0)? -1*deltatime : deltatime;

      } while (!endCurrentPass && !this->StopPlay
         && this->CueState != vtkAnimationCue::INACTIVE);

    //time_adjustment += span;
    time_adjustment = clocktime;

    } while (this->Loop && !this->StopPlay);


  this->StopPlay = 0;
  this->InPlay = 0;
}

//----------------------------------------------------------------------------
void vtkAnimationScene::Stop()
{
  if (!this->InPlay)
    {
    return;
    }
  this->StopPlay = 1;
}

//----------------------------------------------------------------------------
void vtkAnimationScene::TickInternal(double currenttime, double deltatime)
{
  this->AnimationTime = currenttime;
  
  vtkCollectionIterator* iter = this->AnimationCuesIterator;
  for (iter->InitTraversal(); !iter->IsDoneWithTraversal(); iter->GoToNextItem())
    {
    vtkAnimationCue* cue = vtkAnimationCue::SafeDownCast(
      iter->GetCurrentObject());
    if (cue)
      {
      switch(cue->GetTimeMode())
        {
      case vtkAnimationCue::TIMEMODE_RELATIVE:
        cue->Tick(currenttime - this->StartTime, deltatime);
        break;
      case vtkAnimationCue::TIMEMODE_NORMALIZED:
        cue->Tick( (currenttime - this->StartTime) / (this->EndTime - this->StartTime),
          deltatime / (this->EndTime - this->StartTime));
        break;
      default:
        vtkErrorMacro("Invalid cue time mode");
        }
      }
    }
  this->Superclass::TickInternal(currenttime, deltatime);
}

//----------------------------------------------------------------------------
void vtkAnimationScene::StartCueInternal()
{
  this->Superclass::StartCueInternal();
  this->InitializeChildren();
}

//----------------------------------------------------------------------------
void vtkAnimationScene::EndCueInternal()
{
  this->FinalizeChildren();
  this->Superclass::EndCueInternal();
}

//----------------------------------------------------------------------------
void vtkAnimationScene::SetAnimationTime(double currenttime)
{
  if (this->InPlay)
    {
    vtkErrorMacro("SetAnimationTime cannot be called while playing");
    return;
    }
  this->Initialize();
  this->Tick(currenttime,0.0);
  if (this->CueState == vtkAnimationCue::INACTIVE)
    {
    this->Finalize();
    }
}

//----------------------------------------------------------------------------
void vtkAnimationScene::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);
  os << indent << "PlayMode: " << this->PlayMode << endl;
  os << indent << "FrameRate: " << this->FrameRate << endl;
  os << indent << "Loop: " << this->Loop << endl;
  os << indent << "InPlay: " << this->InPlay << endl;
  os << indent << "StopPlay: " << this->StopPlay << endl;
  os << indent << "AnimationTime: " << this->AnimationTime << endl;
}




/*------------------------------------------------------------------------------

Livedata Support: Read file of parameters

------------------------------------------------------------------------------*/

void vtkAnimationScene::initLD( void )
{
   int i, n, iLine;
   char *s, tmp[256];
   FILE *file;

   file = 0;

////  Clear everything

   if( this->runDirLD ) {
      free( this->runDirLD );
      this->runDirLD = 0;
   }

   if( this->timeDirBaseLD ) {
      free( this->timeDirBaseLD );
      this->timeDirBaseLD = 0;
   }

   if( this->fileNameBaseLD ) {
      free( this->fileNameBaseLD );
      this->fileNameBaseLD = 0;
   }

   if( this->fileExtLD ) {
      free( this->fileExtLD );
      this->fileExtLD = 0;
   }

   this->numPeLD = 0;
   this->numLoopStepsLD = 0;
   this->lastTimeFoundLD = -1;
   this->onLD = 0;

////  Nothing else to do if feature is off

   if( !this->onGlobalLD ) goto wrapup;

////  Read, check, save parameters from initialization file

   if( !( file = fopen( this->initFileNameLD, "r" ) ) ) goto wrapup;

   for( iLine=0; iLine<14; ++iLine ) {
      if( !( s = fgets( tmp, 255, file ) ) ) {
         printf( "Error: cannot read line %d\n", iLine + 1 );
         printf( "   in %s\n", this->initFileNameLD );
         goto wrapup;
      }

      s = strchr( tmp, '\n' );
      if( s ) *s = 0;

      switch( iLine ) {

      ////  Run directory
         case 3:
            if( strlen(tmp) < 2 ) {
               printf( "Error: run directory name too short\n" );
               printf( "   in %s\n", this->initFileNameLD );
               goto wrapup;
            }

            this->runDirLD = strdup( tmp );
            break;

      ////  Time directory base
         case 5:
            if( strlen(tmp) < 2 ) {
               printf( "Error: time directory name too short\n" );
               printf( "   in %s\n", this->initFileNameLD );
               goto wrapup;
            }

            this->timeDirBaseLD = strdup( tmp );
            break;

      ////  File name base
         case 7:
            if( strlen(tmp) < 2 ) {
               printf( "Error: file name base too short\n" );
               printf( "   in %s\n", this->initFileNameLD );
               goto wrapup;
            }

            this->fileNameBaseLD = strdup( tmp );
            break;

      ////  File extension
         case 9:
            if( strlen(tmp) < 2 ) {
               printf( "Error: file name extension too short\n" );
               printf( "   in %s\n", this->initFileNameLD );
               goto wrapup;
            }

            this->fileExtLD = strdup( tmp );
            break;

      ////  Number of processors
         case 11:
            if( !( n = sscanf( tmp, "%d", &i ) ) ) {
               printf( "Error: num PEs cannot be read\n" );
               printf( "   in %s\n", this->initFileNameLD );
               goto wrapup;
            }

            if( ( i < 1 ) && ( i > 3000 ) ) {
               printf( "Error: num PEs %d not valid\n", i );
               printf( "   in %s\n", this->initFileNameLD );
               goto wrapup;
            }

            this->numPeLD = i;
            break;

      ////  Maximum number of steps in main animation loop
         case 13:
            if( !( n = sscanf( tmp, "%d", &i ) ) ) {
               printf( "Error: num loop steps cannot be read\n" );
               printf( "   in %s\n", this->initFileNameLD );
               goto wrapup;
            }

            if( ( i < 1 ) && ( i > 3000 ) ) {
               printf( "Error: num loop steps %d not valid\n", i );
               printf( "   in %s\n", this->initFileNameLD );
               goto wrapup;
            }

            this->numLoopStepsLD = i;
            break;
      }
   }

   onLD = 1;

 wrapup:
   if( this->onLD ) {
      printf( "livedata on\n" );
      printf( "   run directory is %s\n", this->runDirLD );
      printf( "   time directory base is %s\n", this->timeDirBaseLD );
      printf( "   file name base is %s\n", this->fileNameBaseLD );
      printf( "   file extension is %s\n", this->fileExtLD );
      printf( "   number of PEs is %d\n", this->numPeLD );
      printf( "   max number of steps in loop is %d\n", this->numLoopStepsLD );
   }

   if( file ) fclose( file );
   return;
}

/*------------------------------------------------------------------------------

Livedata Support: Set the time parameters

------------------------------------------------------------------------------*/

void vtkAnimationScene::setTimeLD(
   double *time_adjustment,
   double *clocktime
)
{
   int start, desiredStart;

   if( !this->onLD ) goto done;

   start = (int)( 0.001 + *clocktime - *time_adjustment );
   *time_adjustment = 0.0;
   desiredStart = 1 + this->lastTimeFoundLD - this->numLoopStepsLD;

   if( start > desiredStart ) {
      *clocktime = start;
   }
   else {
      *clocktime = desiredStart;
   }

 done:
   return;
}


/*------------------------------------------------------------------------------

Livedata Support: Test whether next time step is available
   Clock is converted to an integer time step by rounding down
   Return: 1/0 means skip/show data at given clock value
------------------------------------------------------------------------------*/

int vtkAnimationScene::nextLD( double clock )
{
   int pe, iClock, status, found;
   char timeDir[1024], fileName[1024];
   FILE *file;

   status = 0;

////  Nothing to do if initLD turned off the livedata feature

   if( !this->onLD ) goto done;

////  Guess which time step will be read
////     Is usually wrong if duration or time mode not the defaults

   iClock = (int)( clock + 0.001 );

////  Don't need to check if have already seen this time step

   if( iClock <= this->lastTimeFoundLD ) goto done;

////  Look for files at this time step

   found = 1;
   file = 0;
   status = 1;
   sprintf( timeDir, "%s/%s%d/", this->runDirLD, this->timeDirBaseLD, iClock );

   for( pe=0; pe<this->numPeLD; ++pe ) {
      sprintf( fileName, "%s%s%d.%s",
         timeDir, this->fileNameBaseLD, pe, this->fileExtLD );

      if( !( file = fopen( fileName, "r" ) ) ) {
         found = 0;
         break;
      }

      fclose( file );
      file = 0;
   }

   if( file ) fclose( file );
   if( !found ) goto done;

   if( this->lastTimeFoundLD < iClock ) this->lastTimeFoundLD = iClock;
   status = 0;

 done:
   return( status );
}
