#include "TGLAutoRotator.h"
#include "TGLPhysicalShape.h"
#include "TGLLogicalShape.h"
#include "TGLViewer.h"
#include "TGLCamera.h"
#include "TGLScene.h"
#include "TMath.h"
#include "TTimer.h"
#include "TStopwatch.h"
ClassImp(TGLAutoRotator);
TGLAutoRotator::TGLAutoRotator(TGLViewer* v) :
   fViewer(v), fCamera(0),
   fTimer(new TTimer), fWatch(new TStopwatch),
   fRotateScene(kFALSE),
   fDeltaPhi(0.005),
   fDt    (0.01),
   fWPhi  (0.40),
   fWTheta(0.15), fATheta(0.5),
   fWDolly(0.30), fADolly(0.4),
   fTimerRunning(kFALSE),
   fImageCount(0), fImageAutoSave(kFALSE),
   fImageGUIBaseName("animation"), fImageGUIOutMode(1)
{
   
   fTimer->Connect("Timeout()", "TGLAutoRotator", this, "Timeout()");
}
TGLAutoRotator::~TGLAutoRotator()
{
   
   delete fWatch;
   delete fTimer;
}
void TGLAutoRotator::SetDt(Double_t dt)
{
   
   
   fDt = TMath::Range(0.01, 1.0, dt);
   if (fTimerRunning)
   {
      fTimer->SetTime(TMath::Nint(1000*fDt));
      fTimer->Reset();
   }
}
void TGLAutoRotator::SetATheta(Double_t a)
{
   
   
   a = TMath::Range(0.01, 1.0, a);
   if (fTimerRunning)
   {
      fThetaA0 = fThetaA0 * a / fATheta;
   }
   fATheta = a;
}
void TGLAutoRotator::SetADolly(Double_t a)
{
   
   
  a = TMath::Range(0.01, 1.0, a);
  if (fTimerRunning)
  {
     fDollyA0 = fDollyA0 * a / fADolly;
  }
  fADolly = a;
}
void TGLAutoRotator::Start()
{
   
   if (fTimerRunning)
   {
      Stop();
   }
   fCamera = & fViewer->CurrentCamera();
   fThetaA0 = fATheta * TMath::PiOver2();
   fDollyA0 = fADolly * fCamera->GetCamTrans().GetBaseVec(4).Mag();
   fTimerRunning = kTRUE;
   fTimer->SetTime(TMath::Nint(1000*fDt));
   fTimer->Reset();
   fTimer->TurnOn();
   fWatch->Start();
}
void TGLAutoRotator::Stop()
{
   
   if (fTimerRunning)
   {
      fWatch->Stop();
      fTimer->TurnOff();
      fTimerRunning = kFALSE;
   }
}
void TGLAutoRotator::Timeout()
{
   
   
   if (!fTimerRunning || gTQSender != fTimer)
   {
      Error("Timeout", "Not running or not called via timer.");
      return;
   }
   using namespace TMath;
   fWatch->Stop();
   Double_t time = fWatch->RealTime();
   fWatch->Continue();
   if (fRotateScene) {
      RotateScene();
   } else {
      Double_t delta_p = fWPhi*fDt;
      Double_t delta_t = fThetaA0*fWTheta*Cos(fWTheta*time)*fDt;
      Double_t delta_d = fDollyA0*fWDolly*Cos(fWDolly*time)*fDt;
      Double_t th      = fCamera->GetTheta();
      if (th + delta_t > 3.0 || th + delta_t < 0.1416)
         delta_t = 0;
      fCamera->RotateRad(delta_t, delta_p);
      fCamera->RefCamTrans().MoveLF(1, -delta_d);
   }
   fViewer->RequestDraw(TGLRnrCtx::kLODHigh);
   if (fImageAutoSave)
   {
      TString filename;
      if (fImageName.Contains("%"))
      {
         filename.Form(fImageName, fImageCount);
      }
      else
      {
         filename = fImageName;
      }
      fViewer->SavePicture(filename);
      ++fImageCount;
   }
}
void TGLAutoRotator::StartImageAutoSaveAnimatedGif(const TString& filename)
{
   
   
   
   
   if ( ! filename.Contains(".gif+"))
   {
      Error("StartImageAutoSaveAnimatedGif", "Name should end with '.gif+'. Not starting.");
      return;
   }
   fImageName     = filename;
   fImageCount    = 0;
   fImageAutoSave = kTRUE;
}
void TGLAutoRotator::StartImageAutoSave(const TString& filename)
{
   
   
   
   
   
   if ( ! filename.Contains("%"))
   {
      Error("StartImageAutoSave", "Name should include a '%%' character, like 'image-%%05d.png'. Not starting.");
      return;
   }
   fImageName     = filename;
   fImageCount    = 0;
   fImageAutoSave = kTRUE;
}
void TGLAutoRotator::StopImageAutoSave()
{
   
   fImageAutoSave = kFALSE;
}
void TGLAutoRotator::SetImageGUIOutMode(Int_t m)
{
   
   
   
   if (m < 1 || m > 2)
   {
      Warning("SetImageGUIOutMode", "Invalid value, ignoring");
      return;
   }
   fImageGUIOutMode = m;
}
void TGLAutoRotator::StartImageAutoSaveWithGUISettings()
{
   
   if (fImageGUIOutMode == 1)
   {
      TString name = fImageGUIBaseName + ".gif+";
      StartImageAutoSaveAnimatedGif(name);
   }
   else if (fImageGUIOutMode == 2)
   {
      TString name = fImageGUIBaseName + "-%05d.png";
      StartImageAutoSave(name);
   }
   else
   {
      Warning("StartImageAutoSaveWithGUISettings", "Unsupported mode '%d'.", fImageGUIOutMode);
   }
}
void TGLAutoRotator::RotateScene()
{
   
   
   
   TGLViewer::SceneInfoList_t & scenes = fViewer->fScenes;
   TGLViewer::SceneInfoList_i sceneIter = scenes.begin();
   
   for (; sceneIter != scenes.end(); ++sceneIter) {
     TGLScene::TSceneInfo *sceneInfo = dynamic_cast<TGLScene::TSceneInfo *>(*sceneIter);
      if (sceneInfo) {
         TGLPhysicalShape *axisShape = 0;
         TGLScene::ShapeVec_i shapeIter = sceneInfo->fShapesOfInterest.begin();
         for (; shapeIter != sceneInfo->fShapesOfInterest.end(); ++shapeIter) {
            TGLPhysicalShape * const testShape = const_cast<TGLPhysicalShape *>(*shapeIter);
            if (testShape && testShape->GetLogical()->ID()->TestBit(13)) {
               axisShape = testShape;
               break;
            }
         }
         
         TGLVector3 axis;
         TGLVertex3 center;
         
         if (!axisShape) {
            const TGLBoundingBox &bbox = sceneInfo->GetTransformedBBox();
            axis = bbox.Axis(2);
            center = bbox.Center();
         } else {
            axis = axisShape->BoundingBox().Axis(2);
            center = axisShape->BoundingBox().Center();
         }
         
         shapeIter = sceneInfo->fShapesOfInterest.begin();
         for (; shapeIter != sceneInfo->fShapesOfInterest.end(); ++shapeIter) {
            if (TGLPhysicalShape * const shape = const_cast<TGLPhysicalShape *>(*shapeIter))
               shape->Rotate(center, axis, fDeltaPhi);
         }
      }
   }
}