///////////////////////////////////////////////////////////////////////////////
// Name:        wx/msw/private/comstream.h
// Purpose:     COM Stream adapter to wxStreamBase based streams
// Created:     2021-01-15
// Copyright:   (c) 2021 wxWidgets team
// Licence:     wxWindows licence
///////////////////////////////////////////////////////////////////////////////

#ifndef WX_COMSTREAM_H
#define WX_COMSTREAM_H

#include "wx/stream.h"
#include "wx/msw/ole/comimpl.h"

class wxCOMBaseStreamAdapter : public IStream
{
public:
    wxCOMBaseStreamAdapter(wxStreamBase* stream):
        m_stream(stream)
    { }

    virtual ~wxCOMBaseStreamAdapter() = default;

    // IUnknown
    STDMETHODIMP QueryInterface(REFIID riid, void** ppv) override
    {
        if (riid == IID_IUnknown ||
            riid == IID_ISequentialStream ||
            riid == IID_IStream)
        {
            *ppv = this;
            AddRef();
            return S_OK;
        }

        *ppv = nullptr;
        return E_NOINTERFACE;
    }

    STDMETHODIMP_(ULONG) AddRef() override
    {
        return ++m_cRef;
    }

    STDMETHODIMP_(ULONG) Release() override
    {
        if (--m_cRef == wxAutoULong(0))
        {
                delete this;
                return 0;
        }
        else
            return m_cRef;
    }

    // IStream
    virtual HRESULT STDMETHODCALLTYPE Seek(
        LARGE_INTEGER WXUNUSED(dlibMove), DWORD WXUNUSED(dwOrigin),
        ULARGE_INTEGER *WXUNUSED(plibNewPosition)) override
    {
        return E_NOTIMPL;
    }

    virtual HRESULT STDMETHODCALLTYPE SetSize(ULARGE_INTEGER WXUNUSED(libNewSize)) override
    {
        return E_NOTIMPL;
    }

    virtual HRESULT STDMETHODCALLTYPE CopyTo(IStream *WXUNUSED(pstm),
        ULARGE_INTEGER WXUNUSED(cb), ULARGE_INTEGER *WXUNUSED(pcbRead),
        ULARGE_INTEGER *WXUNUSED(pcbWritten)) override
    {
        return E_NOTIMPL;
    }

    virtual HRESULT STDMETHODCALLTYPE Commit(DWORD WXUNUSED(grfCommitFlags)) override
    {
        return E_NOTIMPL;
    }

    virtual HRESULT STDMETHODCALLTYPE Revert(void) override
    {
        return E_NOTIMPL;
    }

    virtual HRESULT STDMETHODCALLTYPE LockRegion(ULARGE_INTEGER WXUNUSED(libOffset),
        ULARGE_INTEGER WXUNUSED(cb), DWORD WXUNUSED(dwLockType)) override
    {
        return E_NOTIMPL;
    }

    virtual HRESULT STDMETHODCALLTYPE UnlockRegion(ULARGE_INTEGER WXUNUSED(libOffset),
        ULARGE_INTEGER WXUNUSED(cb), DWORD WXUNUSED(dwLockType)) override
    {
        return E_NOTIMPL;
    }

    virtual HRESULT STDMETHODCALLTYPE Stat(STATSTG *pstatstg, DWORD WXUNUSED(grfStatFlag)) override
    {
        pstatstg->type = STGTY_STREAM;
        pstatstg->cbSize.QuadPart = m_stream->GetSize();

        return S_OK;
    }

    virtual HRESULT STDMETHODCALLTYPE Clone(IStream **WXUNUSED(ppstm)) override
    {
        return E_NOTIMPL;
    }

    // ISequentialStream

    virtual HRESULT STDMETHODCALLTYPE Read(void *WXUNUSED(pv),
        ULONG WXUNUSED(cb), ULONG *WXUNUSED(pcbRead)) override
    {
        return E_NOTIMPL;
    }

    virtual HRESULT STDMETHODCALLTYPE Write(const void *WXUNUSED(pv),
        ULONG WXUNUSED(cb), ULONG *WXUNUSED(pcbWritten)) override
    {
        return E_NOTIMPL;
    }


protected:
    wxStreamBase* m_stream;

    static wxSeekMode OriginToSeekMode(DWORD origin)
    {
        switch (origin)
        {
        case STREAM_SEEK_SET:
            return wxFromStart;
        case STREAM_SEEK_CUR:
            return wxFromCurrent;
        case STREAM_SEEK_END:
            return wxFromEnd;
        }

        return wxFromStart;
    }
private:
    wxAutoULong m_cRef;
};

class wxCOMInputStreamAdapter : public wxCOMBaseStreamAdapter
{
public:
    wxCOMInputStreamAdapter(wxInputStream* stream):
        wxCOMBaseStreamAdapter(stream)
    {

    }

    virtual HRESULT STDMETHODCALLTYPE Seek(
        LARGE_INTEGER dlibMove, DWORD dwOrigin,
        ULARGE_INTEGER *plibNewPosition) override
    {
        wxFileOffset newOffset = reinterpret_cast<wxInputStream*>(m_stream)->SeekI(dlibMove.QuadPart, OriginToSeekMode(dwOrigin));
        if (plibNewPosition)
            plibNewPosition->QuadPart = newOffset;
        return S_OK;
    }

    virtual HRESULT STDMETHODCALLTYPE Read(void *pv, ULONG cb, ULONG *pcbRead) override
    {
        wxInputStream* inputStr = reinterpret_cast<wxInputStream*>(m_stream);
        inputStr->Read(pv, cb);
        *pcbRead = inputStr->LastRead();
        return S_OK;
    }

};

class wxCOMOutputStreamAdapter : public wxCOMBaseStreamAdapter
{
public:
    wxCOMOutputStreamAdapter(wxOutputStream* stream) :
        wxCOMBaseStreamAdapter(stream)
    { }

    virtual HRESULT STDMETHODCALLTYPE Seek(
        LARGE_INTEGER dlibMove, DWORD dwOrigin,
        ULARGE_INTEGER *plibNewPosition) override
    {
        wxFileOffset newOffset = reinterpret_cast<wxOutputStream*>(m_stream)->SeekO(dlibMove.QuadPart, OriginToSeekMode(dwOrigin));
        if (plibNewPosition)
            plibNewPosition->QuadPart = newOffset;
        return S_OK;
    }

    virtual HRESULT STDMETHODCALLTYPE Write(const void *pv, ULONG cb, ULONG *pcbWritten) override
    {
        wxOutputStream* outputStr = reinterpret_cast<wxOutputStream*>(m_stream);
        outputStr->Write(pv, cb);
        *pcbWritten = outputStr->LastWrite();
        return S_OK;
    }

};

#endif // WX_COMSTREAM_H
