/*****************************************************************
|
|   Platinum - Device Host
|
| Copyright (c) 2004-2010, Plutinosoft, LLC.
| All rights reserved.
| http://www.plutinosoft.com
|
| 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 2
| of the License, or (at your option) any later version.
|
| OEMs, ISVs, VARs and other distributors that combine and 
| distribute commercially licensed software with Platinum software
| and do not wish to distribute the source code for the commercially
| licensed software under version 2, or (at your option) any later
| version, of the GNU General Public License (the "GPL") must enter
| into a commercial license agreement with Plutinosoft, LLC.
| licensing@plutinosoft.com
|  
| 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; see the file LICENSE.txt. If not, write to
| the Free Software Foundation, Inc., 
| 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
| http://www.gnu.org/licenses/gpl-2.0.html
|
****************************************************************/

/** @file
 UPnP Device
 */

#ifndef _PLT_DEVICE_HOST_H_
#define _PLT_DEVICE_HOST_H_

/*----------------------------------------------------------------------
|   includes
+---------------------------------------------------------------------*/
#include "Neptune.h"
#include "PltDeviceData.h"
#include "PltSsdp.h"
#include "PltTaskManager.h"
#include "PltAction.h"
#include "PltHttp.h"
#include "PltHttpServer.h"

/*----------------------------------------------------------------------
|   forward declarations
+---------------------------------------------------------------------*/
class PLT_SsdpDeviceAnnounceTask;
class PLT_SsdpListenTask;

/*----------------------------------------------------------------------
|   PLT_DeviceHost class
+---------------------------------------------------------------------*/
/**
 UPnP Device Host.
 The PLT_DeviceHost class is a base class for implementing a UPnP Device. It handles 
 network announcements and responses to searches from ControlPoints. ControlPoint
 action invocations are also received and delegated to derived classes. A 
 PLT_DeviceHost also takes care of eventing when services state variables change.
 */
class PLT_DeviceHost : public PLT_DeviceData,
                       public PLT_SsdpPacketListener,
                       public NPT_HttpRequestHandler
{
public:
    /**
     Creates a new instance of UPnP Device Host.
     @param description_path Relative path for description url
     @param uuid UPnP device unique id
     @param device_type UPnP device type
     @param friendly_name Name advertised for UPnP device
     @param show_ip Flag to indicate if device IP should be appended to friendly name
     @param port local port for the device host internal HTTP server, 0 for randomly
     selected.
     @param port_rebind Flag to indicate if device host should automatically try to look 
     for another port if failing to choose the one passed.
     */
    PLT_DeviceHost(const char*  description_path = "/",
                   const char*  uuid = "",
                   const char*  device_type = "",
                   const char*  friendly_name = "",
                   bool         show_ip = false,
                   NPT_UInt16   port = 0,
                   bool         port_rebind = false);
    ~PLT_DeviceHost() override;
    
    virtual void SetExtraBroadcast(bool broadcast) { m_ExtraBroascast = broadcast; }
     
    /**
     When a UPnP device comes up, the specifications require that a SSDP bye-bye
     sequence is sent to force the removal of the device in case it wasn't sent
     properly during the last shutdown.
     @param bye_bye_first Boolean to indicate that SSDP bye-bye sequence should 
     be sent first or not.
     */
    virtual void SetByeByeFirst(bool bye_bye_first) { m_ByeByeFirst = bye_bye_first; }
    
    /**
     Returns the port used by the internal HTTP server for all incoming requests.
     @return port
     */
    virtual NPT_UInt16 GetPort() { return m_Port; };
    
    /**
     Sets the lease time.
     @param lease_time Lease Time
     */
    NPT_Result SetLeaseTime(NPT_TimeInterval lease_time) { return PLT_DeviceData::SetLeaseTime(lease_time); }

protected:
    /**
     NPT_HttpRequestHandler method for setting up the response of an incoming
     HTTP request.
     @param request the request received
     @param context the context of the request
     @param response the response to set up
     */
    NPT_Result SetupResponse(NPT_HttpRequest&              request,
                                     const NPT_HttpRequestContext& context,
                                     NPT_HttpResponse&             response) override;

    /**
     Static method similar to Announce.
     @param device the device to announce
     @param request the SSDP pre formatted request
     @param socket the network socket to use to send the request
     @param type PLT_SsdpAnnounceType enum if the announce is a SSDP bye-bye, update or alive.
     */
    static NPT_Result Announce(PLT_DeviceData*      device,
                               NPT_HttpRequest&     request,
                               NPT_UdpSocket&       socket,
                               PLT_SsdpAnnounceType type);
    /**
     Called during SSDP announce. The HTTP request is already configured with
     the right method and host.
     @param request the SSDP pre formatted request
     @param socket the network socket to use to send the request
     @param type PLT_SsdpAnnounceType enum if the announce is a SSDP bye-bye, update or alive.
     */
    NPT_Result Announce(NPT_HttpRequest&     request,
                        NPT_UdpSocket&       socket,
                        PLT_SsdpAnnounceType type) {
        return Announce(this, request, socket, type);
    }

    /**
     PLT_SsdpPacketListener method called when a M-SEARCH SSDP packet is received.
     @param request SSDP packet
     @param context the context of the request
     */
    NPT_Result OnSsdpPacket(const NPT_HttpRequest&        request, 
                                    const NPT_HttpRequestContext& context) override;

    /**
     Static method similar to SendSsdpSearchResponse.
     @param device the device to announce
     @param response the SSDP pre formatted response
     @param socket the network socket to use to send the request
     @param st the original request search target
     @param addr the remote address to send the response back to in case the socket
     is not already connected.
     */
    static NPT_Result SendSsdpSearchResponse(PLT_DeviceData*          device, 
                                             NPT_HttpResponse&        response, 
                                             NPT_UdpSocket&           socket, 
                                             const char*              st,
                                             const NPT_SocketAddress* addr = NULL);
    /**
     Called by PLT_SsdpDeviceSearchResponseTask when responding to a M-SEARCH
     SSDP request.
     @param response the SSDP pre formatted response
     @param socket the network socket to use to send the request
     @param st the original request search target
     @param addr the remote address to send the response back to in case the socket
     is not already connected.
     */
    virtual NPT_Result SendSsdpSearchResponse(NPT_HttpResponse&        response, 
                                              NPT_UdpSocket&           socket, 
                                              const char*              st,
                                              const NPT_SocketAddress* addr = NULL) {
        return SendSsdpSearchResponse(this, response, socket, st, addr);
    }
    
public:
    /**
     Add UPnP icon information to serve from file system.
     @param icon the icon information including url path
     @param fileroot the file system root path 
     @param urlroot the url root path of the icon url to match to fileroot
     Note: As an exemple, if the icon url path is "/images/icon1.jpg", the fileroot
     is "/Users/joe/www" and the urlroot is "/", when a request is made for
     "/images/icon1.jpg", the file is expected to be found at 
     "/Users/joe/www/images/icon1.jpg". If the urlroot were "/images", the file 
     would be expected to be found at "/Users/joe/www/icon1.jpg".
     */
    virtual NPT_Result AddIcon(const PLT_DeviceIcon& icon, 
                               const char*           fileroot,
                               const char*           urlroot = "/");
    
    /**
     Add UPnP icon information to serve using static image.
     @param icon the icon information including url path
     @param data the image data
     @param size the image data size
     @param copy boolean to indicate the data should be copied internally
     */
    virtual NPT_Result AddIcon(const PLT_DeviceIcon& icon, 
                               const void*           data, 
                               NPT_Size              size, 
                               bool                  copy = true);

protected:
    /**
     Required method for setting up UPnP services of device host 
     (and any embedded). Called when device starts.
     */
    virtual NPT_Result SetupServices() = 0;
    
    /**
     Default implementation for registering device icon resources. Override to 
     use different ones. Called when device starts.
     */
    virtual NPT_Result SetupIcons();
    
    /** 
     Default implementation for setting up device host. This calls SetupServices
     and SetupIcons when device starts.
     */
    virtual NPT_Result SetupDevice();
    
    /**
     Called by PLT_TaskManager when the device is started.
     @param task the SSDP listening task to attach to for receiving 
     SSDP M-SEARCH messages.
     */
    virtual NPT_Result Start(PLT_SsdpListenTask* task);
    
    /**
     Called by PLT_TaskManager when the device is stoped.
     @param task the SSDP listening task to detach from to stop receiving 
     SSDP M-SEARCH messages.
     */
    virtual NPT_Result Stop(PLT_SsdpListenTask* task);
    
    /**
     This mehod is called when an action performed by a control point has been 
     received and needs to be answered.
     @param action the action information to answer
     @param context the context information including the HTTP request and
     local and remote socket information (IP & port).
     */
    virtual NPT_Result OnAction(PLT_ActionReference&          action, 
                                const PLT_HttpRequestContext& context);
    
    /**
     This method is called when a control point is requesting the device
     description.
     @param request the HTTP request
     @param context the context information including local and remote socket information.
     @param response the response to setup.
     */
    virtual NPT_Result ProcessGetDescription(NPT_HttpRequest&              request,
                                             const NPT_HttpRequestContext& context,
                                             NPT_HttpResponse&             response);
    
    /**
     This method is called when a control point is requesting a service SCPD.
     @param service the service
     @param request the HTTP request
     @param context the context information including local and remote socket information.
     @param response the response to setup.
     */
    virtual NPT_Result ProcessGetSCPD(PLT_Service*                  service,
                                      NPT_HttpRequest&              request,
                                      const NPT_HttpRequestContext& context,
                                      NPT_HttpResponse&             response);
    
    /**
     This method is called when a "GET" request for a resource other than the device
     description, SCPD, or icons has been received.
     @param request the HTTP request
     @param context the context information including local and remote socket information.
     @param response the response to setup.
     */
    virtual NPT_Result ProcessHttpGetRequest(NPT_HttpRequest&              request,
                                             const NPT_HttpRequestContext& context,
                                             NPT_HttpResponse&             response);
    
    /**
     This method is called when a "POST" request has been received. This is usually
     an UPnP service action invocation. This will deserialize the request and call
     the OnAction method.
     @param request the HTTP request
     @param context the context information including local and remote socket information.
     @param response the response to setup.
     */
    virtual NPT_Result ProcessHttpPostRequest(NPT_HttpRequest&              request,
                                              const NPT_HttpRequestContext& context,
                                              NPT_HttpResponse&             response);
    
    /**
     This method is called when a request from a subscriber has been received. This is
     for any new subscritions, existing subscrition renewal or cancellation.
     @param request the HTTP request
     @param context the context information including local and remote socket information.
     @param response the response to setup.
     */
    virtual NPT_Result ProcessHttpSubscriberRequest(NPT_HttpRequest&              request,
                                                    const NPT_HttpRequestContext& context,
                                                    NPT_HttpResponse&             response);

protected:
    friend class PLT_UPnP;
    friend class PLT_UPnP_DeviceStartIterator;
    friend class PLT_UPnP_DeviceStopIterator;
    friend class PLT_Service;
    friend class NPT_Reference<PLT_DeviceHost>;
    friend class PLT_SsdpDeviceSearchResponseInterfaceIterator;
    friend class PLT_SsdpDeviceSearchResponseTask;
    friend class PLT_SsdpAnnounceInterfaceIterator;

    PLT_TaskManagerReference m_TaskManager;
    PLT_HttpServerReference  m_HttpServer;
    bool                     m_ExtraBroascast;
    NPT_UInt16               m_Port;
    bool                     m_PortRebind;
    bool                     m_ByeByeFirst;
    bool                     m_Started;
};

typedef NPT_Reference<PLT_DeviceHost> PLT_DeviceHostReference;

#endif /* _PLT_DEVICE_HOST_H_ */
