// -*- C++ -*-

//=============================================================================
/**
 *  @file    HTBP_Session.h
 *
 *  @author Phil Mesnier
 */
//=============================================================================

#ifndef ACE_HTBP_SESSION_H
#define ACE_HTBP_SESSION_H
#include /**/ "ace/pre.h"

#include "ace/SOCK_IO.h"
#include "ace/Hash_Map_Manager.h"
#include "ace/Synch.h"
#include "ace/Message_Queue.h"

#if !defined (ACE_LACKS_PRAGMA_ONCE)
# pragma once
#endif /* ACE_LACKS_PRAGMA_ONCE */

#include "HTBP_Addr.h"
#include "HTBP_Export.h"
#include "HTBP_Channel.h"

#include "HTBP_Stream.h"

ACE_BEGIN_VERSIONED_NAMESPACE_DECL

// Forward declarations.
class ACE_HTBP_Filter;
class ACE_Event_Handler;

namespace ACE
{
  namespace HTBP
  {

    class Session_Id_t
    {
    public:
      ACE_UINT32 id_;
      Addr    local_;
      Addr    peer_;

      u_long hash () const;
      bool operator ==(const Session_Id_t &other) const;
    };

    /**
     * @class Session
     *
     * @brief Defines the methods in the <Session> abstraction.
     *
     * A session is an entity that combines two Ht_Channels that connect directly
     * to a proxy to manage communication with a remote peer. The Session may
     * persist longer than either stream, assuming that the proxy is libel to
     * close a connection at any time.
     *
     * This means that the session needs to be able to reconnect to the remote
     * peer. This also means that the session needs to be aware of its location
     * If it is outside the proxy and looses a stream, oh well. If it is inside,
     * then the next time a stream is required, then it must reconnect before
     * returning the stream.
     *
     * The session does not queue outbound messages. That is going to be the
     * responsibility of the application, or a higher level protocol wrapper.
     */
    class HTBP_Export Session
    {
    public:
      // Initialization and termination methods.
      /// Constructor.
      Session (void);

      /// Constructor (sets the underlying session id with <sid>).
      Session (const Addr& peer,
               const Addr& local,
               ACE_UINT32 sid = 0,
               ACE_INET_Addr *proxy = 0,
               bool take_proxy = false);
      Session (const Session_Id_t &id,
               ACE_INET_Addr *proxy = 0,
               bool take_proxy = false);

      Session (const Session &other);
      Session& operator= (const Session &other);

      /// Destructor.
      ~Session (void);

      /// The following methods are specific to the Session
      static ACE_UINT32 next_session_id ();

      static int add_session (Session *);
      static int remove_session (Session *);
      static int find_session (const Session_Id_t&,
                               Session *&out);

      Stream *stream (void) const;
      void stream (Stream *);

      int enqueue (ACE_Message_Block *msg);
      int flush_outbound_queue (void);

      int close_inbound (void) const;
      int close_outbound (void) const;

      /// get references to the actual streams based on the direction
      /// of data flow if this session is on the inside of the proxy
      /// ie, has a non-null proxy addr, then the inbound stream is
      /// the out_to_in stream, otherwise it is the in_to_out
      /// stream. The outbound is the opposite of the inbound.
      /// Whenever an application wishes to send data, whether that is
      /// request or reply data, it uses the outbound stream, and it
      /// should associate an event handler with the inbound stream
      /// for receiving data.
      Channel *inbound (void) const;
      Channel *outbound (void) const;
      void inbound (Channel *);
      void outbound (Channel *);

      int enable (int value);
      int disable (int value);

      const Session_Id_t& session_id(void) const;
      void session_id (ACE_UINT32 );

      const ACE_INET_Addr *proxy_addr (void) const;
      void proxy_addr (ACE_INET_Addr *, int destroy = 0);

      const Addr &peer_addr (void) const;
      const Addr &local_addr (void) const;

      void peer_addr (const Addr &);
      void local_addr (const Addr &);

      /// invoke close on both streams, then remove self from session map
      int close (void);

      ACE_Event_Handler *handler (void);
      void handler (ACE_Event_Handler *);
      void reactor (ACE_Reactor *);
      void detach (Channel *);

      int sock_flags(void) const;

    private:
      /// Connected Stream ensures that the particular stream is
      /// connected to the proxy, if possible. The result is same as
      /// the reference passed in, so that it may be used inline for
      /// the inboundor outbound methods

      void reconnect () const;
      void reconnect_i (Channel *) const;

      typedef ACE_Hash_Map_Manager<Session_Id_t, Session*,
                                   ACE_SYNCH_MUTEX> Session_Map;
      typedef ACE_Hash_Map_Entry <Session_Id_t, Session*> Map_Entry;
      static Session_Map session_map_;
      static ACE_UINT32 last_session_id_;
      static ACE_SYNCH_MUTEX session_id_lock_;

      ACE_INET_Addr *proxy_addr_;
      int destroy_proxy_addr_;

      Session_Id_t session_id_;

      Channel *inbound_;
      Channel *outbound_;

      bool closed_;

      ACE_Event_Handler *handler_;
      ACE_Reactor *reactor_;

      ACE_Message_Queue<ACE_SYNCH> outbound_queue_;
      Stream * stream_;
      int sock_flags_;
    };
  }
}

ACE_END_VERSIONED_NAMESPACE_DECL

#if defined (__ACE_INLINE__)
#include "HTBP_Session.inl"
#endif

#include /**/ "ace/post.h"
#endif /* ACE_HTBP_SESSION_H */
