//+-------------------------------------------------------------------
//
//  File:       actstrm.cxx
//
//  Contents:   code for providing a stream with an IBuffer interface
//              as well as providing marshalled interface data for 
//              RPC.
//
//  Classes:    ActivationStream
//
//  History:    30-Jan-93   Ricksa      Created CXmitRpcStream
//              04-Feb-98   Vinaykr     ActivationStream
//
//  Description: All requirements of CXmitRpcStream, plus the additional
//               requirement of being able to support a buffer interface
//               for activation custom marshalling.
//--------------------------------------------------------------------

#include <ole2int.h>

#include    <actstrm.hxx>


HRESULT GetActivationStream(REFIID riid, void** ppv, DWORD size)
{
    ActivationStream *st = new ActivationStream(size);
    if (st==NULL)
        return E_OUTOFMEMORY;
    return st->QueryInterface(riid, ppv);
    return S_OK;
}



STDMETHODIMP ActivationStream::QueryInterface(
    REFIID iidInterface,
    void FAR* FAR* ppvObj)
{
    HRESULT hresult = S_OK;

    // We only support IUnknown and IStream
    if (IsEqualIID(iidInterface, IID_IUnknown) ||
        IsEqualIID(iidInterface, IID_IStream))
    {
        *ppvObj = (IStream*)this;
        AddRef();
    }
    else
    if (IsEqualIID(iidInterface, IID_IBuffer))
    {
        *ppvObj = (IBuffer*)this;
        AddRef();
    }
    else
    {
        *ppvObj = NULL;
        hresult = E_NOINTERFACE;
    }

    return hresult;
}

STDMETHODIMP_(ULONG) ActivationStream::AddRef(void)
{
    Win4Assert((_clRefs != 0) && "ActivationStream::AddRef");
    InterlockedIncrement(&_clRefs);
    return _clRefs;
}

STDMETHODIMP_(ULONG) ActivationStream::Release(void)
{
    Win4Assert((_clRefs != 0) && "ActivationStream::Release");

    if (InterlockedDecrement(&_clRefs) == 0)
    {
        delete this;
        return 0;
    }

    return _clRefs;
}

STDMETHODIMP ActivationStream::Read(
    VOID HUGEP* pv,
    ULONG cb,
    ULONG FAR* pcbRead)
{
    HRESULT hresult = S_OK;

    if (pcbRead)
    {
        *pcbRead = 0L;
    }

    if (cb + _lOffset > _cbData)
    {
        cb = _cbData - _lOffset;
        //CairoleDebugOut((DEB_ERROR, "ActivationStream read past end of stream %x\n", cb+_lOffset));
        hresult = STG_E_READFAULT;
    }

    memcpy(pv,_pifData->abData + _lOffset, (size_t) cb);
    _lOffset += cb;

    if (pcbRead != NULL)
    {
        *pcbRead = cb;
    }

    return hresult;
}

STDMETHODIMP ActivationStream::Write(
    VOID  const HUGEP* pv,
    ULONG cbToWrite,
    ULONG FAR* pcbWritten)
{
    HRESULT hresult = S_OK;

    if (pcbWritten)
    {
        *pcbWritten = 0L;
    }

    if (cbToWrite + _lOffset > _cbData)
    {
        // the current stream is too small, try to grow it.

        if (!_fFree)
        {
            // The stream doesn't own the buffer so it can't reallocate it
            //CairoleDebugOut((DEB_ERROR, "ActivationStream write past end of stream %x\n",
                //cbToWrite + _lOffset));
            return STG_E_WRITEFAULT;
        }

        // Reallocate the size of the buffer
        // REVIEW: The constant added to the size allocated is a number
        // designed simply to try and decrease the number of follow on
        // allocations. In other words it needs to be tuned (or dropped!).

        BYTE *pbNewBuf = (BYTE *) ActMemAlloc(sizeof(DWORD) +
                                                     cbToWrite +
                                                     _lOffset + 64);

        if (pbNewBuf == NULL)
        {
            //CairoleDebugOut((DEB_ERROR, "ActivationStream cant grow stream\n"));
            return E_OUTOFMEMORY;
        }

        if (_pifData)
        {
            // we had a buffer from before, copy that in, and free the old one.
            memcpy(pbNewBuf, _pifData, sizeof(DWORD) + _cbData);
            ActMemFree(_pifData);
        }

        _cbData = cbToWrite + _lOffset + 64;
        _pifData = (InterfaceData *)pbNewBuf;
    }


    // copy in the new data
    memcpy(_pifData->abData + _lOffset, pv, (size_t) cbToWrite);
    _lOffset += cbToWrite;

    if (pcbWritten != NULL)
    {
        *pcbWritten = cbToWrite;
    }

    // We assume maxium size of buffer is the size to send on the network.
    if (_cSize < _lOffset)
    {
        _cSize = _lOffset;
    }

    return hresult;
}

STDMETHODIMP ActivationStream::Seek(
    LARGE_INTEGER dlibMoveIN,
    DWORD dwOrigin,
    ULARGE_INTEGER FAR* plibNewPosition)
{
    HRESULT hresult = S_OK;

/*
can't use this code until the stuff in ole2pr32.dll is fixed.

    // check against -2^31-1 <= x <= 2^31-1
    if (dlibMoveIN.HighPart == 0 && dlibMoveIN.LowPart < 0x80000000)
        // positive 31 bit value
        ;
    else if (dlibMoveIN.HighPart == -1L && dlibMoveIN.LowPart >= 0x80000000)
        // negative 31 bit value
        ;
    else
        return STG_E_SEEKERROR;
*/

    LONG dlibMove = dlibMoveIN.LowPart;
    ULONG cbNewPos = dlibMove;

    switch(dwOrigin)
    {
    case STREAM_SEEK_SET:

        if (dlibMove >= 0)
        {
            _lOffset = dlibMove;
        }
        else
        {
            hresult = STG_E_SEEKERROR;
        }
        break;

    case STREAM_SEEK_CUR:

        if (!(dlibMove < 0 && (-dlibMove > _lOffset)))
        {
            _lOffset += (ULONG) dlibMove;
        }
        else
        {
            hresult = STG_E_SEEKERROR;
        }
        break;

    case STREAM_SEEK_END:

        if (!(dlibMove < 0 && ((ULONG) -dlibMove) > _cbData))
        {
            _lOffset = _cbData + dlibMove;
        }
        else
        {
            hresult = STG_E_SEEKERROR;
        }
        break;

    default:

        hresult = STG_E_SEEKERROR;
    }

    if (plibNewPosition != NULL)
    {
        ULISet32(*plibNewPosition, _lOffset);
    }

    return hresult;
}

STDMETHODIMP ActivationStream::SetSize(ULARGE_INTEGER cb)
{
    return E_NOTIMPL;
}

STDMETHODIMP ActivationStream::CopyTo(
    IStream FAR* pstm,
    ULARGE_INTEGER cb,
    ULARGE_INTEGER FAR* pcbRead,
    ULARGE_INTEGER FAR* pcbWritten)
{
    return E_NOTIMPL;
}

STDMETHODIMP ActivationStream::Commit(DWORD grfCommitFlags)
{
    return NOERROR;
}

STDMETHODIMP ActivationStream::Revert(void)
{
    return NOERROR;
}

STDMETHODIMP ActivationStream::LockRegion(
    ULARGE_INTEGER libOffset,
    ULARGE_INTEGER cb,
    DWORD dwLockType)
{
    return STG_E_INVALIDFUNCTION;
}

STDMETHODIMP ActivationStream::UnlockRegion(
    ULARGE_INTEGER libOffset,
    ULARGE_INTEGER cb,
    DWORD dwLockType)
{
    return STG_E_INVALIDFUNCTION;
}

STDMETHODIMP ActivationStream::Stat(
    STATSTG FAR* pstatstg,
    DWORD statflag)
{
    memset(pstatstg, 0, sizeof(STATSTG));
    return E_NOTIMPL;
}

STDMETHODIMP ActivationStream::SetCopyAlignment(DWORD alignment)
{
    _copyAlignment = alignment;
    return S_OK;
}

inline ActivationStream *ActivationStream::Clone()
{
    DWORD len;
    BYTE *newBuff;
    DWORD alignmentOffset=0;

    // Assume 8 byte alignment for new buffer
    ActivationStream *strm = new ActivationStream(_cbData+_copyAlignment-1);

    if (strm == NULL)
        return strm;

    // make sure we were able to allocate an internal buffer
    if (_cbData != 0)
    {
        strm->GetLength(&len);
        if (len == 0)
        {
            delete strm;
            return NULL;
        }
    }
    
    strm->GetBuffer(&len, &newBuff);
   
    ASSERT(len==(_cbData+_copyAlignment-1));
    ASSERT( sizeof(_lOffset) == sizeof(LONG) );
    if ((UINT_PTR)(newBuff+_lOffset) & (_copyAlignment-1))
    {
        alignmentOffset = _copyAlignment - 
                               ( PtrToUlong(newBuff+_lOffset) & (_copyAlignment-1) );
    }
    GetCopy(newBuff+alignmentOffset);
    strm->SetPosition(len, _lOffset+alignmentOffset);
    return strm;
}

STDMETHODIMP ActivationStream::Clone(IStream FAR * FAR *ppstm)
{
    *ppstm = Clone();
    if (*ppstm==NULL)
        return E_OUTOFMEMORY;
    return S_OK;
}

STDMETHODIMP ActivationStream::GetOrCreateBuffer(DWORD dwReq, DWORD *pdwLength, BYTE **ppBuff)
{
    if (((_cbData - _lOffset) < dwReq) || (!_pifData))
    {
        BYTE* pbNewBuf = (BYTE*)ActMemAlloc(sizeof(DWORD)+dwReq+_cbData-_lOffset);
        if (pbNewBuf==NULL)
            return E_OUTOFMEMORY;

        if (_pifData)
        {
            // we had a buffer from before, copy that in, and free the old one.
            memcpy(pbNewBuf, _pifData, sizeof(DWORD) + _cbData);
            ActMemFree(_pifData);
        } 
        // update _cbData
        _cbData = dwReq + _cbData - _lOffset;

        _pifData = (InterfaceData*)pbNewBuf;
    }
    *ppBuff = _pifData->abData + _lOffset;
    *pdwLength = _cbData - _lOffset;
    return S_OK;
}

STDMETHODIMP ActivationStream::GetBuffer(DWORD *pdwLength, BYTE **ppBuff)
{
    *pdwLength = _cbData-_lOffset;
    *ppBuff = _pifData->abData + _lOffset;
    return S_OK;
}

STDMETHODIMP ActivationStream::GetLength(DWORD *pdwLength)
{
    *pdwLength = _cbData;
    return S_OK;
}

STDMETHODIMP ActivationStream::GetCopy(BYTE *pBuff)
{
    memcpy(pBuff, _pifData->abData, _cbData);
    return S_OK;
}

STDMETHODIMP ActivationStream::SetPosition(DWORD dwLenFromEnd, DWORD dwPosFromStart)
{
    if (dwPosFromStart > dwLenFromEnd)
        return E_FAIL;
    _lOffset = _cbData - dwLenFromEnd + dwPosFromStart;
    if (_cSize < _lOffset)
        _cSize = _lOffset;
    return S_OK;
}

STDMETHODIMP ActivationStream::SetBuffer(DWORD dwLength, BYTE *pBuff)
{
    return E_NOTIMPL;
}