// 
//   Copyright (C) 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
// 
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
//

#ifndef __HANDLER_H__
#define __HANDLER_H__ 1

#include <map>
#include <cstdint>
#include <mutex>
#include <memory>

#include <vector>
#include <string>

#ifdef HAVE_POLL
# include <sys/poll.h>
#else 
# ifdef HAVE_EPOLL
#  include <sys/epoll.h>
# endif
#endif

#include "log.h"
#include "network.h"
#include "buffer.h"
#include "element.h"
#include "cque.h"
#include "network.h"
#include "dsodefs.h" //For DSOEXPORT.
#include "proc.h"

#include "diskstream.h"
#include "sharedlib.h"
#include "extension.h"
#include "diskstream.h"

#include "rtmp.h"
#include "rtmp_msg.h"
#include "http.h"
#include "network.h"

// _definst_ is the default instance name
namespace cygnal
{

// The number of disk streams in the array.
const size_t STREAMS_BLOCK = 1000;

class Cygnal;
class HTTPServer;
class RTMPServer;

class Handler : public gnash::Extension, gnash::Network
{
public:
    /// \enum admin_cmd_e
    ///		The Admin command sent by the client
    typedef enum {
	UNKNOWN,
	STATUS,
	POLL,
	HELP,
	INTERVAL,
	QUIT
    } admin_cmd_e;
    /// This enum contains the possible values for streaming video
    /// types.
    typedef enum {
	RECORD,
	LIVE,
	APPEND
    } pub_stream_e;
    /// This typedef is only used for the io function that must be
    /// supported by the plugin.
    typedef size_t (*cygnal_io_write_t)(std::uint8_t *data, size_t size);
    typedef std::shared_ptr<cygnal::Buffer> (*cygnal_io_read_t)();
    typedef struct {
	std::string version;
	std::string description;
	std::string hostname;
	std::string path;	
  	cygnal_io_read_t read_func;
  	cygnal_io_write_t write_func;
	gnash::Network::protocols_supported_e protocol;
    } cygnal_init_t;
    
    /// This typedef is only used for the init function optionally
    /// supported by the plugin.
    typedef std::shared_ptr<cygnal_init_t>(*cygnal_io_init_t)(std::shared_ptr<gnash::RTMPMsg> &msg);

    DSOEXPORT Handler();
    ~Handler();

    /// \var sync
    ///     Send the onSync message to all connected clients
    bool sync() { return sync(_in_fd); };
    bool sync(int in_fd);

    // Access the name field
    void setName(const std::string &x) { _name = x; };
    std::string &getName() { return _name; }

    // Check the status of active disk streams, which is one less than
    // default as the Streams IDs start at 1.
    int getActiveDiskStreams() { return _diskstreams.size(); }
    // int removeDiskStream(std::shared_ptr<DiskStream> x);
    
    // Operate on a disk streaming inprogress
    std::shared_ptr<gnash::DiskStream> getDiskStream(int x) { return _diskstreams[x]; }
    void setDiskStream(int x, std::shared_ptr<gnash::DiskStream> y) { _diskstreams[x] = y; }

    /// Add a SharedObject
    void addSOL(std::shared_ptr<cygnal::Element> x) {
	_sol.push_back(x);
    };

    /// \method addClient
    ///     Add a client to the list for output messages for a
    ///     resource. This also specifies the protocol handler
    ///     required for data on this file descriptor.
    size_t addClient(int fd, gnash::Network::protocols_supported_e proto);
    /// \method removeClient
    ///     Remove a client from the list for messages.
    void removeClient(int fd);
    /// \var getClients
    ///     Get the vector of file descriptors for this handler.
    std::vector<int> &getClients() { return _clients; };
    /// \var getClient
    ///     Get a client from the list of clients, we have too many
    ///     arrays so using an operator isn't useful.
    int getClient(int x) { return _clients[x]; };

    /// \brief Receive a message from the other end of the network connection.
    ///
    /// @param fd The file descriptor to read from
    ///
    /// @return The number of bytes sent
    int recvMsg(int fd);
    
    gnash::Network::protocols_supported_e getProtocol(int x) { return _protocol[x]; };
    void setProtocol(int fd, gnash::Network::protocols_supported_e x) { _protocol[fd] = x; };

    /// \method addRemote
    ///     Add a remote machine to the list for input messages.
    size_t addRemote(int x) { _remote.push_back(x); return _remote.size(); };

    void setPlugin(std::shared_ptr<Handler::cygnal_init_t> &init);
    void setPlugin(Handler::cygnal_io_read_t read_ptr, Handler::cygnal_io_write_t write_ptr );

    /// Initialize the named module within Cygnal
    //
    std::shared_ptr<cygnal_init_t> initModule(const std::string& module);

    /// \method initialized
    ///     See if any of the cgi-bins has been loaded.
    bool initialized();

    /// This method reads raw data from a plugin.
    std::shared_ptr<cygnal::Buffer> readFromPlugin();

    /// This method writes raw data to a plugin.
    size_t writeToPlugin(cygnal::Buffer &buf) {
	return writeToPlugin(buf.begin(), buf.allocated()); };
    size_t writeToPlugin(std::uint8_t *data, size_t size);

    // These methods handle control of the file streaming, and are
    // used by both HTTP and RTMP*

    /// \fn     int createStream()
    double createStream(double transid);
    /// \overload int createStream(const std::string &filespec)
    /// @param filespec The spec of the file to stream
    double createStream(double transid, const std::string &filespec);

    /// \fn playStream
    ///    Play the specified file as a stream
    bool playStream();
    /// \overload int playStream(const std::string &filespec)
    bool playStream(const std::string &filespec);

    // Publish a live RTMP stream
    int publishStream();
    int publishStream(const std::string &filespec, pub_stream_e op);

    // Seek within the RTMP stream
    int seekStream();
    int seekStream(int offset);

    // Pause the RTMP stream
    int pauseStream(double transid);

    // Find a stream in the vector or Disk Streams
    std::shared_ptr<gnash::DiskStream> findStream(const std::string &filespec);

    // Pause the RTMP stream
    int togglePause(double);

    // Resume the paused RTMP stream
    double resumeStream(double transid);

    // Close the RTMP stream
    double closeStream(double transid);

    // Delete the RTMP stream
    double deleteStream(double transid);

    // This is a site specific identifier of some kind.
    void setFCSubscribe(const std::string &x) { _fcsubscribe = x; };
    std::string &getFCSubscribe() { return _fcsubscribe; }

#if 1
    // FIXME: This holds the data from the first NetConnection packet,
    // and shouldn't really be done here, but we're trying not to
    // break things while refactoring.
    void setNetConnection(gnash::RTMPMsg *msg) { _netconnect.reset(msg); };
    void setNetConnection(std::shared_ptr<gnash::RTMPMsg> msg) { _netconnect = msg; };
    std::shared_ptr<gnash::RTMPMsg> getNetConnection() { return _netconnect;};
#endif
    
#if 1
    std::shared_ptr<HTTPServer> &getHTTPHandler(int fd)  { return _http[fd]; };
    std::shared_ptr<RTMPServer> getRTMPHandler(int fd)  { return _rtmp[fd]; };
#endif
    
    // Parse the first nessages when starting a new message handler,
    // which is used to determine the name of the resource to
    // initialize, or load from the cache.
    cygnal::Buffer *parseFirstRequest(int fd, gnash::Network::protocols_supported_e proto);
    
    std::string &getKey(int x) { return _keys[x]; };
    void setKey(int fd, std::string x) { _keys[fd] = x; };
    
    // Dump internal data.
    void dump();
    
protected:
    /// \var _name
    ///	    The name of the path this handler is supporting.
    std::string	_name; 
    ///	    Each incoming request has one of 4 states the server has
    ///     to handle to send a response.
    /// \var _streams
    ///    This is a counter of how many streams have been allocated
    ///    by the server.
    int		_streams;
    /// \var _diskstreams
    ///   This is all the opened disk based files that are currently
    ///   being streamed by the server.
    std::map<int, std::shared_ptr<gnash::DiskStream> > _diskstreams;
    /// \var _protocol
    ///    this is the map of which protocol is being used by which
    ///    file descriptor.
    std::map<int, gnash::Network::protocols_supported_e> _protocol;
#if 1
    std::map<int, std::shared_ptr<HTTPServer> > _http;
    std::map<int, std::shared_ptr<RTMPServer> > _rtmp;
#endif
    /// \var _clients
    ///	    is the array of all clients connected to this server for
    ///     this application. This is where all the output goes.
    std::vector<int> _clients;
    /// \var _remote
    ///	    This is network connections to other processes,
    ///	    on other computers.
    std::vector<int> _remote;

    /// \var _local
    ///    These are local process we're responsible for
    ///    starting and stopping.
    std::shared_ptr<cygnal::Proc>	_local;
    /// \var _plugins
    ///	    is for the dynamically loaded applications
    std::shared_ptr<cygnal_init_t>	_plugin;
    /// \var _file
    ///	    is for disk based files
    std::vector<std::shared_ptr<gnash::DiskStream> > _files;
    /// \var _sol
    ///	    is for remote SharedObjects
    std::vector<std::shared_ptr<cygnal::Element> > _sol;
    ///var _bodysize;
    ///     is to store the body size of the previous packet for this
    ///     channel. 4 and 1 byte heades don't use the length field,
    ///     they just use the previous vaue for this field.
    std::map<int, size_t>		_bodysize;
    /// \var _in_fd
    ///	    The file descriptor of the incoming data for an
    ///     Invoke message.
    int					_in_fd;

    /// \var _fcssubscribe
    ///    This is a string sometimes sent by the client with what
    ///    appears to be a unique ID number.
    std::string				_fcsubscribe;

#if 1
    /// \var _netconnect
    ///    This store the data from the NetConnection ActionScript
    ///    object we get as the final part of the handshake process
    ///    that is used to set up the connection. This has all the
    ///    file paths and other information needed by the server.
    std::shared_ptr<gnash::RTMPMsg>	_netconnect;
#endif

    std::map<int, std::string> _keys;
private:    
    std::mutex			_mutex;
    
// Remote Shared Objects. References are an index into this vector.
//    std::map<std::string, std::shared_ptr<handler_t> > _handlers;
};

} // end of gnash namespace

#endif // end of __HANDLER_H__

// local Variables:
// mode: C++
// indent-tabs-mode: t
// End:
