// MLSBWalk.h : Declaration of the CMLStrBufWalk

#ifndef __MLSBWALK_H_
#define __MLSBWALK_H_

/////////////////////////////////////////////////////////////////////////////
// CMLStrBufWalk
template <class IMLSTRBUF, class CHTYPE>
class CMLStrBufWalk
{
public:
    inline CMLStrBufWalk(IMLSTRBUF* pMLStrBuf, long cchOffset, long cchLen, BOOL fCanStopAtMiddle = FALSE);
    BOOL Lock(HRESULT& rhr);
    void Unlock(HRESULT& rhr, long cchActual = 0);
    inline CHTYPE* GetStr(void);
    inline long GetCCh(void) const;
    inline long GetDoneCCh(void) const;
    inline long GetRestCCh(void) const;

protected:
    IMLSTRBUF* m_pMLStrBuf;
    BOOL m_fCanStopAtMiddle;
    long m_cchOffset;
    long m_cchLen;
    long m_cchDone;
    CHTYPE* m_pszBuf;
    long m_cchBuf;
};

template <class IMLSTRBUF, class CHTYPE>
CMLStrBufWalk<IMLSTRBUF, CHTYPE>::CMLStrBufWalk(IMLSTRBUF* pMLStrBuf, long cchOffset, long cchLen, BOOL fCanStopAtMiddle) :
    m_pMLStrBuf(pMLStrBuf),
    m_fCanStopAtMiddle(fCanStopAtMiddle)
{
    m_cchOffset = cchOffset;
    m_cchLen = cchLen;
    m_cchDone = 0;

    m_pszBuf = NULL; // Mark as it's not locked
}

template <class IMLSTRBUF, class CHTYPE>
BOOL CMLStrBufWalk<IMLSTRBUF, CHTYPE>::Lock(HRESULT& rhr)
{
    if (m_pszBuf)
        rhr = E_FAIL; // Already locked

    if (SUCCEEDED(rhr) &&
        m_cchLen > 0 &&
        FAILED(rhr = m_pMLStrBuf->LockBuf(m_cchOffset, m_cchLen, &m_pszBuf, &m_cchBuf)))
    {
        m_pszBuf = NULL; // Mark as it's not locked
    }

    if (m_fCanStopAtMiddle && FAILED(rhr) && m_cchDone > 0)
    {
        rhr = S_OK;
        return FALSE; // Stop it, but not fail
    }
    else
    {
        return (SUCCEEDED(rhr) && m_cchLen > 0);
    }
}

template <class IMLSTRBUF, class CHTYPE>
void CMLStrBufWalk<IMLSTRBUF, CHTYPE>::Unlock(HRESULT& rhr, long cchActual)
{
    HRESULT hr = S_OK;

    if (!m_pszBuf)
        hr = E_FAIL; // Not locked yet

    if (SUCCEEDED(hr) &&
        SUCCEEDED(hr = m_pMLStrBuf->UnlockBuf(m_pszBuf, 0, 0))) // Unlock even if rhr is already failed
    {
        if (!cchActual)
            cchActual = m_cchBuf;
        else
            ASSERT(cchActual > 0 && cchActual <= m_cchBuf);

        m_cchOffset += cchActual;
        m_cchLen -= cchActual;
        m_cchDone += cchActual;
    }

    m_pszBuf = NULL; // Unlock anyway

    if (SUCCEEDED(rhr))
        rhr = hr; // if rhr is failed before UnlockBuf, use it
}

template <class IMLSTRBUF, class CHTYPE>
CHTYPE* CMLStrBufWalk<IMLSTRBUF, CHTYPE>::GetStr(void)
{
    ASSERT(m_pszBuf); // Not locked
    return m_pszBuf;
}

template <class IMLSTRBUF, class CHTYPE>
long CMLStrBufWalk<IMLSTRBUF, CHTYPE>::GetCCh(void) const
{
    ASSERT(m_pszBuf); // Not locked
    if (m_pszBuf)
        return m_cchBuf;
    else
        return 0;
}

template <class IMLSTRBUF, class CHTYPE>
long CMLStrBufWalk<IMLSTRBUF, CHTYPE>::GetDoneCCh(void) const
{
    return m_cchDone;
}

template <class IMLSTRBUF, class CHTYPE>
long CMLStrBufWalk<IMLSTRBUF, CHTYPE>::GetRestCCh(void) const
{
    return m_cchLen - m_cchDone;
}

typedef CMLStrBufWalk<IMLangStringBufW, WCHAR> CMLStrBufWalkW;
typedef CMLStrBufWalk<IMLangStringBufA, CHAR>  CMLStrBufWalkA;

#endif //__MLSBWALK_H_