//
//  MotionBlurSimWindow.cpp
//  dart
//
//  Created by Dong Xu on 1/22/17.
//
//

/////////////////////////////////////////////////////////////////////////
// OpenGL Motion blur require the Accumulate function of OpenGL
// To intergrate this class into the engine
// Change line 86 of dart/gui/glut/Window.cpp
// From  glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA | GLUT_MULTISAMPLE);
// to    glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA | GLUT_MULTISAMPLE | GLUT_ACCUM);
/////////////////////////////////////////////////////////////////////////


#include "dart/gui/glut/MotionBlurSimWindow.hpp"

#include "dart/constraint/ConstraintSolver.hpp"
#include "dart/gui/GLFuncs.hpp"
#include "dart/gui/glut/LoadGlut.hpp"

namespace dart {
namespace gui {
namespace glut {

//==============================================================================
MotionBlurSimWindow::MotionBlurSimWindow()
  : SimWindow()
{
  mMotionBlurFrequency = 1;
}

//==============================================================================
MotionBlurSimWindow::~MotionBlurSimWindow()
{
  // Do nothing
}

//==============================================================================
void MotionBlurSimWindow::setMotionBlurQuality(int _val)
{
  int numIter = mDisplayTimeout / (mWorld->getTimeStep() * 1000);
  if ( _val < 0 )
  {
    std::cout << "setMotionBlurQuality: input should be an int between 0-5, Regard as 0" << std::endl;
    std::cout << "0: No motion blur, 5: Motion blur with highest quality" << std::endl;
    mMotionBlurFrequency = numIter;
  }
  else if ( _val > 5 )
  {
    std::cout << "setMotionBlurQuality: input should be an int between 0-5, Regard as 5" << std::endl;
    std::cout << "0: No motion blur, 5: Motion blur with highest quality" << std::endl;
    mMotionBlurFrequency = 1;
  }
  else if ( _val == 0)
    mMotionBlurFrequency = mDisplayTimeout / (mWorld->getTimeStep() * 1000);
  else if ( _val == 1)
    mMotionBlurFrequency = std::min(numIter, 16);
  else if ( _val == 2)
    mMotionBlurFrequency = std::min(numIter, 8);
  else if ( _val == 3)
    mMotionBlurFrequency = std::min(numIter, 4);
  else if ( _val == 4)
    mMotionBlurFrequency = std::min(numIter, 2);
  else // _val == 5
    mMotionBlurFrequency = 1;
}
    
//==============================================================================
void MotionBlurSimWindow::render()
{
  int numIter = mDisplayTimeout / (mWorld->getTimeStep() * 1000);
  int numMotionBlurFrames = ceil( floor(mDisplayTimeout) / (mWorld->getTimeStep() * 1000 * mMotionBlurFrequency) );
  if (!mPlay && mSimulating)
  {
    for (int i = 0; i < numIter; i+=mMotionBlurFrequency)
    {
      for (int j = 0; j < mMotionBlurFrequency; j++)
      {
        if ( i+j < numIter )
        {
          timeStepping();
          mWorld->bake();
        }
      }

      // Update the camera position before every draw
      glMatrixMode(GL_PROJECTION);
      glLoadIdentity();
      gluPerspective(mPersp, \
                     static_cast<double>(mWinWidth)/static_cast<double>(mWinHeight), 0.1, 10.0);
      gluLookAt(mEye[0], mEye[1], mEye[2], 0.0, 0.0, -1.0, mUp[0], mUp[1], mUp[2]);
            
      glMatrixMode(GL_MODELVIEW);
      glLoadIdentity();
      initGL();
            
      mTrackBall.applyGLRotation();
            
      // Draw world origin indicator
      if (!mCapture)
      {
        glEnable(GL_DEPTH_TEST);
        glDisable(GL_TEXTURE_2D);
        glDisable(GL_LIGHTING);
        glLineWidth(2.0);
        if (mRotate || mTranslate || mZooming)
        {
          glColor3f(1.0f, 0.0f, 0.0f);
          glBegin(GL_LINES);
          glVertex3f(-0.1f, 0.0f, -0.0f);
          glVertex3f(0.15f, 0.0f, -0.0f);
          glEnd();
                    
          glColor3f(0.0f, 1.0f, 0.0f);
          glBegin(GL_LINES);
          glVertex3f(0.0f, -0.1f, 0.0f);
          glVertex3f(0.0f, 0.15f, 0.0f);
          glEnd();
                    
          glColor3f(0.0f, 0.0f, 1.0f);
          glBegin(GL_LINES);
          glVertex3f(0.0f, 0.0f, -0.1f);
          glVertex3f(0.0f, 0.0f, 0.15f);
          glEnd();
        }
      }
            
      glScalef(mZoom, mZoom, mZoom);
      glTranslatef(mTrans[0]*0.001, mTrans[1]*0.001, mTrans[2]*0.001);
            
      initLights();
      draw();
        
      if (i == 0)
      {
        //glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        glAccum(GL_LOAD, 1/float(numMotionBlurFrames));
      }
      else
      {
        glAccum(GL_ACCUM, 1/float(numMotionBlurFrames));
      }
    } // for loop
  } // if simulating
  else if ( mWorld->getRecording()->getNumFrames() == 0)
  {
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(mPersp, \
                   static_cast<double>(mWinWidth)/static_cast<double>(mWinHeight), 0.1, 10.0);
    gluLookAt(mEye[0], mEye[1], mEye[2], 0.0, 0.0, -1.0, mUp[0], mUp[1], mUp[2]);
      
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    initGL();
      
    mTrackBall.applyGLRotation();
      
    // Draw world origin indicator
    if (!mCapture)
    {
      glEnable(GL_DEPTH_TEST);
      glDisable(GL_TEXTURE_2D);
      glDisable(GL_LIGHTING);
      glLineWidth(2.0);
      if (mRotate || mTranslate || mZooming)
      {
        glColor3f(1.0f, 0.0f, 0.0f);
        glBegin(GL_LINES);
        glVertex3f(-0.1f, 0.0f, -0.0f);
        glVertex3f(0.15f, 0.0f, -0.0f);
        glEnd();
              
        glColor3f(0.0f, 1.0f, 0.0f);
        glBegin(GL_LINES);
        glVertex3f(0.0f, -0.1f, 0.0f);
        glVertex3f(0.0f, 0.15f, 0.0f);
        glEnd();
              
        glColor3f(0.0f, 0.0f, 1.0f);
        glBegin(GL_LINES);
        glVertex3f(0.0f, 0.0f, -0.1f);
        glVertex3f(0.0f, 0.0f, 0.15f);
        glEnd();
      }
    }
      
    glScalef(mZoom, mZoom, mZoom);
    glTranslatef(mTrans[0]*0.001, mTrans[1]*0.001, mTrans[2]*0.001);
      
    initLights();
    draw();
      
    glAccum(GL_LOAD, 1.0f);
  }
    
  // Draw trackball indicator
  // Currently, trackball is not counted into the motion blur
  if (mRotate && !mCapture)
    mTrackBall.draw(mWinWidth, mWinHeight);
  
  // Clear an return the buffer
  glAccum(GL_RETURN, 1.0f);
  glutSwapBuffers();
    
  if (mCapture)
    screenshot();
}

//==============================================================================
void MotionBlurSimWindow::displayTimer(int _val)
{
  if (mPlay)
  {
    mPlayFrame += 16;
    if (mPlayFrame >= mWorld->getRecording()->getNumFrames())
      mPlayFrame = 0;
  }
  glutPostRedisplay();
  glutTimerFunc(mDisplayTimeout, refreshTimer, _val);
}

} // namespace glut
} // namespace gui
} // namespace dart
