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

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

 This file is part of the QGLViewer library version 2.8.0.

 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 <qdatetime.h>
#include <qevent.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
