/****************************************************************************

   Copyright (C) 2002-2008 Gilles Debunne. All rights reserved.

   This file is part of the QGLViewer library version 2.3.5.

   http://www.libqglviewer.com - contact@libqglviewer.com

   This file may be used under the terms of the GNU General Public License
   versions 2.0 or 3.0 as published by the Free Software Foundation and
   appearing in the LICENSE file included in the packaging of this file.
   In addition, as a special exception, Gilles Debunne gives you certain
   additional rights, described in the file GPL_EXCEPTION in this package.

   libQGLViewer uses dual licensing. Commercial/proprietary software must
   purchase a libQGLViewer Commercial License.

   This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
   WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.

 *****************************************************************************/

#ifndef EVENT_RECORDER_H
#define EVENT_RECORDER_H

#include "QGLViewer/frame.h"

#include <qevent.h>
#include <qdatetime.h>

class Viewer;
class ReplayInterface;

/*! \brief A QGLViewer event recorder, that allows for scenario recording and replay.

   An EventRecorder is associated to a QGLViewer. When the EventRecorder isRecording(), the different
   events that are sent to the qglviewer() are recorded in an internal data structure. The
   EventRecorder then can replay() these events with an identical timing in order to reproduce the
   recorded scenario. Snapshots can optionally be captured during playback, and the resulting image
   series can be converted into a movie.

   <h3>Event recording</h3>

   Press the 'R' to toggle event recording. A red dot appears in the upper right corner of the
   qglviewer() window during recording.

   The EventRecorder records all mouse and keyboard events using the eventFilter() function. These
   are usually sufficient to reproduce a given scenario.

   However, Frame specific events can be recorded using recordFrameState() and other arbitrary events
   are recorded using recordCustomEvent().

   Note that the mouse to camera motion conversion depends on the window's size (so that translations
   match for instance). The QGLViewer window is hence resized to the size it had during record at the
   beginning of replay.

   <h3>Replay</h3>

   Press 'Shift+R' to openReplayInterfaceWindow(). You can tune replay parameters, save and load
   recorded events, and replay() the recorded events, with an optional snapshot saving.

   Note that interaction with the viewer is still possible during and between replay(). This can be
   used to change the display mode during or between replay(), in order to generate several
   renderings of a given scenario.

   See cameraIsRestored(), manipulatedFrameIsRestored(), savesSnapshot(), snapshotFrameRate(),
   saveAtGivenFrameRate(), replaySpeed() for details on the replay() parameters.

   <h3>Movie creation</h3>

   The generated numbered image sequence can be converted into a movie by most non linear editing
   software. Here is a way to easily do this using the \c transcode publically available software
   (see <a href="http://zebra.fh-weingarten.de/~transcode/index.html">transcode home page</a>).

   \code
   > \ls -1 snapshot-* | sort -n > list.txt
   > transcode -i list.txt -x imlist,null -g 720x576 -y xvid,null -f 25 -o movie.avi -H 0
   \endcode
   Replace \c 720x576 by your actual snapshot size, and 25 by the frame rate you want. See the
   transcode man page for details.

   \nosubgrouping */
class EventRecorder : public QObject {
#ifndef DOXYGEN
  friend class ::ReplayInterface;
#endif
  Q_OBJECT

public:
  EventRecorder(Viewer* const qglviewer);
  virtual ~EventRecorder();

  /*! @name Recording interface */
  //@{
public:
  /*! Returns \c true between calls to startRecording() and stopRecording(). */
  bool isRecording() const {
    return isRecording_;
  };

public Q_SLOTS:
  void startRecording();
  void stopRecording();
  void toggleRecording();

  void replay();
  void openReplayInterfaceWindow();
  void saveEventRecord(const QString& filename = "") const;
  void loadEventRecord(const QString& filename = "");
  //@}

  /*! @name Replay parameters */
  //@{
public:
  /*! If \c true, at the beginning of replay(), the state of the qglviewer()
     QGLViewer::manipulatedFrame() is restored to the position and orientation it had when the
     EventRecorder startRecording(), so that the replay() can start from a similar viewer
     configuration.

     Default value is \c true. Set using setManipulatedFrameIsRestored() or using the EventRecorder
     interface window (see openReplayInterfaceWindow()). See also cameraIsRestored().

     Use resetBeforeReplay() to reset other viewer and scene related parameters before replay() is
     started. */
  bool manipulatedFrameIsRestored() const {
    return manipulatedFrameIsRestored_;
  };
  /*! If \c true, at the beginning of replay(), the state of the qglviewer() QGLViewer::camera() is
     restored to the position and orientation it had when the EventRecorder startRecording(), so that
     the replay() can start from a similar viewer configuration.

     Default value is \c true. Set using setCameraIsRestored() or using the EventRecorder interface
     window (see openReplayInterfaceWindow()). See also manipulatedFrameIsRestored().

     Use resetBeforeReplay() to reset other viewer and scene related parameters before replay() is
     started. */
  bool cameraIsRestored() const {
    return cameraIsRestored_;
  };
  /*! If \c true, calls QGLViewer::saveSnapshot() during replay(), at a snapshotFrameRate()
     frequency. This is useful to create snapshot series that can then be converted into a movie.
     See the Detailed description section for details on movie creation.

     Default value is \c true. Set using setSavesSnapshots() or using the EventRecorder interface
     window (see openReplayInterfaceWindow()). */
  bool savesSnapshots() const {
    return savesSnapshots_;
  };
  /*! Replay speed, with respect to the timing when the events were recorded. Default value is
     1.0, meaning replay matches recording speed.

     0.5 means that replay will be twice as long as recording, while 2.0 means it will two times
     faster. Set using setReplaySpeed().

     This value is only meaningful for real time replays. It is not taken into account when
     savesSnapshots() is \c true. Use setSnapshotFrameRate() instead. */
  float replaySpeed() const {
    return replaySpeed_;
  };
  /*! When \c true (default), a snapshot is saved at the snapshotFrameRate() frequency during
     replay. When \c false, a snapshot is saved after each qglviewer() redraw.

     Set this value to \c true to create a movie out of an interactive demo (where you move the
     camera, pause for a while, toggle some flags, then move again...) so that the "rythm" of your
     scenario is preserved in the resulting image sequence. Think of it as if a camera was filming
     the screen during your demo. Tune the frame rate using setSnapshotFrameRate().

     Set this value to \c false when an external timer paces your redraw rate. This is typically
     the case when you use the QGLViewer::animate() function, which in a loop computes an animation
     step and then draws the scene. In that case, no matter how long lasts an animation step, you
     want to save a snapshot after each redraw, so that each image corresponds to a given animation
     step, and hence a to a given animation time.

     This value is of course only meaningful when saveSnapshot() is \c true. Can be modified
     when you openReplayInterfaceWindow() or using setSaveAtGivenFrameRate(). */
  bool saveAtGivenFrameRate() const {
    return saveAtGivenFrameRate_;
  };
  /*! Defines the number of snapshots that will be generated per second during replay(), when
     savesSnapshots() and saveAtGivenFrameRate() are both \c true (otherwise meaningless).

     Default value is 25. Set using setReplayFrameRate() or using the EventRecorder interface
     window (see openReplayInterfaceWindow()). */
  int snapshotFrameRate() const {
    return snapshotFrameRate_;
  };

public Q_SLOTS:
  /*! Sets the manipulatedFrameIsRestored() value. Default value is \c true. */
  void setManipulatedFrameIsRestored(bool restored = true) {
    manipulatedFrameIsRestored_ = restored;
  };
  /*! Sets the cameraIsRestored() value. Default value is \c true. */
  void setCameraIsRestored(bool restored = true) {
    cameraIsRestored_ = restored;
  };
  /*! Sets whether replay savesSnapshots() or not. Default value is \c true. */
  void setSavesSnapshots(bool savesSnapshots = true) {
    savesSnapshots_ = savesSnapshots;
  };
  /*! Sets the snapshotFrameRate() frequency. Default value is 25 (Hertz). */
  void setSnapshotFrameRate(int snapshotFrameRate) {
    snapshotFrameRate_ = snapshotFrameRate;
  };
  /*! Defines the replaySpeed(). Default value is 1.0 (same speed as during recording). Values
     lower or equal to 0.0 are silently ignored. */
  void setReplaySpeed(float replaySpeed)
  {
    if(replaySpeed > 0.0)
      replaySpeed_ = replaySpeed;
  };
  /*! Sets the saveAtGivenFrameRate() value. Default is \c true. */
  void setSaveAtGivenFrameRate(bool saveAtGivenFrameRate) {
    saveAtGivenFrameRate_ = saveAtGivenFrameRate;
  };
  //@}

  /*! @name Record extra events */
  //@{
public:
Q_SIGNALS:
  /*! This signal is emitted during replay() when a custom event should be replayed back. Custom
     events are recorded using recordCustomEvent(). Connect this signal to slots that are able to
     reproduce this event.

     The \p id parameter is the one given in recordCustomEvent(). It can be used by the slot to
     select which custom event to reproduce. */
  void replayCustomEvent(int id);

public Q_SLOTS:
  void recordFrameState();
  void recordCustomEvent(int id = 0);
  //@}

protected:
  bool eventFilter(QObject* o, QEvent* e);

private Q_SLOTS:
  void changeImageSize(int i);
  void selectSnapshotName();
  void triggerNextEvent();
  void saveNumberedSnapshot() const;

private:
  Viewer* const qglviewer() const {
    return qglviewer_;
  };
  int predefinedFormat();
  void replayNextEvent();

  class FrameState {
public:
    FrameState(qglviewer::Frame* const fr);
    qglviewer::Frame* const frame;
    qglviewer::Frame state;
  };

  union EventPointer {
    QKeyEvent* keyEvent;
    QMouseEvent* mouseEvent;
    QWheelEvent* wheelEvent;
    FrameState* frameState;
    ;
  };

  class Event {
public:
    union EventPointer event;
    QEvent::Type type;
    int time;
  };

  struct PredefinedSettings {
    PredefinedSettings(){
    };
    PredefinedSettings(int w, int h, int fr)
      : width(w)
      , height(h)
      , frameRate(fr){
    };
    int width, height, frameRate;
  };

  // A s s o c i a t e d   Q G L V i e w e r
  Viewer* const qglviewer_;
  int viewerWidth_, viewerHeight_;

// R e c o r d s
#if QT_VERSION < 0x040000
  QValueVector<Event> eventRecords_;
#else
  QList<Event> eventRecords_;
#endif
  int eventIndex_;
  QTime time_;

  // I n t e r n a l   d a t a
  bool isRecording_;
#if QT_VERSION < 0x040000
  QValueVector<PredefinedSettings> predefinedSettings_;
#else
  QList<PredefinedSettings> predefinedSettings_;
#endif
  int nextSnapshotTime_;
  int nextReplayEvent_;
  bool nextEventIsSaveSnapshot_;

  // R e p l a y   p a r a m e t e r s
  qglviewer::Frame initialCameraFrame_, initialManipulatedFrame_;
  bool cameraIsRestored_, manipulatedFrameIsRestored_, savesSnapshots_;
  int snapshotFrameRate_;
  int recordDuration_;
  float replaySpeed_;
  bool saveAtGivenFrameRate_;

  // R e p l a y   i n t e r f a c e
  ReplayInterface* replayInterface_;
  int originalWidth_, originalHeight_;
};
#endif // EVENT_RECORDER_H
