/* 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_PHONE_H
#define BLINKER_PHONE_H

#include <map>
#include <string>
#include <vector>

#include "Directory.h"
#include "File.h"
#include "IoCallee.h"
#include "ListTracker.h"
#include "Mgrs.h"
#include "Module.h"
#include "OpConn.h"
#include "OpConnIf.h"
#include "SettingFile.h"
#include "Time.h"
#include "TimeCallee.h"

namespace Blinker {

/// phone connector (using EBIP)
template<typename ADDR, typename SOCK>
class Phone: public IoCallee, public Module, public OpConnIf, public TimeCallee
{
protected:
  /// type for address setting file
  typedef SettingFile<ADDR> AddrFile;

  /// extension to be called
  class Extension;

  /// extension list tracker
  typedef ListTracker<Phone, Extension, Directory> ExtListTracker;

  /// map of extensions to call (extension name -> module name)
  typedef std::map<std::string, std::string> ExtMap;

  /// map from line number to connection
  typedef std::map<unsigned int, OpConn *> LineConnMap;

  /// map from connection to line number
  typedef std::map<OpConn *, unsigned int> ConnLineMap;

  /// type for command buffer (command sent before line connected)
  typedef std::vector<std::string> CmdBuf;

  /// type for command buffer map (line to command buffer)
  typedef std::map<unsigned int, CmdBuf> CmdBufMap;

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

  /// virtual destructor
  virtual ~Phone();

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

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

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

  /// 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);

  /**
   * @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:
  /// (re-)read server address
  void readServer();

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

  /// destroy socket
  void destroySock();

  /// register with server
  void sendRegister();

  /// send heartbeat to server
  void sendHeartbeat();

  /**
   * @brief send accept message
   * @param[in] line number of line accept on
   */
  void sendAccept(unsigned int line);

  /**
   * @brief send play message
   * @param[in] line number of line to request play on
   * @param[in] sound name of sound to request
   */
  void sendPlay(unsigned int line, const std::string &sound);

  /**
   * @brief send hangup message
   * @param[in] line number of line to hangup
   */
  void sendHangup(unsigned int line);

  /**
   * @brief send message buffered to server
   * @param[in] line number of line to hangup
   * @param[in] msg message to send
   */
  void sendBuf(unsigned int line, const std::string &msg);

  /**
   * @brief send message to server now
   * @param[in] line number of line to hangup
   * @param[in] msg message to send
   */
  void sendNow(unsigned int line, const std::string &msg);

  /**
   * @brief send message to socket
   * @param[in] msg message to send
   */
  void sendSock(const std::string &msg);

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

  /**
   * @brief process message from server
   * @param[in] msg message from server
   */
  void serverMsg(const std::string &msg);

  /**
   * @brief process incoming call
   * @param[in] line number of phone line
   * @param[in] extension extenstion called (i.e. phone number)
   */
  void incomingCall(unsigned int line, const std::string &extension);

  /**
   * @brief phone line connected
   * @param[in] line number of phone line
   */
  void connected(unsigned int line);

  /**
   * @brief hangup on phone line
   * @param[in] line number of phone line
   */
  void hangup(unsigned int line);

  /**
   * @brief key pressed on phone line
   * @param[in] line number of phone line
   * @param[in] key key that has been pressed
   */
  void keyPressed(unsigned int line, char key);

  /**
   * @brief get phone line number from operator connection
   * @param[in] pConn operator connection object
   * @param[out] line phone line number
   * @return if phone line was found
   */
  bool conn2line(OpConn *pConn, unsigned int &line) const;

  /**
   * @brief get operator connection from phone line number
   * @param[in] line phone line number
   * @param[out] pConn operator connection object
   * @return if phone line was found
   */
  bool line2conn(unsigned int line, OpConn *&pConn) const;

  /// update time callback
  void updateTimeCallback();

protected:
  static const Time m_serverTimeout;
  static const Time m_heartbeatInterval;

protected:
  AddrFile       m_fileBind;       ///< bind address file
  AddrFile       m_fileServer;     ///< server address file
  ExtListTracker m_extListTracker; ///< extension tracker
  SOCK           *m_pSock;         ///< socket to use for sending messages
  Time           m_timeRegister;   ///< time to re-register
  Time           m_timeHeartbeat;  ///< time to send next heartbeat
  ExtMap         m_extMap;         ///< map of extensions to call
  LineConnMap    m_lineConnMap;    ///< map from line number to connection
  ConnLineMap    m_connLineMap;    ///< map from connection to line number
  CmdBufMap      m_cmdBufMap;      ///< commands to send after line connected
}; // class Phone

} // namespace Blinker

#endif // #ifndef BLINKER_PHONE_H

