/* 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_SENDER_H
#define BLINKER_SENDER_H

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

#include <BlinkenLib/BlinkenProto.h>
#include <BlinkenLib/BlinkenFrame.h>

#include "Directory.h"
#include "File.h"
#include "InStreamFile.h"
#include "IoCallee.h"
#include "Mgrs.h"
#include "Module.h"
#include "ListTracker.h"
#include "Protocol.h"
#include "ProtocolFile.h"
#include "SettingFile.h"
#include "StreamRecv.h"
#include "Time.h"
#include "TimeCallee.h"

namespace Blinker {

/// stream sender
template<typename ADDR, typename SOCK>
class Sender: public IoCallee, public Module, public StreamRecv,
              public TimeCallee
{
protected:
  /// type for address setting file
  typedef SettingFile<ADDR> AddrFile;

  /// static destination
  class Dest;

  /// static destination list tracker
  typedef ListTracker<Sender, Dest, Directory> DestListTracker;

  /// dynamic destinations: address -> time of last request
  typedef std::map<ADDR, Time> DynDests;

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

  /// virtual destructor
  virtual ~Sender();

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

  /// assignment operator disabled
  const Sender & operator=(const Sender &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 callback when I/O object is readable
   * @param[in] io I/O object that is readable
   */
  virtual void ioReadCall(Io *io);

  /**
   * @brief callback when I/O object is writable
   * @param[in] io I/O object that is writable
   */
  virtual void ioWriteCall(Io *io);

protected:
  /// (re-)read protocol
  void readProto();

  /// create socket and bind it
  void createSock();

  /// destroy socket
  void destroySock();

  /// remove timed-out dynamic destinations
  void removeTimedOutDynDests();

  /// send current protocol data to all destinations
  void sendAllProto();

  /// send "no frame" to all destinations
  void sendAllNoFrame();

  /**
   * @brief send data to static/dynamic destinations
   * @param[in] data *pData protocol data to send
   */
  void sendDests(const std::string *pData);

  /**
   * @brief send protocol data to address
   * @param[in] data protocol data of frame (empty if unknown)
   * @param[in] addr address to send to
   */
  void sendProto(const std::string &data, const ADDR &addr) const;

  /**
   * @brief convert frame to protocol data
   * @param[in] pFrame frame (NULL for none)
   * @param[out] data protocol data
   */
  void frame2data(stBlinkenFrame *pFrame, std::string &data) const;

  /// receive data from socket
  void receiveFromSock();

protected:
  InStreamFile    m_fileInStream;    ///< input stream name file
  AddrFile        m_fileBind;        ///< bind address file
  ProtocolFile    m_fileProtocol;    ///< protocol file
  SOCK            *m_pSock;          ///< socket to use for sending streams
  DestListTracker m_destListTracker; ///< static destinations tracker
  DynDests        m_dynDests;        ///< dynamic destinations
  std::string     m_noFrameData;     /**< "no frame" protocol data
                                          (empty if unknown) */
  std::string     m_data;            /**< current protocol data
                                          (empty if unknown) */
}; // class Sender

} // namespace Blinker

#endif // #ifndef BLINKER_SENDER_H

