/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

#ifndef _DDEMLIMP_HXX
#define _DDEMLIMP_HXX

#include <bf_svtools/bf_solar.h>


#include <string.h>
#include <ctype.h>
#include "ddemlos2.h"

namespace binfilter
{

#define DDEMLSERVICETABLE_INISIZE   8

// Bezeichner der systemglobalen DDEML-Tabelle
#define DDEMLDATA   "\\SHAREMEM\\OV_DDEML.DAT"

// vorlaeufig konstante Tabellengroessen
#define CONVTABLECOUNT      2048    /* max count conversations */
#define TRANSTABLECOUNT     2048    /*           transactions  */
#define DDEMLAPPCOUNT       16      /* max count simultaniously running */
                                    /* ddeml (StarDivision) applications */

#define ST_TERMACKREC       0x8000  /* wird im Conversationhandle gesetzt, */
                                    /* wenn die Partner-App DDE_TERMINATE */
                                    /* bestaetigt hat */

#define XST_TIMEOUT         17      /* Trans. hat Timeout ueberschritten */
#define XST_WAITING_ACK     18      /* Trans. wartet auf Acknowledge */
#define XST_WAITING_ADVDATA 19      /* Trans. wartet auf Advise-Daten */
#define XST_WAITING_REQDATA 20      /* Trans. wartet auf angeforderte Daten */


/* User-Flags DDESTRUCT */
#define IMP_HDATAAPPOWNED   0x8000

#define CONVLISTNAME        "DdeConvListId"

#define XTYPF_MASK (XTYPF_NOBLOCK | XTYPF_NODATA | XTYPF_ACKREQ)

//
// DDEML-Messages; werden nur an registrierte DDEML-Apps gesendet
//

// Msg: WM_DDEML_REGISTER
// Empfaenger: wird allen DDEML-Applikationen nach Registrierung
// eines neuen Services gesendet
// Params: nPar1: hszBaseServName
//         nPar2: hszInstServName
#define WM_DDEML_REGISTER               WM_USER+1

// Msg: WM_DDEML_UNREGISTER
// Empfaenger: wird allen DDEML-Applikationen nach Deregistrierung
// eines Services gesendet
// Params: nPar1: hszBaseServName
//         nPar2: hszInstServName
#define WM_DDEML_UNREGISTER             WM_USER+2

//
//
//

struct ImpHCONV
{
    HCONV       hConvPartner;
    HSZ         hszPartner;     // Name of partner application
    HSZ         hszServiceReq;  // Service name
    HSZ         hszTopic;       // Topic name
    USHORT      nStatus;        // ST_* of conversation
    HCONVLIST   hConvList;      // ConvListId , wenn in ConvList
    CONVCONTEXT aConvContext;   // Conversation context

    // private
    HWND        hWndThis;       // 0 == Handle not used
    HWND        hWndPartner;
    PID         pidOwner;       // PID des DdeManagers, der
                                // den Conv-Handle erzeugt hat.
    USHORT      nPrevHCONV;     // 0 == no previous hConv or not in list
    USHORT      nNextHCONV;     // 0 == no next hconv or not in list
};

struct Transaction
{
    HSZ         hszItem;        // Item name
    USHORT      nFormat;        // Data format
    USHORT      nType;          // Transaction type (XTYP_*)
                                // XTYP_ADVREQ [|XTYPF_NODATA] == Advise-Loop
                                //             [|XTYPF_ACKREQ]
                                // XTYP_EXECUTE == laufendes Execute
                                // XTYP_REQUEST
                                // XTYP_POKE
                                // XTYP_ADVSTOP
                                // XTYP_ADVSTART
    USHORT      nConvst;        // Conversation state (XST_*)
                                // 0 == idle
                                // XST_REQSENT (fuer XTYP_ADVREQ)
                                // XST_TIMEOUT (fuer alle Typen!)
                                // XST_WAITING (alle ausser XTYP_ADVREQ)
    USHORT      nLastError;     // last err in transaction
    ULONG       nUser;          // Userhandle
    // private
    HCONV       hConvOwner;     // 0 == Transaction not used
};


struct ImpWndProcParams
{
    HWND        hWndReceiver;
    MPARAM      nPar1;
    MPARAM      nPar2;
};

struct ImpService
{
    HSZ         hBaseServName;  // Basis-Name des Service
    HSZ         hInstServName;  // Basis-Name + DDEML-Server-HWND der App
};

class ImpDdeMgr;

// Daten eines Conversation-Windows
struct ImpConvWndData
{
    ImpDdeMgr*  pThis;
    USHORT      nRefCount;  // Zahl Conversations auf diesem Window
};


// systemglobale Daten der Library (liegen in named shared memory)
struct ImpDdeMgrData
{
    ULONG       nTotalSize;
    ULONG       nOffsAppTable;
    ULONG       nOffsConvTable;
    ULONG       nOffsTransTable;
    USHORT      nMaxAppCount;
    USHORT      nMaxConvCount;
    USHORT      nMaxTransCount;
    USHORT      nLastErr;
    USHORT      nReserved;
    USHORT      nCurTransCount;
    USHORT      nCurConvCount;
    HWND        aAppTable[ 1 ];     // fuer Broadcast-Messages
    ImpHCONV    aConvTable[ 1 ];
    Transaction aTransTable[ 1 ];
};



class ImpDdeMgr
{
    friend MRESULT EXPENTRY ConvWndProc(HWND hWnd,ULONG nMsg,MPARAM nPar1,MPARAM nPar2);
    friend MRESULT EXPENTRY ServerWndProc(HWND hWnd,ULONG nMsg,MPARAM nPar1,MPARAM nPar2);
    friend void ImpWriteDdeStatus(char*,char*);
    friend void ImpAddHSZ( HSZ, String& );

    static PSZ      AllocAtomName( ATOM hString, ULONG& rBufLen );
    static PDDESTRUCT MakeDDEObject( HWND hwnd, ATOM hItemName,
        USHORT fsStatus, USHORT usFormat, PVOID pabData, ULONG usDataLen );
    static APIRET   AllocNamedSharedMem( PPVOID ppBaseAddress, PSZ pName,
        ULONG nElementSize, ULONG nElementCount );

    HWND            hWndServer;
    PID             pidThis;
    PFNCALLBACK     pCallback;
    ULONG           nTransactFilter;
    CONVCONTEXT     aDefaultContext;
    ImpDdeMgrData*  pData;
    ImpService*     pServices;
    USHORT          nServiceCount;

    ImpHCONV*       pConvTable;         // liegt in pData (nicht deleten!)
    Transaction*    pTransTable;        // liegt in pData (nicht deleten!)
    HWND*           pAppTable;          // liegt in pData (nicht deleten!)

    static ImpHCONV*    GetConvTable( ImpDdeMgrData* );
    static Transaction* GetTransTable( ImpDdeMgrData* );
    static HWND*        GetAppTable( ImpDdeMgrData* );


    static HWND     NextFrameWin( HENUM hEnum );
    void            CreateServerWnd();
    void            DestroyServerWnd();
    HWND            CreateConversationWnd();
    // Fktn. duerfen nur fuer HCONVs aufgerufen werden, die
    // in der eigenen Applikation erzeugt wurden
    static void     DestroyConversationWnd( HWND hWndConv );
    static USHORT   GetConversationWndRefCount( HWND hWndConv );
    static USHORT   IncConversationWndRefCount( HWND hWndConv );

    MRESULT         SrvWndProc(HWND hWnd,ULONG nMsg,MPARAM nPar1,MPARAM nPar2);
    MRESULT         ConvWndProc(HWND hWnd,ULONG nMsg,MPARAM nPar1,MPARAM nPar2);
    void            RegisterDDEMLApp();
    void            UnregisterDDEMLApp();
    void            CleanUp();
    ImpDdeMgrData*  InitAll();
    static BOOL     MyWinDdePostMsg( HWND, HWND, USHORT, PDDESTRUCT, ULONG );
    void            MyInitiateDde( HWND hWndServer, HWND hWndClient,
                        HSZ hszService, HSZ hszTopic, CONVCONTEXT* pCC );
    DDEINIT*        CreateDDEInitData( HWND hWndDest, HSZ hszService,
                        HSZ hszTopic, CONVCONTEXT* pCC );
    // wenn pDDEData==0, muss pCC gesetzt sein
    HCONV           ConnectWithClient( HWND hWndClient, HSZ hszPartner,
                        HSZ hszService, HSZ hszTopic, BOOL bSameInst,
                        DDEINIT* pDDEData, CONVCONTEXT* pCC = 0);

    HCONV           CheckIncoming( ImpWndProcParams*, ULONG nTransMask,
                        HSZ& rhszItem );
    // fuer Serverbetrieb. Ruft Callback-Fkt fuer alle offenen Advises
    // auf, deren Owner der uebergebene HCONV ist.
    // bFreeTransactions==TRUE: loescht die Transaktionen
    // gibt Anzahl der getrennten Transaktionen zurueck
    USHORT          SendUnadvises( HCONV hConv,
                       USHORT nFormat, // 0==alle
                       BOOL bFreeTransactions );

    BOOL            WaitTransState(
                        Transaction* pTrans, ULONG nTransId,
                        USHORT nNewState,
                        ULONG nTimeout );

    // DDEML ruft Callback mit XTYP_CONNECT-Transaction nur auf,
    // wenn die App den angeforderten Service registriert hat
    // Standardeinstellung: TRUE
    BOOL            bServFilterOn;

    // Fehlercode muss noch systemglobal werden (Atom o. ae.)
    static USHORT   nLastErrInstance; // wenn 0, dann gilt globaler Fehlercode

    static ImpDdeMgrData* AccessMgrData();

    static HCONV    CreateConvHandle( ImpDdeMgrData* pBase,
                        PID pidOwner,
                        HWND hWndThis, HWND hWndPartner,
                        HSZ hszPartner, HSZ hszServiceReq, HSZ hszTopic,
                        HCONV hPrevHCONV = 0 );

    static HCONV    IsConvHandleAvailable( ImpDdeMgrData* pBase );
    static HCONV    GetConvHandle( ImpDdeMgrData* pBase,
                        HWND hWndThis, HWND hWndPartner );
    static void     FreeConvHandle( ImpDdeMgrData*, HCONV,
                        BOOL bDestroyHWndThis = TRUE );

    static ULONG    CreateTransaction( ImpDdeMgrData* pBase,
                        HCONV hOwner, HSZ hszItem, USHORT nFormat,
                        USHORT nTransactionType );
    static ULONG    GetTransaction( ImpDdeMgrData* pBase,
                        HCONV hOwner, HSZ hszItem, USHORT nFormat );

    static void     FreeTransaction( ImpDdeMgrData*, ULONG nTransId );

    BOOL            DisconnectAll();
    // Transaktionen muessen _vor_ den Konversationen geloescht werden!
    static void     FreeTransactions( ImpDdeMgrData*, HWND hWndThis,
                        HWND hWndPartner );
    static void     FreeTransactions( ImpDdeMgrData*, HCONV hConvOwner );

    static void     FreeConversations( ImpDdeMgrData*,HWND hWndThis,
                        HWND hWndPartner );

    ImpService*     GetService( HSZ hszService );
    ImpService*     PutService( HSZ hszService );
    void            BroadcastService( ImpService*, BOOL bRegistered );

    // rh: Startposition(!) & gefundener Handle
    static ImpHCONV* GetFirstServer( ImpDdeMgrData*, HCONVLIST, HCONV& rh);
    static ImpHCONV* GetLastServer( ImpDdeMgrData*, HCONVLIST, HCONV& );
    static BOOL     CheckConvListId( HCONVLIST hConvListId );

    BOOL            IsSameInstance( HWND hWnd );
    HSZ             GetAppName( HWND hWnd );


    // Transactions
    MRESULT         DdeAck( ImpWndProcParams* pParams );
    MRESULT         DdeAdvise( ImpWndProcParams* pParams );
    MRESULT         DdeData( ImpWndProcParams* pParams );
    MRESULT         DdeExecute( ImpWndProcParams* pParams );
    MRESULT         DdeInitiate( ImpWndProcParams* pParams );
    MRESULT         DdeInitiateAck( ImpWndProcParams* pParams );
    MRESULT         DdePoke( ImpWndProcParams* pParams );
    MRESULT         DdeRequest( ImpWndProcParams* pParams );
    MRESULT         DdeTerminate( ImpWndProcParams* pParams );
    MRESULT         DdeUnadvise( ImpWndProcParams* pParams );
    MRESULT         DdeRegister( ImpWndProcParams* pParams );
    MRESULT         DdeUnregister( ImpWndProcParams* pParams );
    MRESULT         DdeTimeout( ImpWndProcParams* pParams );

    HDDEDATA    Callback(
        USHORT      nTransactionType,
        USHORT      nClipboardFormat,
        HCONV       hConversationHandle,
        HSZ         hsz1,
        HSZ         hsz2,
        HDDEDATA    hData,
        ULONG       nData1,
        ULONG       nData2 );

    HCONV       DdeConnectImp( HSZ hszService,HSZ hszTopic,CONVCONTEXT* pCC);

    // connection data
    HCONV           hCurConv;       // wird im DdeInitiateAck gesetzt
    HCONVLIST       hCurListId;     // fuer DdeConnectList
    USHORT          nPrevConv;      // .... "" ....
    BOOL            bListConnect;

    // synchr. transaction data
    BOOL            bInSyncTrans;
    ULONG           nSyncTransId;
    HDDEDATA        hSyncResponseData;
    ULONG           nSyncResponseMsg; // WM_DDE_ACK, WM_DDE_DATA, WM_TIMER
    // TRUE==nach Ende der synchronen Transaktion eine evtl. benutzte
    // asynchrone Transaktion beenden (typisch synchroner Request auf
    // Advise-Loop)
    BOOL            bSyncAbandonTrans;

public:
                    ImpDdeMgr();
                    ~ImpDdeMgr();

    USHORT          DdeInitialize( PFNCALLBACK pCallbackProc, ULONG nTransactionFilter );
    USHORT          DdeGetLastError();

    HCONV           DdeConnect( HSZ hszService, HSZ hszTopic, CONVCONTEXT* );
    HCONVLIST       DdeConnectList( HSZ hszService, HSZ hszTopic,
                        HCONVLIST hConvList, CONVCONTEXT* );
    static BOOL     DdeDisconnect( HCONV hConv );
    static BOOL     DdeDisconnectList( HCONVLIST hConvList );
    static HCONV    DdeReconnect(HCONV hConv);
    static HCONV    DdeQueryNextServer(HCONVLIST hConvList, HCONV hConvPrev);
    static USHORT   DdeQueryConvInfo(HCONV hConv, ULONG idTrans,CONVINFO* pCI);
    static BOOL     DdeSetUserHandle(HCONV hConv, ULONG id, ULONG hUser);
    BOOL            DdeAbandonTransaction( HCONV hConv, ULONG idTransaction);

    BOOL            DdePostAdvise( HSZ hszTopic, HSZ hszItem);
    BOOL            DdeEnableCallback( HCONV hConv, USHORT wCmd);

    HDDEDATA        DdeNameService( HSZ hszService, USHORT afCmd);

    static HDDEDATA DdeClientTransaction(void* pData, ULONG cbData,
                        HCONV hConv, HSZ hszItem, USHORT wFmt, USHORT wType,
                        ULONG dwTimeout, ULONG* pdwResult);

    // Data handles

    HDDEDATA        DdeCreateDataHandle( void* pSrc, ULONG cb, ULONG cbOff,
                        HSZ hszItem, USHORT wFmt, USHORT afCmd);
    static BYTE*    DdeAccessData(HDDEDATA hData, ULONG* pcbDataSize);
    static BOOL     DdeUnaccessData(HDDEDATA hData);
    static BOOL     DdeFreeDataHandle(HDDEDATA hData);
    static HDDEDATA DdeAddData(HDDEDATA hData,void* pSrc,ULONG cb,ULONG cbOff);
    static ULONG    DdeGetData(HDDEDATA hData,void* pDst,ULONG cbMax,ULONG cbOff);

    // String handles

    static HSZ      DdeCreateStringHandle( PSZ pStr, int iCodePage);
    static ULONG    DdeQueryString(HSZ hsz,PSZ pStr,ULONG cchMax,int iCPage);
    static BOOL     DdeFreeStringHandle( HSZ hsz );
    static BOOL     DdeKeepStringHandle( HSZ hsz );
    static int      DdeCmpStringHandles(HSZ hsz1, HSZ hsz2);

    // mit dieser Funktion kann geprueft werden, ob eine
    // Applikation schon eine DDEML-Instanz angelegt hat.
    // Die aktuelle Impl. unterstuetzt nur eine DDEML-Instanz
    // pro Applikation (wg. synchroner Transaktionen)
    static ImpDdeMgr* GetImpDdeMgrInstance( HWND hWnd );

    // gibt TRUE zurueck, wenn mind. ein lebender HCONV
    // von diesem DdeMgr erzeugt wurde
    BOOL            OwnsConversationHandles();
};

// static
inline ImpHCONV* ImpDdeMgr::GetConvTable( ImpDdeMgrData* pData )
{
    ImpHCONV* pRet;
    if( pData )
        pRet = (ImpHCONV*)((ULONG)(pData) + pData->nOffsConvTable);
    else
        pRet = 0;
    return pRet;
}

// static
inline Transaction* ImpDdeMgr::GetTransTable( ImpDdeMgrData* pData )
{
    Transaction* pRet;
    if( pData )
        pRet = (Transaction*)((ULONG)(pData) + pData->nOffsTransTable);
    else
        pRet = 0;
    return pRet;
}

// static
inline HWND* ImpDdeMgr::GetAppTable( ImpDdeMgrData* pData )
{
    HWND* pRet;
    if( pData )
        pRet = (HWND*)((ULONG)(pData) + pData->nOffsAppTable);
    else
        pRet = 0;
    return pRet;
}


}

#endif

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
