/*
 * IPERSTOR.CPP
 * Cosmo Handler Chapter 11
 *
 * Implementation of the IPersistStorage interface that we expose on
 * the Figure object.
 *
 * Copyright (c)1993-1994 Microsoft Corporation, All Rights Reserved
 *
 * Kraig Brockschmidt, Software Design Engineer
 * Microsoft Systems Developer Relations
 *
 * Internet  :  kraigb@microsoft.com
 * Compuserve:  >INTERNET:kraigb@microsoft.com
 */


#include "hcosmo.h"


/*
 * CImpIPersistStorage:CImpIPersistStorage
 * CImpIPersistStorage::~CImpIPersistStorage
 *
 * Constructor Parameters:
 *  pObj            PCFigure pointing to the object we live in.
 *  pUnkOuter       LPUNKNOWN of the controlling unknown.
 */

CImpIPersistStorage::CImpIPersistStorage(PCFigure pObj
    , LPUNKNOWN pUnkOuter)
    {
    m_cRef=0;
    m_pObj=pObj;
    m_pUnkOuter=pUnkOuter;
    m_psState=PSSTATE_UNINIT;
    return;
    }


CImpIPersistStorage::~CImpIPersistStorage(void)
    {
    return;
    }




/*
 * CImpIPersistStorage::QueryInterface
 * CImpIPersistStorage::AddRef
 * CImpIPersistStorage::Release
 *
 * Purpose:
 *  Standard set of IUnknown members for this interface
 */

STDMETHODIMP CImpIPersistStorage::QueryInterface(REFIID riid
    , PPVOID ppv)
    {
    return m_pUnkOuter->QueryInterface(riid, ppv);
    }

STDMETHODIMP_(ULONG) CImpIPersistStorage::AddRef(void)
    {
    ++m_cRef;
    return m_pUnkOuter->AddRef();
    }

STDMETHODIMP_(ULONG) CImpIPersistStorage::Release(void)
    {
    --m_cRef;
    return m_pUnkOuter->Release();
    }





/*
 * CImpIPersistStorage::GetClassID
 *
 * Purpose:
 *  Returns the CLSID of the object represented by this interface.
 */

STDMETHODIMP CImpIPersistStorage::GetClassID(LPCLSID pClsID)
    {
    if (PSSTATE_UNINIT==m_psState)
        return ResultFromScode(E_UNEXPECTED);

    *pClsID=m_pObj->m_clsID;
    return NOERROR;
    }




/*
 * CImpIPersistStorage::IsDirty
 *
 * Purpose:
 *  Tells the caller if we have made changes to this object since
 *  it was loaded or initialized new.
 */

STDMETHODIMP CImpIPersistStorage::IsDirty(void)
    {
    if (PSSTATE_UNINIT==m_psState)
        return ResultFromScode(E_UNEXPECTED);

    /*
     * Since we don't edit, we have no idea if this data is dirty.
     * Delegate to the default handler in case it wants to ask the
     * server.
     */
    return m_pObj->m_pDefIPersistStorage->IsDirty();
    }





/*
 * CImpIPersistStorage::InitNew
 *
 * Purpose:
 *  Provides the object with the IStorage they hold on to while
 *  they are running.  Since we don't create data in the handler,
 *  there it nothing to write here.  We only need to initialize
 *  out internal state.  This function is only be called once in
 *  the object's lifetime in lieu of Load.
 */

STDMETHODIMP CImpIPersistStorage::InitNew(LPSTORAGE pIStorage)
    {
    if (PSSTATE_UNINIT!=m_psState)
        return ResultFromScode(E_UNEXPECTED);

    if (NULL==pIStorage)
        return ResultFromScode(E_POINTER);

    //Good time to initilize our data
    m_pObj->m_pl.wVerMaj=VERSIONMAJOR;
    m_pObj->m_pl.wVerMin=VERSIONMINOR;
    m_pObj->m_pl.cPoints=0;
    m_pObj->m_pl.rgbBackground=GetSysColor(COLOR_WINDOW);
    m_pObj->m_pl.rgbLine=GetSysColor(COLOR_WINDOWTEXT);
    m_pObj->m_pl.iLineStyle=PS_SOLID;

    //Make sure these aren't filled with trash.
    memcpy(&m_pObj->m_plContent,   &m_pObj->m_pl, CBPOLYLINEDATA);
    memcpy(&m_pObj->m_plThumbnail, &m_pObj->m_pl, CBPOLYLINEDATA);

    m_pObj->m_pDefIPersistStorage->InitNew(pIStorage);

    /*
     * This is just for state validation in other calls.  As
     * a handler we never actually 'scribble.'
     */
    m_psState=PSSTATE_SCRIBBLE;
    return NOERROR;
    }





/*
 * CImpIPersistStorage::Load
 *
 * Purpose:
 *  Instructs the object to load itself from a previously saved
 *  IStorage that was handled by Save in another object lifetime.
 *  This function will only be called once in the object's lifetime
 *  in lieu of InitNew.
 */

STDMETHODIMP CImpIPersistStorage::Load(LPSTORAGE pIStorage)
    {
    POLYLINEDATA    pl;
    ULONG           cb;
    LPSTREAM        pIStream;
    HRESULT         hr;

    if (PSSTATE_UNINIT!=m_psState)
        return ResultFromScode(E_UNEXPECTED);

    if (NULL==pIStorage)
        return ResultFromScode(E_POINTER);

    //Open the CONTENTS stream
    hr=pIStorage->OpenStream(SZSTREAM, 0, STGM_DIRECT | STGM_READ
        | STGM_SHARE_EXCLUSIVE, 0, &pIStream);

    if (FAILED(hr))
        return ResultFromScode(STG_E_READFAULT);

    //Read all the data into the POLYLINEDATA structure.
    hr=pIStream->Read(&pl, CBPOLYLINEDATA, &cb);
    pIStream->Release();

    if (CBPOLYLINEDATA!=cb)
        return ResultFromScode(STG_E_READFAULT);

    //Copy into the actual object now.
    memcpy(&m_pObj->m_pl, &pl, CBPOLYLINEDATA);

    m_pObj->m_pDefIPersistStorage->Load(pIStorage);

    //As with InitNew, this is just for validating other calls
    m_psState=PSSTATE_SCRIBBLE;
    return NOERROR;
    }





/*
 * CImpIPersistStorage::Save
 *
 * Purpose:
 *  Saves the data for this object to an IStorage.
 */

STDMETHODIMP CImpIPersistStorage::Save(LPSTORAGE pIStorage
    , BOOL fSameAsLoad)
    {
    ULONG           cb;
    HRESULT         hr;
    LPSTREAM        pIStream;

    //Have to come here from scribble state.
    if (PSSTATE_SCRIBBLE!=m_psState)
        return ResultFromScode(E_UNEXPECTED);

    //Must have an IStorage if we're not in SameAsLoad
    if (NULL==pIStorage && !fSameAsLoad)
        return ResultFromScode(E_POINTER);

    /*
     * If the server is running, don't do the save ourselves since
     * we'd end up writing the storage twice with possible conflicts.
     */
    if (OleIsRunning(m_pObj->m_pDefIOleObject))
        {
        hr=m_pObj->m_pDefIPersistStorage->Save(pIStorage
            , fSameAsLoad);

        if (SUCCEEDED(hr))
            m_psState=PSSTATE_ZOMBIE;

        return hr;
        }

    /*
     * Since we don't have any data we modify in the handler,
     * we have nothing to do for same-as-load cases.  The only
     * case we have to handle is writing to a new storage.
     */
    if (!fSameAsLoad)
        {
        /*
         * This code facilitates making copies of an object into
         * a new storage during a container Save As.
         */

        hr=pIStorage->CreateStream(SZSTREAM, STGM_DIRECT | STGM_CREATE
            | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pIStream);

        if (FAILED(hr))
            return hr;

        WriteClassStg(pIStorage, CLSID_Cosmo2Figure);
        WriteFmtUserTypeStg(pIStorage, m_pObj->m_cf
            , TEXT("Polyline Figure"));

        hr=pIStream->Write(&m_pObj->m_pl, CBPOLYLINEDATA, &cb);
        pIStream->Release();

        if (FAILED(hr) || CBPOLYLINEDATA!=cb)
            return ResultFromScode(STG_E_WRITEFAULT);
        }

    m_psState=PSSTATE_ZOMBIE;
    m_pObj->m_pDefIPersistStorage->Save(pIStorage, fSameAsLoad);
    return NOERROR;
    }




/*
 * CImpIPersistStorage::SaveCompleted
 * CImpIPersistStorage::HandsOffStorage
 *
 * Purpose:
 *  Pass throughs with typical management of our pointers.
 */

STDMETHODIMP CImpIPersistStorage::SaveCompleted(LPSTORAGE pIStorage)
    {
    //Must be called in no-scribble or hands-off state
    if (!(PSSTATE_ZOMBIE==m_psState || PSSTATE_HANDSOFF==m_psState))
        return ResultFromScode(E_UNEXPECTED);

    //If we're coming from Hands-Off, we'd better get a storage
    if (NULL==pIStorage && PSSTATE_HANDSOFF==m_psState)
        return ResultFromScode(E_UNEXPECTED);

    /*
     * Since this handler modifies nothing, it saves no pointers
     * which it would have to release here.  Nothing to do.
     */

    m_pObj->m_pDefIPersistStorage->SaveCompleted(pIStorage);
    m_psState=PSSTATE_SCRIBBLE;
    return NOERROR;
    }


STDMETHODIMP CImpIPersistStorage::HandsOffStorage(void)
    {
    if (PSSTATE_UNINIT==m_psState || PSSTATE_HANDSOFF==m_psState)
        return ResultFromScode(E_UNEXPECTED);

    /*
     * Since this handler modifies nothing, it saves no pointers
     * which it would have to release here.  Nothing to do.
     */

    m_pObj->m_pDefIPersistStorage->HandsOffStorage();
    m_psState=PSSTATE_HANDSOFF;
    return NOERROR;
    }
