/****************************************************************************
 *
 *  Copyright (C) 2000-2001 RealNetworks, Inc. All rights reserved.
 *
 *  This program is free software.  It may be distributed under the terms
 *  in the file LICENSE, found in the top level of the source distribution.
 *
 */

#include "types.h"
#include "dbg.h"

#include "proxytran.h"
#include "proxypkt.h"

// The UDP port range that we will use
#define MIN_UDP_PORT  6970
#define MAX_UDP_PORT 32000

CProxyTransport::CProxyTransport( void ) :
    CTransport( ),
    m_nPorts( 0 ),
    m_portBase( 0 ),
    m_protData( this ),
    m_protCtrl( this ),
    m_pSibling( NULL )
{
    // Empty
}

CProxyTransport::~CProxyTransport( void )
{
    // Empty
}

/*
 * Look through the transport spec (supplied by the client) and bind to the
 * appropriate local port(s), if any.
 */
bool CProxyTransport::Init( int nPorts )
{
    assert( nPorts <= 2 );

    if( ! nPorts ) return true;

    bool bRet = false;
    static UINT16 port = MIN_UDP_PORT;
    UINT16 wrap = port;
    do
    {
        if( m_protData.Init( port ) )
        {
            if( nPorts == 1 )
            {
                bRet = true;
                break;
            }

            if( m_protCtrl.Init( port+1 ) )
            {
                bRet = true;
                break;
            }
            else
            {
                m_protData.Close( );
            }
        }
        port += 2;
        if( port >= MAX_UDP_PORT ) port = MIN_UDP_PORT;
    }
    while( port != wrap );

    if( bRet )
    {
        m_nPorts = nPorts;
        m_portBase = port;
    }

    return bRet;
}

bool CProxyTransport::Init( int nChannels, UINT16 chan, CRtspProtocol* pProt )
{
    assert( nChannels <= 2 );

    if( ! nChannels ) return true;
    
    if( !m_protData.Init( chan, pProt ) )
    {
        return false;
    }

    if( nChannels == 2 )
    {
        if( !m_protCtrl.Init( chan + 1, pProt ) )
        {
            return false;
        }
    }

    m_nPorts = nChannels;
    m_portBase = chan;
    return true;
}

void CProxyTransport::Close( void )
{
    m_protData.Close( );

    if(m_nPorts == 2)
    {
        m_protCtrl.Close( );
    }
}

void CProxyTransport::SetPeer( const CString& strHost, UINT16 port )
{
    m_protData.SetPeer( strHost, port );
    if( m_nPorts > 1 ) m_protCtrl.SetPeer( strHost, port+1 );
}

void CProxyTransport::SetPeer( const CInetAddr& host, UINT16 port )
{
    m_protData.SetPeer( host, port );
    if( m_nPorts > 1 ) m_protCtrl.SetPeer( host, port+1 );
}

UINT16 CProxyTransport::GetBasePort( void )
{
    return m_portBase;
}

void CProxyTransport::SendPacket( UINT chan, CPacket* ppkt )
{
    //assert( chan >= m_portBase && chan - m_portBase < m_nPorts );
    assert( ppkt );

    if( chan == 0 )
    {
        m_protData.SendPacket( ppkt );
    }
    else
    {
        m_protCtrl.SendPacket( ppkt );
    }
}

void CProxyTransport::SetSibling(CProxyTransport * pSibling)
{
    m_pSibling = pSibling;
}

void CProxyTransport::OnPacket( UINT chan, CBuffer* pbuf )
{
    CProxyPacket pkt;
    pkt.Set( pbuf );
    m_pSibling->SendPacket( chan - m_portBase, &pkt );
}

/**************************************
 *
 * Passthru protocol class
 *
 **************************************/

CProxyTransport::CPassthruProtocol::CPassthruProtocol( CProxyTransport* pOwner ) :
    m_pOwner( pOwner ),
    m_pSock( NULL ),
    m_chan( 0 )
{
    // Empty
}

CProxyTransport::CPassthruProtocol::~CPassthruProtocol( void )
{
    Close( );
}

bool CProxyTransport::CPassthruProtocol::Init( UINT16 port )
{
    m_pSock = new CUdpSocket( this );
    m_chan = port;

    CSockAddr addr( CInetAddr::Any(), port );
    if( ( ( CUdpSocket* )m_pSock )->Bind( addr ) )
    {
        return true;
    }
    
    delete m_pSock;
    m_pSock = NULL;
    return false;
}

bool CProxyTransport::CPassthruProtocol::Init( UINT16 chan, CRtspProtocol * pProt)
{
    m_pSock = new CRtspInterleavedSocket( this );
    m_chan = chan;
    return ( ( CRtspInterleavedSocket* )m_pSock )->Connect( chan, pProt );
}

void CProxyTransport::CPassthruProtocol::Close( )
{
    if( m_pSock )
    {
        m_pSock->Close( );
        delete m_pSock;
        m_pSock = NULL;
    }
}

void CProxyTransport::CPassthruProtocol::SetPeer( const CString& strHost, UINT16 port )
{
    ( ( CUdpSocket* )m_pSock )->Connect( CSockAddr( strHost, port ) );
    m_pSock->Select( SF_READ );
}

void CProxyTransport::CPassthruProtocol::SetPeer( const CInetAddr& host, UINT16 port )
{
    ( ( CUdpSocket* )m_pSock )->Connect( CSockAddr( host, port ) );
    m_pSock->Select( SF_READ );
}

void CProxyTransport::CPassthruProtocol::SendPacket( CPacket* ppkt )
{
    CProxyPacket* pproxypkt;
#ifdef NO_RTTI
    pproxypkt = (CProxyPacket*)ppkt; //XXX: very bad, upgrade compiler
#else
    pproxypkt = dynamic_cast<CProxyPacket*>( ppkt );
#endif
    assert_or_ret( pproxypkt );

    CBuffer* pbuf = pproxypkt->Get( );
    assert( pbuf );
    m_pSock->Write( pbuf->GetBuffer( ), pbuf->GetSize( ) );
}

void CProxyTransport::CPassthruProtocol::OnConnectDone( int err )
{
    assert( false );
}

void CProxyTransport::CPassthruProtocol::OnReadReady( void )
{
    CBuffer buf;
    buf.SetSize( MAX_UDP_LEN+1 );
    size_t len = 0;

    len = m_pSock->Read( buf.GetBuffer( ), MAX_UDP_LEN+1 );
    if( len )
    {
        if( len <= MAX_UDP_LEN + 1 )
        {
            buf.SetSize( len );
            m_pOwner->OnPacket( m_chan, &buf );
        }
        else
        {
            dbgout( "CProxyTransport::CPassthruProtocol::OnReadReady: UDP packet too large" );
        }
    }
}

void CProxyTransport::CPassthruProtocol::OnWriteReady( void )
{
    assert( false );
}

void CProxyTransport::CPassthruProtocol::OnExceptReady( void )
{
    assert( false );
}

void CProxyTransport::CPassthruProtocol::OnClosed( void )
{
    //dbgout( "CProxyTransport::CPassthruProtocol::OnClosed: socket closed" );
}


syntax highlighted by Code2HTML, v. 0.9.1