/* Blinker
   Copyright 2011-2019 Stefan Schuermans <stefan@blinkenarea.org>
   Copyleft GNU public license - http://www.gnu.org/copyleft/gpl.html
   a blinkenarea.org project */

#ifndef BLINKER_LOVELETTER_H
#define BLINKER_LOVELETTER_H

#include <list>
#include <map>
#include <string>

#include <BlinkenLib/BlinkenMovie.h>

#include "Directory.h"
#include "File.h"
#include "InStreamFile.h"
#include "ListTracker.h"
#include "Mgrs.h"
#include "Module.h"
#include "NameFile.h"
#include "OpConn.h"
#include "OpConnIf.h"
#include "OpReqIf.h"
#include "OutStreamFile.h"
#include "StreamRecv.h"
#include "Time.h"
#include "TimeCallee.h"

namespace Blinker {

/// on demand movie player
class Loveletter: public Module, public OpReqIf, public StreamRecv,
                  public TimeCallee
{
protected:
  /// movie
  class Movie;

  /// movie tracker
  typedef ListTracker<Loveletter, Movie, File> MovieTracker;

  /// movie iterator
  typedef MovieTracker::ListIt MovieIt;

  /// map from movie number to movie
  typedef std::map<std::string, Movie *> MovieMap;

public:
  /**
   * @brief constructor
   * @param[in] name module name
   * @param[in] mgrs managers
   * @param[in] dirBase base directory
   */
  Loveletter(const std::string &name, Mgrs &mgrs, const Directory &dirBase);

  /// virtual destructor
  virtual ~Loveletter();

private:
  /// copy constructor disabled
  Loveletter(const Loveletter &that);

  /// assignment operator disabled
  const Loveletter & operator=(const Loveletter &that);

public:
  /// check for update of configuration
  virtual void updateConfig();

  /**
   * @brief set current frame
   * @param[in] stream stream name
   * @param[in] pFrame current frame (NULL for none)
   */
  virtual void setFrame(const std::string &stream, stBlinkenFrame *pFrame);

  /// callback when requested time reached
  virtual void timeCall();

  /**
   * @brief check if accepting new operator connection is possible
   * @param[in] name operator interface name
   * @return if accepting new connection is possible
   */
  virtual bool acceptNewOpConn(const std::string &name);

  /**
   * @brief new operator connection
   * @param[in] name operator interface name
   * @param[in] pConn operator connection object
   *
   * The new connection may not yet be used for sending inside this callback.
   */
  virtual void newOpConn(const std::string &name, OpConn *pConn);

  /**
   * @brief key command received on operator connection
   * @param[in] pConn operator connection object
   * @param[in] key key that was pressed
   */
  virtual void opConnRecvKey(OpConn *pConn, char key);

  /**
   * @brief play command received on operator connection
   * @param[in] pConn operator connection object
   * @param[in] sound name of sound to play
   */
  virtual void opConnRecvPlay(OpConn *pConn, const std::string &sound);

  /**
   * @brief operator connection is closed
   * @param[in] pConn operator connection object
   *
   * The connection may not be used for sending any more in this callback.
   */
  virtual void opConnClose(OpConn *pConn);

protected:
  /// check if current movie changed and react
  void checkCurChanged();

  /// process current frame
  void procFrame();

  /// send current frame to output stream
  void sendFrame();

  /// close current operator connection
  void closeOpConn();

  /// start playing movie (indictaed by m_movieNumber)
  void startPlaying();

  /// stop playing movie
  void stopPlaying();

protected:
  NameFile      m_fileSound;      ///< file containing sound name
  OutStreamFile m_fileOutStream;  ///< output stream name file
  InStreamFile  m_fileHaltStream; /**< halt stream name file
                                       (playing halts if stream active) */
  MovieTracker  m_movieTracker;   ///< current movies
  MovieMap      m_movieMap;       ///< map from movie number to movie
  bool          m_curValid;       ///< if there is a current frame
  Movie         *m_pCurMovie;     ///< current movie or NULL
  int           m_curFrame;       ///< current frame in movie
  bool          m_curChange;      ///< current movie changed
  bool          m_halted;         ///< if playing is halted
  Time          m_remainTime;     /**< remaining time of current frame
                                       (valid if m_curValid && m_halted) */
  Time          m_nextTime;       /**< when to show next frame
                                       (valid if m_curValid && !m_halted) */
  OpConn        *m_pOpConn;       ///< current operator connection or NULL
  bool          m_sendPlay;       ///< if sending play request needed
  std::string   m_movieNumber;    ///< number of movie to play
}; // class Loveletter

} // namespace Blinker

#endif // #ifndef BLINKER_LOVELETTER_H

