/****************************** Module Header ******************************\
* Module Name: doc.c 
*
* PURPOSE: Contains client document maipulation routines.
*
* Created: Jan 1991
*
* Copyright (c) 1991  Microsoft Corporation
*
* History:
*   Srinik  01/11/1191  Orginal
*
\***************************************************************************/

#include <windows.h>
#include "dll.h"

#ifdef FIREWALLS
extern BOOL     bShowed;
extern void FARINTERNAL ShowVersion (void);
#endif

LPCLIENTDOC lpHeadDoc = NULL;
LPCLIENTDOC lpTailDoc  = NULL;

extern ATOM aClipDoc;
extern int  iUnloadableDll;

#pragma alloc_text(_TEXT, CheckClientDoc, CheckPointer)


OLESTATUS FAR PASCAL OleRegisterClientDoc (lpClassName, lpDocName, future, lplhclientdoc)
LPSTR               lpClassName;
LPSTR               lpDocName;
LONG                future;
LHCLIENTDOC FAR *   lplhclientdoc;
{
    HANDLE      hdoc = NULL;
    LPCLIENTDOC lpdoc;
    OLESTATUS   retVal;
    ATOM        aClass, aDoc;
    
    
#ifdef FIREWALLS
    if (!bShowed && (ole_flags & DEBUG_MESSAGEBOX))
        ShowVersion ();
#endif

    Puts ("OleRegisterClientDoc");

    PROBE_MODE(bProtMode);
    FARPROBE_WRITE(lplhclientdoc);
    *lplhclientdoc = NULL;
    FARPROBE_READ(lpClassName);
    FARPROBE_READ(lpDocName);
    if (!lpDocName[0])
        return OLE_ERROR_NAME;
    
    aDoc = GlobalAddAtom (lpDocName); 
    aClass = GlobalAddAtom (lpClassName);

    if (!(hdoc = GlobalAlloc (GMEM_MOVEABLE | GMEM_ZEROINIT | GMEM_SHARE, 
                        sizeof(CLIENTDOC)))
            || !(lpdoc = (LPCLIENTDOC) GlobalLock (hdoc))) {
        retVal =  OLE_ERROR_MEMORY;
        goto err;
    }

    lpdoc->docId[0] = 'C';
    lpdoc->docId[1] = 'D';
    lpdoc->aClass   = aClass;
    lpdoc->aDoc     = aDoc;
    lpdoc->hdoc     = hdoc;
        
    // Documents are doubly linked
        
    if (!lpHeadDoc) {
#ifdef FIREWALLS        
        ASSERT(!lpTailDoc, "lpTailDoc is not NULL");
#endif  
        lpHeadDoc = lpTailDoc = lpdoc;
    }
    else {
        lpTailDoc->lpNextDoc = lpdoc;
        lpdoc->lpPrevDoc = lpTailDoc;
        lpTailDoc = lpdoc;
    }
    
    *lplhclientdoc = (LHCLIENTDOC) lpdoc;

    // inform the link manager;
    return OLE_OK;
    
err:
    if (aClass) 
        GlobalDeleteAtom (aClass);

    if (aDoc)
        GlobalDeleteAtom (aDoc);    
    
    if (hdoc)
        GlobalFree (hdoc);

    return retVal;
}


OLESTATUS FAR PASCAL OleRevokeClientDoc (lhclientdoc)
LHCLIENTDOC lhclientdoc;
{
    LPCLIENTDOC lpdoc;
    
    Puts ("OleRevokeClientDoc");

    // if there is any handler dll that can be freed up, free it now. 
    // Otherwise it might become too late if the app quits.
    if (iUnloadableDll) 
        UnloadDll ();
    
    if (!CheckClientDoc (lpdoc = (LPCLIENTDOC) lhclientdoc))
        return OLE_ERROR_HANDLE;
    
    if (lpdoc->lpHeadObj) {
        ASSERT (0, "OleRevokeClientDoc() called without freeing all objects");
        return OLE_ERROR_NOT_EMPTY;
    }
    
    if (lpdoc->aClass)
        GlobalDeleteAtom (lpdoc->aClass);   

    if (lpdoc->aDoc)
        GlobalDeleteAtom (lpdoc->aDoc);

    // if only one doc is in the list then it's prev and next docs are NULL
        
    if (lpdoc == lpHeadDoc)
        lpHeadDoc = lpdoc->lpNextDoc;

    if (lpdoc == lpTailDoc)
        lpTailDoc = lpdoc->lpPrevDoc;       

    if (lpdoc->lpPrevDoc)
        lpdoc->lpPrevDoc->lpNextDoc = lpdoc->lpNextDoc;

    if (lpdoc->lpNextDoc)   
        lpdoc->lpNextDoc->lpPrevDoc = lpdoc->lpPrevDoc;     

    GlobalUnlock (lpdoc->hdoc);
    GlobalFree (lpdoc->hdoc);
    
    // inform link manager
    return OLE_OK;
}


OLESTATUS FAR PASCAL OleRenameClientDoc (lhclientdoc, lpNewDocName)
LHCLIENTDOC lhclientdoc;
LPSTR       lpNewDocName;
{
    LPCLIENTDOC lpdoc;
    ATOM        aNewDoc;
    LPOLEOBJECT lpobj = NULL;
    
    if (!CheckClientDoc (lpdoc = (LPCLIENTDOC) lhclientdoc))
        return OLE_ERROR_HANDLE;
    
    FARPROBE_READ(lpNewDocName);
    
    aNewDoc = GlobalAddAtom (lpNewDocName);
    if (aNewDoc == lpdoc->aDoc) {
        if (aNewDoc)
            GlobalDeleteAtom (aNewDoc);
        return OLE_OK;
    }
    
    // Document name has changed. So, change the topic of all embedded objects
    if (lpdoc->aDoc)
        GlobalDeleteAtom (lpdoc->aDoc);
    lpdoc->aDoc = aNewDoc;
    
    while (lpobj = DocGetNextObject (lpdoc, lpobj)) {
        if (lpobj->ctype == CT_EMBEDDED)
            if (OleQueryReleaseStatus (lpobj) != OLE_BUSY)
                SetEmbeddedTopic ((LPOBJECT_LE) lpobj);
    }
    
    return OLE_OK;
}


OLESTATUS FAR PASCAL OleRevertClientDoc (lhclientdoc)
LHCLIENTDOC lhclientdoc;
{
    // if there is any handler dll that can be freed up, free it now.
    // Otherwise it might become too late if the app quits.
    if (iUnloadableDll) 
        UnloadDll ();
    
    if (!CheckClientDoc ((LPCLIENTDOC) lhclientdoc))
        return OLE_ERROR_HANDLE;

    return OLE_OK;
}


OLESTATUS FAR PASCAL OleSavedClientDoc (lhclientdoc)
LHCLIENTDOC lhclientdoc;
{
    if (!CheckClientDoc ((LPCLIENTDOC) lhclientdoc))
        return OLE_ERROR_HANDLE;

    return OLE_OK;
}

OLESTATUS FAR PASCAL OleEnumObjects (lhclientdoc, lplpobj)
LHCLIENTDOC         lhclientdoc;
LPOLEOBJECT FAR *   lplpobj;
{
    if (!CheckClientDoc ((LPCLIENTDOC) lhclientdoc))
        return OLE_ERROR_HANDLE;

    FARPROBE_WRITE(lplpobj);

    if (*lplpobj) {
        // we are making lhclientdoc field of the object NULL at deletion 
        // time. The check (*lpobj->lhclientdoc != lhclientdoc) will take care
        // of the case where same chunk of memory is allocated again for the
        // same pointer and old contents are not erased yet.
        if (!FarCheckObject (*lplpobj) 
                || ((*lplpobj)->lhclientdoc != lhclientdoc))
            return OLE_ERROR_OBJECT;    
    }
    
    *lplpobj = DocGetNextObject ((LPCLIENTDOC) lhclientdoc, *lplpobj);
    return OLE_OK;
}


    
LPOLEOBJECT INTERNAL DocGetNextObject (lpdoc, lpobj)
LPCLIENTDOC lpdoc;
LPOLEOBJECT lpobj;
{
    if (!lpobj)
        return lpdoc->lpHeadObj;
    
    return lpobj->lpNextObj;
}


BOOL FARINTERNAL CheckClientDoc (lpdoc)
LPCLIENTDOC lpdoc;
{
    if (!CheckPointer(lpdoc, WRITE_ACCESS))
        return FALSE;

    if ((lpdoc->docId[0] == 'C') && (lpdoc->docId[1] == 'D'))
        return TRUE;
    return FALSE;
}


void FARINTERNAL DocAddObject (lpdoc, lpobj, lpobjname)
LPCLIENTDOC lpdoc;
LPOLEOBJECT lpobj;
LPSTR       lpobjname;
{
    if (lpobjname) 
        lpobj->aObjName = GlobalAddAtom (lpobjname);
    else
        lpobj->aObjName = NULL;
    
    // Objects of a doc are doubly linked
        
    if (!lpdoc->lpHeadObj)
        lpdoc->lpHeadObj = lpdoc->lpTailObj = lpobj;
    else {
        lpdoc->lpTailObj->lpNextObj = lpobj;
        lpobj->lpPrevObj = lpdoc->lpTailObj;
        lpdoc->lpTailObj = lpobj;
    }
    lpobj->lhclientdoc = (LHCLIENTDOC)lpdoc;
}


void FARINTERNAL DocDeleteObject (lpobj)
LPOLEOBJECT lpobj;
{
    LPCLIENTDOC lpdoc;

    if (!(lpdoc = (LPCLIENTDOC) lpobj->lhclientdoc))
        return;

    if (lpobj->aObjName) {
        GlobalDeleteAtom (lpobj->aObjName);
        lpobj->aObjName = NULL;
    }
    
    // Remove this obj from object chain of the relevant client doc.
    // The objects of a doc are doubly linked.
                
    if (lpdoc->lpHeadObj == lpobj)
        lpdoc->lpHeadObj = lpobj->lpNextObj;

    if (lpdoc->lpTailObj == lpobj)
        lpdoc->lpTailObj = lpobj->lpPrevObj;       

    if (lpobj->lpPrevObj)
        lpobj->lpPrevObj->lpNextObj = lpobj->lpNextObj;

    if (lpobj->lpNextObj)   
        lpobj->lpNextObj->lpPrevObj = lpobj->lpPrevObj;     

    lpobj->lhclientdoc = NULL;
}