// Copyright (C) 2011  Davis E. King (davis@dlib.net)
// License: Boost Software License   See LICENSE.txt for the full license.
#undef DLIB_BRIDGe_ABSTRACT_
#ifdef DLIB_BRIDGe_ABSTRACT_

#include <string>
#include "../pipe/pipe_kernel_abstract.h"

namespace dlib
{

// ---------------------------------------------------------------------------------------- 

    struct connect_to_ip_and_port
    {
        connect_to_ip_and_port (
            const std::string& ip,
            unsigned short port
        );
        /*!
            requires
                - is_ip_address(ip) == true
                - port != 0
            ensures
                - this object will represent a request to make a TCP connection
                  to the given IP address and port number.
        !*/
    };

    connect_to_ip_and_port connect_to (
        const network_address& addr
    );
    /*!
        requires
            - addr.port != 0
        ensures
            - converts the given network_address object into a connect_to_ip_and_port
              object.
    !*/

    struct listen_on_port
    {
        listen_on_port(
            unsigned short port
        );
        /*!
            requires
                - port != 0
            ensures
                - this object will represent a request to listen on the given
                  port number for incoming TCP connections.
        !*/
    };

    template <
        typename pipe_type
        >
    bridge_transmit_decoration<pipe_type> transmit ( 
        pipe_type& p
    ); 
    /*!
        requires
            - pipe_type is some kind of dlib::pipe object
            - the objects in the pipe must be serializable
        ensures
            - Adds a type decoration to the given pipe, marking it as a transmit pipe, and 
              then returns it.  
    !*/

    template <
        typename pipe_type
        >
    bridge_receive_decoration<pipe_type> receive ( 
        pipe_type& p
    );
    /*!
        requires
            - pipe_type is some kind of dlib::pipe object
            - the objects in the pipe must be serializable
        ensures
            - Adds a type decoration to the given pipe, marking it as a receive pipe, and 
              then returns it.  
    !*/

// ----------------------------------------------------------------------------------------

    struct bridge_status
    {
        /*!
            WHAT THIS OBJECT REPRESENTS
                This simple struct represents the state of a bridge object.  A
                bridge is either connected or not.  If it is connected then it
                is connected to a foreign host with an IP address and port number
                as indicated by this object.
        !*/
        
        bridge_status(
        ); 
        /*!
            ensures
                - #is_connected == false
                - #foreign_port == 0
                - #foreign_ip == ""
        !*/

        bool is_connected;
        unsigned short foreign_port;
        std::string foreign_ip;
    };

// ---------------------------------------------------------------------------------------- 

    class bridge : noncopyable
    {
        /*!
            WHAT THIS OBJECT REPRESENTS
                This object is a tool for bridging a dlib::pipe object between
                two network connected applications.  


                Note also that this object contains a dlib::logger object
                which will log various events taking place inside a bridge.
                If you want to see these log messages then enable the logger
                named "dlib.bridge".

            
            BRIDGE PROTOCOL DETAILS
                The bridge object creates a single TCP connection between
                two applications.  Whenever it sends an object from a pipe
                over a TCP connection it sends a byte with the value 1 followed 
                immediately by the serialized copy of the object from the pipe. 
                The serialization is performed by calling the global serialize()
                function.  

                Additionally, a bridge object will periodically send bytes with
                a value of 0 to ensure the TCP connection remains alive.  These
                are just read and ignored.  
        !*/

    public:

        bridge (
        );
        /*!
            ensures
                - this object is properly initialized
                - #get_bridge_status().is_connected == false
        !*/

        template <typename T, typename U, typename V>
        bridge (
            T network_parameters,
            U pipe1,
            V pipe2 
        ); 
        /*!
            requires
                - T is of type connect_to_ip_and_port or listen_on_port
                - U and V are of type bridge_transmit_decoration or bridge_receive_decoration,
                  however, U and V must be of different types (i.e. one is a receive type and 
                  another a transmit type).
            ensures
                - this object is properly initialized
                - performs: reconfigure(network_parameters, pipe1, pipe2)
                  (i.e. using this constructor is identical to using the default constructor 
                  and then calling reconfigure())
        !*/

        template <typename T, typename U>
        bridge (
            T network_parameters,
            U pipe 
        ); 
        /*!
            requires
                - T is of type connect_to_ip_and_port or listen_on_port
                - U is of type bridge_transmit_decoration or bridge_receive_decoration.
            ensures
                - this object is properly initialized
                - performs: reconfigure(network_parameters, pipe)
                  (i.e. using this constructor is identical to using the default constructor 
                  and then calling reconfigure())
        !*/

        ~bridge (
        );
        /*!
            ensures
                - blocks until all resources associated with this object have been destroyed.
        !*/

        void clear (
        );
        /*!
            ensures
                - returns this object to its default constructed state.  That is, it will
                  be inactive, neither maintaining a connection nor attempting to acquire one.
                - Any active connections or listening sockets will be closed.
        !*/

        bridge_status get_bridge_status (
        ) const;
        /*!
            ensures
                - returns the current status of this bridge object. In particular, returns 
                  an object BS such that:
                    - BS.is_connected == true if and only if the bridge has an active TCP 
                      connection to another computer.
                    - if (BS.is_connected) then
                        - BS.foreign_ip == the IP address of the remote host we are connected to.
                        - BS.foreign_port == the port number on the remote host we are connected to.
                    - else if (the bridge has previously been connected to a remote host but hasn't been 
                               reconfigured or cleared since) then
                        - BS.foreign_ip == the IP address of the remote host we were connected to.
                        - BS.foreign_port == the port number on the remote host we were connected to.
                    - else
                        - BS.foreign_ip == ""
                        - BS.foreign_port == 0
        !*/



        template < typename T, typename R >
        void reconfigure (
            listen_on_port network_parameters,
            bridge_transmit_decoration<T> transmit_pipe,
            bridge_receive_decoration<R> receive_pipe
        ); 
        /*!
            ensures
                - This object will begin listening on the port specified by network_parameters
                  for incoming TCP connections.  Any previous bridge state is cleared out.
                - Onces a connection is established we will:
                    - Stop accepting new connections.
                    - Begin dequeuing objects from the transmit pipe and serializing them over 
                      the TCP connection.
                    - Begin deserializing objects from the TCP connection and enqueueing them 
                      onto the receive pipe.
                - if (the current TCP connection is lost) then 
                    - This object goes back to listening for a new connection.
                - if (the receive pipe can contain bridge_status objects) then
                    - Whenever the bridge's status changes the updated bridge_status will be
                      enqueued onto the receive pipe unless the change was a TCP disconnect 
                      resulting from a user calling reconfigure(), clear(), or destructing this 
                      bridge.  The status contents are defined by get_bridge_status().
            throws
                - socket_error
                  This exception is thrown if we are unable to open the listening socket.
        !*/
        template < typename T, typename R >
        void reconfigure (
            listen_on_port network_parameters,
            bridge_receive_decoration<R> receive_pipe,
            bridge_transmit_decoration<T> transmit_pipe
        ); 
        /*!
            ensures
                - performs reconfigure(network_parameters, transmit_pipe, receive_pipe)
        !*/
        template < typename T >
        void reconfigure (
            listen_on_port network_parameters,
            bridge_transmit_decoration<T> transmit_pipe
        );
        /*!
            ensures
                - This function is identical to the above two reconfigure() functions 
                  except that there is no receive pipe.
        !*/
        template < typename R >
        void reconfigure (
            listen_on_port network_parameters,
            bridge_receive_decoration<R> receive_pipe
        );
        /*!
            ensures
                - This function is identical to the above three reconfigure() functions 
                  except that there is no transmit pipe.
        !*/



        template <typename T, typename R>
        void reconfigure (
            connect_to_ip_and_port network_parameters,
            bridge_transmit_decoration<T> transmit_pipe,
            bridge_receive_decoration<R> receive_pipe
        ); 
        /*!
            ensures
                - This object will begin making TCP connection attempts to the IP address and port 
                  specified by network_parameters.  Any previous bridge state is cleared out.
                - Onces a connection is established we will:
                    - Stop attempting new connections.
                    - Begin dequeuing objects from the transmit pipe and serializing them over 
                      the TCP connection.
                    - Begin deserializing objects from the TCP connection and enqueueing them 
                      onto the receive pipe.
                - if (the current TCP connection is lost) then 
                    - This object goes back to attempting to make a TCP connection with the
                      IP address and port specified by network_parameters.
                - if (the receive pipe can contain bridge_status objects) then
                    - Whenever the bridge's status changes the updated bridge_status will be
                      enqueued onto the receive pipe unless the change was a TCP disconnect 
                      resulting from a user calling reconfigure(), clear(), or destructing this 
                      bridge.  The status contents are defined by get_bridge_status().
        !*/
        template <typename T, typename R>
        void reconfigure (
            connect_to_ip_and_port network_parameters,
            bridge_receive_decoration<R> receive_pipe,
            bridge_transmit_decoration<T> transmit_pipe
        ); 
        /*!
            ensures
                - performs reconfigure(network_parameters, transmit_pipe, receive_pipe)
        !*/
        template <typename T>
        void reconfigure (
            connect_to_ip_and_port network_parameters,
            bridge_transmit_decoration<T> transmit_pipe
        );
        /*!
            ensures
                - This function is identical to the above two reconfigure() functions 
                  except that there is no receive pipe.
        !*/
        template <typename R>
        void reconfigure (
            connect_to_ip_and_port network_parameters,
            bridge_receive_decoration<R> receive_pipe
        );
        /*!
            ensures
                - This function is identical to the above three reconfigure() functions 
                  except that there is no transmit pipe.
        !*/

    };

// ---------------------------------------------------------------------------------------- 

}

#endif // DLIB_BRIDGe_ABSTRACT_