/**
 * @file
 * $Id$
 * $Revision$
 * $Author$
 * $Date$
 *
 * This file is part of The iWear Framework.
 *
 * The iWear Framework 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 as in version 2 of the License.

 * 
 * The iWear Framework 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
 * The iWear Framework; if not, write to the Free Software Foundation, Inc., 59
 * Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifndef __IWREMOTE_SOCKETREMOTECONNECTION_H
#define __IWREMOTE_SOCKETREMOTECONNECTION_H

#include <iwremote/remoteconnection.h>
#include <net/ipconnection.h>
#include <iwremote/xdrstream.h>
#include <boost/intrusive_ptr.hpp>

namespace iwear
{
    namespace net
    {
// to be able to do the following typedef (it should be in the namespace but
// usable in the class too)
class SocketRemoteConnection;
class SocketConnectionDescriptor;

typedef boost::intrusive_ptr<SocketRemoteConnection> SocketRemoteConnectionPtr;

/**
 * This is a class that can wrap around different connection types to form a
 * simple uniform interface to all possible interfaces.
 */
class SocketRemoteConnection : public RemoteConnection
{
friend class SocketProvider;
private:
protected:
    IPConnection* scon;
    /**
     * The fd that was entered into the socket set (in case the connection has
     * disabled itself)
     */
    int sockset_fd;

    string ip;

    uint16_t port;

//    char* buffer;
    /**
     */
    float send_timeout;

    /**
     */
    float recv_timeout;

    float connect_timeout;


    /// This information must be setable, and probably must be propagated to
    /// the SocketProvider too, since the need of sequencing and the need of ssl
    /// should be known there too. (this means only the non-packeted version of
    /// ssl, the packeted version of ssl is done via SSLWrapper encapsulation)
    bool is_udp;
    
    bool need_ssl;

    bool ssl_only;

    SocketRemoteConnection( IPConnection*, CallProvider* cp, bool ms, float r_to,float s_to,float c_to);

    /**
     * Dont allow this class to be copied.
     */
    SocketRemoteConnection( const SocketRemoteConnection& x ) : RemoteConnection(x) { }

    SocketRemoteConnection& operator=( const SocketRemoteConnection& ) { return *this; }

public:

    /**
     * Only allow to create via intrusive pointers.
     */
    static boost::intrusive_ptr<SocketRemoteConnection> 
	create( IPConnection*, CallProvider* cp, bool ms, float r_to,float s_to,float c_to);

    virtual XDRStream* get_stream( uint32_t est );

    virtual void recycle_stream( RPCStream* );

    IPConnection& get_connection( void ) { return *scon; }
    
    virtual ~SocketRemoteConnection();

    virtual rpc_type get_type( void ) const { return rpc_remote; }

    /**
     * This function extracts the buffer from the given rpcstream and sends it
     * over the connection. The stream must be suitable for the given
     * connection type, the responsibility for this lies at the caller, we will
     * here send everything over the connection.
     */
    virtual void send_rpcpacket( const RPCStream* spc );

    /**
     * This will try to receive a complete rpcstream packet, and set the buffer
     * of the complete recived packet into the passed rpcstream
     */
    virtual bool receive_rpcpacket( RPCStream& spc, uint16_t channel )
    {
	return receive_rpcpacket( spc, channel, true );
    }
	
    virtual bool receive_rpcpacket( RPCStream& spc, uint16_t channel, bool checkfd );

    virtual bool receive_rpcpacket( RPCStream& spc, uint32_t deferid);

    virtual bool has_ipaddress( void ) const;// { return ip.length() != 0; }
    virtual string get_ipaddress( void ) const;// { return ip; }

    virtual bool has_sslkey( void ) const;
    virtual bool get_sslkey( void ) const;

    virtual bool is_connected( void ) const;
    virtual bool reconnect( void );
    virtual void disconnect( void );

    virtual void set_connection_policy( void );

    virtual ConnectionDescriptor* get_connectiondescriptor( );

friend void intrusive_ptr_add_ref( SocketRemoteConnection* rc );
friend void intrusive_ptr_release( SocketRemoteConnection* rc );
    
    virtual HostConnector get_connector( void );

};

inline void intrusive_ptr_release( SocketRemoteConnection* rc )
{
    /*
    d_dbg << ANSI_VIOLET << "intrusive_ptr_release( SocketRemoteConnection* 0x" << hex << (void*)rc << dec << ")" << endl;
    d_dbg << "Old: " << rc->get_refcount() << endl;
    d_dbg << "New: " << rc->get_refcount()-1 << ANSI_NORMAL << endl;
    */

    if( ! rc ) return;
    SocketRemoteConnection* rcd = 0;
    {
	ThreadLocker tl(rc->mutex);
	--(rc->get_refcount());
	if( rc->get_refcount() == 0 )
	{
	    rcd = rc;
	}
    }
    delete rcd;
}

inline void intrusive_ptr_add_ref( SocketRemoteConnection* rc )
{
    /*
    d_dbg << ANSI_VIOLET << "intrusive_ptr_add_ref( SocketRemoteConnection* 0x" << hex << (void*)rc << dec << endl;
    d_dbg << "Old: " << rc->get_refcount() << endl;
    d_dbg << "New: " << rc->get_refcount()+1 << ANSI_NORMAL << endl;
    */

    ThreadLocker tl(rc->mutex);
    ++(rc->get_refcount());
    // Do we need to do more ?
}

}
}

#endif
