/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: ppffhttp.cpp,v 1.1.1.1.38.1 2004/07/19 21:04:16 hubbe Exp $
 * 
 * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved.
 * 
 * The contents of this file, and the files included with this file,
 * are subject to the current version of the RealNetworks Public
 * Source License (the "RPSL") available at
 * http://www.helixcommunity.org/content/rpsl unless you have licensed
 * the file under the current version of the RealNetworks Community
 * Source License (the "RCSL") available at
 * http://www.helixcommunity.org/content/rcsl, in which case the RCSL
 * will apply. You may also obtain the license terms directly from
 * RealNetworks.  You may not use this file except in compliance with
 * the RPSL or, if you have a valid RCSL with RealNetworks applicable
 * to this file, the RCSL.  Please see the applicable RPSL or RCSL for
 * the rights, obligations and limitations governing use of the
 * contents of the file.
 * 
 * Alternatively, the contents of this file may be used under the
 * terms of the GNU General Public License Version 2 or later (the
 * "GPL") in which case the provisions of the GPL are applicable
 * instead of those above. If you wish to allow use of your version of
 * this file only under the terms of the GPL, and not to allow others
 * to use your version of this file under the terms of either the RPSL
 * or RCSL, indicate your decision by deleting the provisions above
 * and replace them with the notice and other provisions required by
 * the GPL. If you do not delete the provisions above, a recipient may
 * use your version of this file under the terms of any one of the
 * RPSL, the RCSL or the GPL.
 * 
 * This file is part of the Helix DNA Technology. RealNetworks is the
 * developer of the Original Code and owns the copyrights in the
 * portions it created.
 * 
 * This file, and the files included with this file, is distributed
 * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS
 * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET
 * ENJOYMENT OR NON-INFRINGEMENT.
 * 
 * Technology Compatibility Kit Test Suite(s) Location:
 *    http://www.helixcommunity.org/content/tck
 * 
 * Contributor(s):
 * 
 * ***** END LICENSE BLOCK ***** */

#include "hxtypes.h"
#include "hxresult.h"
#include "hxcom.h"

#include "hxstring.h"
#include "hxbuffer.h"
#include "hxengin.h"

#include "ppffhttp.h"

CScalableHTTP::CScalableHTTP()
    : m_pSocket(NULL)
    , m_pBufMsg(NULL)
    , m_pResponse(NULL)
    , m_bReleased(FALSE)
    , m_lRefCount(0)
    , m_ulConnTimeout(9000)
    , m_pScheduler(NULL)
    , m_pCallback(NULL)
{
}

CScalableHTTP::~CScalableHTTP()
{
    RemoveCallback();

    HX_RELEASE(m_pSocket);
    HX_RELEASE(m_pBufMsg);
    HX_RELEASE(m_pResponse);
    HX_RELEASE(m_pScheduler);
}

HX_RESULT
CScalableHTTP::Init(IUnknown* pContext, UINT32 ulTimeout)
{
    if (!pContext)
    {
	return HXR_FAIL;	
    }

    if (0 != ulTimeout)
    {
	m_ulConnTimeout = ulTimeout;
    }

    HX_RESULT theErr = HXR_OK;
    
    if (HXR_OK != pContext->QueryInterface(IID_IHXScheduler,
					    (void **)&m_pScheduler))
    {
	theErr = HXR_INVALID_PARAMETER;
	goto exit;
    }


    IHXNetworkServices* pNetworkServices;
    if (HXR_OK != pContext->QueryInterface(IID_IHXNetworkServices,
					    (void **)&pNetworkServices))
    {
	theErr = HXR_INVALID_PARAMETER;
	goto exit;
    }
    
    theErr = pNetworkServices->CreateTCPSocket(&m_pSocket);
    HX_RELEASE(pNetworkServices);

    if (theErr != HXR_OK)
    {
	goto exit;
    }

    
    if(!m_pResponse)
    {
	m_pResponse = new CScalableHTTP::CHTTPResponse();
	if (m_pResponse)
	{
	    m_pResponse->Init(this);
	    
	    // XXXkshoop This is illegal! (it is not an 
	    // interface pointer..)
	    m_pResponse->AddRef();
	}
	else
	{
	    theErr = HXR_OUTOFMEMORY;
	    goto exit;
	}
    }
    theErr = m_pSocket->Init(m_pResponse);
    
exit:    
    if (HXR_OK == theErr)
    {
	m_bReleased = FALSE;
	AddRef();
    }
    return theErr;
}

HX_RESULT
CScalableHTTP::Post(REF(CHXString) strMsg, const char* pHost, UINT16 nPort)
{
    if (strMsg.IsEmpty())
    {
	return HXR_INVALID_PARAMETER;
    }


    HX_ASSERT(nPort);

    m_pBufMsg = new CHXBuffer();
    m_pBufMsg->AddRef();
    m_pBufMsg->Set((BYTE*)(const char*)strMsg, strMsg.GetLength() + 1);

    // Ask the scheduler for callback to catch connection
    // timeout
    if (!m_pCallback)	
    {
    	m_pCallback = new CHTTPConnectionTimeoutCallback(this);
	if (m_pCallback)
	{
	    m_pCallback->AddRef();
	    m_pCallback->m_ulCallbackID = m_pScheduler->RelativeEnter(m_pCallback, m_ulConnTimeout);
	}	    
    }

    /* connect to the host */
    return m_pSocket->Connect(pHost, nPort);
}

HX_RESULT
CScalableHTTP::ConnectDone(HX_RESULT status)
{
    if (HXR_OK != status)
    {
	if (!m_bReleased)
	{
	    m_bReleased = TRUE;
	    Release();
	}
	return status;
    }

    RemoveCallback();	

    HX_ASSERT(m_pBufMsg);
    HX_ASSERT(m_pSocket);
    
    if (HXR_OK == m_pSocket->Write(m_pBufMsg))
    {
	m_pSocket->Read(1024);
    }

    return HXR_OK;
}

HX_RESULT
CScalableHTTP::ReadDone(HX_RESULT status, IHXBuffer* pBuffer)
{
#ifdef XXXGo_DEBUG
    BYTE* pc = NULL;
    if (HXR_OK == status && pBuffer)
    {
	pc = pBuffer->GetBuffer();
    }	
#endif

    // don't care about the response...
    if (!m_bReleased)
    {
	m_bReleased = TRUE;
	Release();
    }	
    return status;    
}

HX_RESULT
CScalableHTTP::Closed(HX_RESULT status)
{
    if (!m_bReleased)
    {
	m_bReleased = TRUE;
	Release();
    }
    return status;
}

void 
CScalableHTTP::RemoveCallback(void)
{
    if (m_pCallback && m_pScheduler)
    {
	if (m_pCallback->m_ulCallbackID)
	{
    	    m_pScheduler->Remove(m_pCallback->m_ulCallbackID);
    	    m_pCallback->m_ulCallbackID = 0;
	}    	    
    	HX_RELEASE(m_pCallback);
    }
}


STDMETHODIMP CScalableHTTP::QueryInterface(REFIID riid, void** ppvObj)
{
    if (IsEqualIID(riid, IID_IUnknown))
    {
	AddRef();
	*ppvObj = this;
	return HXR_OK;
    }
    
    *ppvObj = NULL;
    return HXR_NOINTERFACE;
}
STDMETHODIMP_(ULONG32) CScalableHTTP::AddRef()
{
    return InterlockedIncrement(&m_lRefCount);
}
STDMETHODIMP_(ULONG32) CScalableHTTP::Release()
{
    if (InterlockedDecrement(&m_lRefCount) > 0)
    {
        return m_lRefCount;
    }

    delete this;
    return 0;
}


/*****************
*   Response class
*/
CScalableHTTP::CHTTPResponse::CHTTPResponse()
    : m_lRefCount(0)
    , m_pOwner(NULL)
{
}
CScalableHTTP::CHTTPResponse::~CHTTPResponse()
{
}
HX_RESULT
CScalableHTTP::CHTTPResponse::Init(CScalableHTTP* pOwner)
{
    if (pOwner)
    {
	m_pOwner = pOwner;
	return HXR_OK;	
    }
    else
    {
	return HXR_FAIL;
    }
}
STDMETHODIMP CScalableHTTP::CHTTPResponse::QueryInterface(REFIID riid, void** ppvObj)
{
    if (IsEqualIID(riid, IID_IUnknown))
    {
	AddRef();
	*ppvObj = this;
	return HXR_OK;
    }
    else if(IsEqualIID(riid, IID_IHXTCPResponse))
    {
	AddRef();
	*ppvObj = (IHXTCPResponse*)this;
	return HXR_OK;
    }

    *ppvObj = NULL;
    return HXR_NOINTERFACE;
}
STDMETHODIMP_(ULONG32) CScalableHTTP::CHTTPResponse::AddRef()
{
    return InterlockedIncrement(&m_lRefCount);
}
STDMETHODIMP_(ULONG32) CScalableHTTP::CHTTPResponse::Release()
{
    if (InterlockedDecrement(&m_lRefCount) > 0)
    {
        return m_lRefCount;
    }

    delete this;
    return 0;
}
STDMETHODIMP CScalableHTTP::CHTTPResponse::ConnectDone(HX_RESULT status)
{
    return m_pOwner->ConnectDone(status);
}
STDMETHODIMP CScalableHTTP::CHTTPResponse::ReadDone(HX_RESULT   status,
						    IHXBuffer* pBuffer)
{
    return m_pOwner->ReadDone(status, pBuffer);
}
STDMETHODIMP CScalableHTTP::CHTTPResponse::WriteReady(HX_RESULT status)
{
    return HXR_OK;
}
STDMETHODIMP CScalableHTTP::CHTTPResponse::Closed(HX_RESULT status)
{
    return m_pOwner->Closed(status);
}

					

/**************
*   Timeout
*/
CScalableHTTP::CHTTPConnectionTimeoutCallback::CHTTPConnectionTimeoutCallback(CScalableHTTP* pOwner)
    : m_pOwner(pOwner)
    , m_lRefCount(0)
    , m_ulCallbackID(0)
{
}
CScalableHTTP::CHTTPConnectionTimeoutCallback::~CHTTPConnectionTimeoutCallback()
{
}
STDMETHODIMP
CScalableHTTP::CHTTPConnectionTimeoutCallback::Func()
{
    m_ulCallbackID = 0;
    m_pOwner->ConnectDone(HXR_FAIL);
    return HXR_OK;
}
STDMETHODIMP CScalableHTTP::CHTTPConnectionTimeoutCallback::QueryInterface(REFIID riid, void** ppvObj)
{
    if (IsEqualIID(riid, IID_IUnknown))
    {
	AddRef();
	*ppvObj = this;
	return HXR_OK;
    }
    else if(IsEqualIID(riid, IID_IHXCallback))
    {
	AddRef();
	*ppvObj = (IHXCallback*)this;
	return HXR_OK;
    }

    *ppvObj = NULL;
    return HXR_NOINTERFACE;
}
STDMETHODIMP_(ULONG32) CScalableHTTP::CHTTPConnectionTimeoutCallback::AddRef()
{
    return InterlockedIncrement(&m_lRefCount);
}
STDMETHODIMP_(ULONG32) CScalableHTTP::CHTTPConnectionTimeoutCallback::Release()
{
    if (InterlockedDecrement(&m_lRefCount) > 0)
    {
        return m_lRefCount;
    }

    delete this;
    return 0;
}

