/****************************************************************************/
/*  Projekt:  EuroSpeed                                                     */
/*	Programm: Sample														*/
/*	Datei:	  DDE.C															*/
/*                                                                          */
/*	Funktionen: TestDDEInititialize()										*/
/*				TestDDEConnect()											*/
/*				Clip2Cmd()													*/
/*				DdeCallback()												*/
/*				DDETransAktConnect()										*/
/*				DDETransAktSend()											*/
/*				DDETransAktDisconn()										*/
/*				ConnectStateProc()											*/
/*				SaveStateProc()												*/
/*				DisconnStateProc()											*/
/*				ServerStateProc()											*/
/*				Listen()													*/
/*																			*/
/****************************************************************************/
/*                                                                          */
/*	Torsten Schulz	 -	 Acotec GmbH Berlin   -    17.12.1992				*/
/*                                                                          */
/****************************************************************************/

/*
 * Includes
 */

#include <Windows.H>
#include <CommDlg.H>
#include <DdeMl.H>
#include "Sample.H"
#include "FtDdeMl.H"
#include "DDE.H"

/*
 * Defines
 */

#define _WINDOWS
#define NOCOMM

/*
 * Functions
 */

/****************************************************************************/
/* TestDDEInitialize: Initialisierung der DDE Verbindung					*/
/* return:			  Zeiger zur DDE Prozedur und im Fehlerfall NULL		*/
/****************************************************************************/
FARPROC TestDDEInititialize( HWND hWnd )
{
	FARPROC lpDdeProc;
	DWORD   dwCallbackFilter =  APPCMD_CLIENTONLY   |
								CBF_FAIL_EXECUTES   |
								CBF_FAIL_POKES;

	lpDdeProc = MakeProcInstance( (FARPROC) DdeCallback, hInst );
	if ( lpDdeProc )
	{
		if (!DdeInitialize ( &dwDdeInst,
							 (PFNCALLBACK)  lpDdeProc,
							 dwCallbackFilter,
							 0L
							 ) == DMLERR_NO_ERROR )
		{
			FreeProcInstance( lpDdeProc );
			lpDdeProc = NULL;
			ErrorBox( hWnd, IDERR_DDEINIT );
		}
	}

	return lpDdeProc;
}


/****************************************************************************/
/* TestDDEConnect: Setzt die ClipBoardformate; baut DDE Verbindung auf		*/
/* return:		   Konversationshandle und im Fehlerfall NULL				*/
/****************************************************************************/
DWORD TestDDEConnect ( HWND hWnd )
{
	HSZ         hszService,
				hszTopic;
	char        szBuffer[ MAX_TEXT ];
	HCONV		hConv;
	DWORD		dwDummy;
	HDDEDATA	hDdeDataOut;
	HSZ 		hszItem;
	WORD		nIndex;

	/*
	 * Zur Identifizierung der Pokes muss aus dem ClipBoardformat
	 * der Itemname gewonnen werden. Dazu wird die Identifizierungs-
	 * tabelle (aFormats) gefuellt.
	 */
	aFormats[0].atom = CF_TEXT;					/* exception - predefined. */
	for (nIndex = 1; nIndex < MAX_FORMATS; nIndex++)
	{
		aFormats[nIndex].atom = RegisterClipboardFormat(aFormats[nIndex].sz);
		if ( aFormats[nIndex].atom < 0xC000 )
		{
			ErrorBox(hWnd, IDERR_DEFAULT );
			return NULL;
		}
	}

	/*
	 * Verbindung aufbauen
	 */
	if ( dwDdeInst )
	{

		/* Service */
		hszService = DdeCreateStringHandle( dwDdeInst, SZDDE_SERVICE_NAME, CP_WINANSI );
		if ( !hszService )
		{
			ErrorProtokoll( hWnd, dwDdeInst );
			return NULL;
		}

		/* Topic */
		wsprintf ( szBuffer, "%s%lx", (LPSTR) SZDDE_TOPIC_CONNECTION, dwDdeInst);
		hszTopic   = DdeCreateStringHandle( dwDdeInst, szBuffer, CP_WINANSI );
		if ( !hszTopic )
		{
			ErrorProtokoll( hWnd, dwDdeInst );
			return NULL;
		}

		/* Verbindung */
		hConv = DdeConnect( dwDdeInst, hszService, hszTopic, NULL );
		if ( !hConv )
		{
			ErrorProtokoll( hWnd, dwDdeInst );
			return NULL;
		}
	}

	/*
	 * Fuer jedes Hotlink-Item wird eine Konversation eroeffnet
	 */
	for (nIndex=0; nIndex < MAX_HOTITEMS; nIndex++)
	{

		hszItem = DdeCreateStringHandle (dwDdeInst, HotItems[nIndex], CP_WINANSI);
		if (!hszItem)
		{
			ErrorProtokoll( hWnd, dwDdeInst );
			return NULL;
		}
		hDdeDataOut = DdeClientTransaction( &dwDummy,
											sizeof (DWORD),
											hConv,
											hszItem,
											aFormats[HotFormats[nIndex]].atom,
											XTYP_ADVSTART,
											TIMEOUT_ASYNC,
											NULL );
		if (!hDdeDataOut)
		{
			ErrorProtokoll( hWnd, dwDdeInst );
			return NULL;
		}
	}
	return hConv;

}


/*--------------------------------------------------------------------------*/
/* Clip2Cmd: Bildet das ClipBoardFormat auf den Kommandoindex ab.			*/
/* return:	 Kommandoindex
/*--------------------------------------------------------------------------*/
int Clip2Cmd( WORD wClipFmt )
{
  int i;

  for (i=0; i<MAX_FORMATS; i++)
  {
	if (aFormats[i].atom == wClipFmt) /* Tabelle "aFormats" ist in DDE.H	*/
	{								  /* definiert und in der Funktion		*/
	  return i; 					  /* "TestDDEConnect" gefuellt. 		*/
    }
  }
  return 0;
}

/****************************************************************************/
/* DdeCallback                                                              */
/****************************************************************************/
HDDEDATA EXPENTRY DdeCallback ( WORD        wTransType,
								WORD        wClipFmt,
								HCONV       hConv,
								HSZ         hsz1,
								HSZ         hsz2,
								HDDEDATA    hDataIn,
								DWORD       dwData1,
								DWORD       dwData2     )

{
	HDDEDATA				hDataOut;
	int 					iCmd;
	connect_state_t far		*lpConnectState;
	save_state_t	far		*lpSaveState;
	disconnect_state_t far	*lpDisconnState;
	server_state_t far		*lpServerState;
	DWORD					dwDataLen;

	hsz1	= hsz1;    hsz2    = hsz2;
	dwData1 = dwData1; dwData2 = dwData2;

	switch( wTransType )
	{
	case XTYP_ADVDATA:

		if (hDataIn)											/* hot link */
		{
			iCmd = Clip2Cmd (wClipFmt); 	 /* Clipformat in Kommandoindex */
			switch ( iCmd )
			{
			case C_CONNECT_STATE:
				lpConnectState = (connect_state_t far *)
								 DdeAccessData (hDataIn, &dwDataLen);

				if (lpConnectState )
				{
					ConnectStateProc( hWndGlob, hConv, lpConnectState );
				}
				else
				{
					ErrorProtokoll( hWndGlob, dwDdeInst );
					break;
				}
				DdeUnaccessData (hDataIn);

				break;

			case C_SAVE_STATE:
				lpSaveState= (save_state_t far *)
							 DdeAccessData (hDataIn, &dwDataLen);
				if (lpSaveState )
				{
					SaveStateProc( hWndGlob, hConv, lpSaveState );
				}
				else
				{
					ErrorProtokoll( hWndGlob, dwDdeInst );
					break;
				}
				DdeUnaccessData (hDataIn);
				break;
	
			case C_DISCONNECT_STATE:
				lpDisconnState= (disconnect_state_t far *)
							 DdeAccessData (hDataIn, &dwDataLen);
				if (lpDisconnState )
				{
					DisconnStateProc( hWndGlob, lpDisconnState );
				}
				else
				{
					ErrorProtokoll( hWndGlob, dwDdeInst );
					break;
				}
				DdeUnaccessData (hDataIn);
				break;

			case C_SERVER_STATE:
				lpServerState= (server_state_t far *)
								DdeAccessData (hDataIn, &dwDataLen);
				if (lpServerState )
				{
					ServerStateProc( hWndGlob, lpServerState );
				}
				else
				{
					ErrorProtokoll( hWndGlob, dwDdeInst );
					break;
				}
				DdeUnaccessData (hDataIn);
				break;

			default:
				break;

			}
		}

	}
	return hDataOut;
}

/****************************************************************************/
/* DDETransAktConnect: Loest Transaktion "ConnectConnect" aus				*/
/* use global:		   dwDdeInst											*/
/* return:			   Ok oder nicht Ok 									*/
/****************************************************************************/
BOOL DDETransAktConnect( HWND hWnd, HCONV hConv, LPSTR lpNumber	)
{
	HSZ 					hszItem;
	HDDEDATA				hDdeDataOut;
	HANDLE					hConnect;
	connect_req_t far		*lpConnect;			  /* definiert in FtDdeMl.H */

	hszItem = DdeCreateStringHandle( dwDdeInst, SZDDE_ITEM_CONNECT_REQ, CP_WINANSI );

	/* Speicher fuer die Struktur */
	hConnect = GlobalAlloc (GMEM_ZEROINIT | GMEM_MOVEABLE,
							sizeof (connect_req_t));
	if (!hConnect)
	{
		ErrorBox( hWnd, IDERR_NOMEM );
		return FALSE;
	}
	lpConnect = (connect_req_t far *) GlobalLock( hConnect );

	/* Struktur fuellen */
	lstrcpy (lpConnect->szCallingNo, (LPSTR) "");
	lstrcpy (lpConnect->szCalledNo1, lpNumber);
	lstrcpy (lpConnect->szCalledNo2, (LPSTR) "");
	lstrcpy (lpConnect->szApplName,  (LPSTR) "!K");
	lpConnect->ubTimeout = 20;
	lpConnect->ubWinSize = 8;
	lpConnect->dwBlkSize = 2048;

	hDdeDataOut = DdeClientTransaction( lpConnect,
										sizeof (connect_req_t),
										hConv,
										hszItem,
										aFormats[C_CONNECT_REQ].atom,
										XTYP_POKE,
										TIMEOUT_ASYNC,
										NULL );

	GlobalUnlock (hConnect);
	GlobalFree (hConnect);

	if ( hDdeDataOut )
	{
		return TRUE;
	}
	else
	{
		ErrorProtokoll( hWnd, dwDdeInst);
		return FALSE;
	}
}

/****************************************************************************/
/* DDETransAktSend: Loest Transaktion "SaveSave" aus						*/
/* use global:		dwDdeInst												*/
/* return:			Ok oder nicht Ok										*/
/****************************************************************************/
BOOL DDETransAktSend( HWND hWnd, HCONV hConv, LPSTR lpFile	)
{
	OFSTRUCT				of;
	int						hFile	   = 0;
	DWORD					dwFileSize;
	HSZ 					hszItem;
	HDDEDATA				hDdeDataOut;
	HANDLE					hSend;
	save_req_t far 			*lpSend;			  /* definiert in FtDdeMl.H */

	hszItem = DdeCreateStringHandle( dwDdeInst, SZDDE_ITEM_SAVE_REQ, CP_WINANSI );

	/* Speicher fuer die Struktur */
	hSend = GlobalAlloc (GMEM_ZEROINIT | GMEM_MOVEABLE,
						 sizeof (save_req_t));
	if (!hSend)
	{
		ErrorBox( hWnd, IDERR_NOMEM );
		return FALSE;
	}
	lpSend = (save_req_t far *) GlobalLock( hSend );

	/* Dateigroesse berechnen */
	hFile = OpenFile( lpFile , &of, OF_READ | OF_SHARE_EXCLUSIVE );
	if( hFile != -1 )
	{
		dwFileSize = _llseek( hFile, 0, 2 );    /* 2: auf End of file (EOF) */
		_llseek( hFile, 0, 0 );                 /* 0: auf Dateianfang       */
		_lclose ( hFile );
	}
	else
	{
		ErrorBox( hWnd, IDERR_NOFILE );
		return FALSE;
	}

	/* Struktur fuellen */
	lpSend->dwFileLen = dwFileSize;
	lstrcpy (lpSend->szLocalFileName, lpFile);

	hDdeDataOut = DdeClientTransaction( lpSend,
										sizeof (save_req_t),
										hConv,
										hszItem,
										aFormats[C_SAVE_REQ].atom,
										XTYP_POKE,
										TIMEOUT_ASYNC,
										NULL );

	GlobalUnlock( hSend );
	GlobalFree( hSend );

	if ( hDdeDataOut )
	{
		return TRUE;
	}
	else
	{
		ErrorProtokoll( hWnd, dwDdeInst);
		return FALSE;
	}
}

/****************************************************************************/
/* DDETransAktDisconn: Loest Transaktion "DiscDisc" aus						*/
/* use global:		dwDdeInst												*/
/* return:			Ok oder nicht Ok										*/
/****************************************************************************/
BOOL DDETransAktDisconn( HWND hWnd, HCONV hConv	)
{
	HSZ 							hszItem;
	HDDEDATA						hDdeDataOut;
	HANDLE							hDisconn;
	disconnect_req_t far			*lpDisconn;	  /* definiert in FtDdeMl.H */

	hszItem = DdeCreateStringHandle( dwDdeInst, SZDDE_ITEM_DISCONNECT_REQ, CP_WINANSI );

	/* Speicher fuer die Struktur */
	hDisconn = GlobalAlloc (GMEM_ZEROINIT | GMEM_MOVEABLE,
						 sizeof (disconnect_req_t));
	if (!hDisconn)
	{
		ErrorBox( hWnd, IDERR_NOMEM );
		return FALSE;
	}
	lpDisconn = (disconnect_req_t far *) GlobalLock( hDisconn );

	hDdeDataOut = DdeClientTransaction( lpDisconn,
										sizeof (disconnect_req_t),
										hConv,
										hszItem,
										aFormats[C_DISCONNECT_REQ].atom,
										XTYP_POKE,
										TIMEOUT_ASYNC,
										NULL );

	GlobalUnlock( hDisconn );
	GlobalFree( hDisconn );

	if ( hDdeDataOut )
	{
		return TRUE;
	}
	else
	{
		ErrorProtokoll( hWnd, dwDdeInst);
		return FALSE;
	}
}

/****************************************************************************/
/* ConnectStateProc: Gibt das Ergebnis der Verbindungsanforderung aus		*/
/*					 und startet die Sendeaktion							*/
/****************************************************************************/
void ConnectStateProc( HWND 				hWnd,
					   HCONV				hConv,
					   connect_state_t far	*lpConnectState )
{
	char		szMessage[ MAX_TEXT ];

	HANDLE		hData;
	LPLOCAL		lpData;

	/*
	**	Wenn die Verbindung nicht aufgebaut werden konnte, wird eine 
	**	entsprechende Fehlermeldung ausgegeben werden.
	*/
	switch (lpConnectState->wState)
	{
		case CONNECT_STATE_SERVER_BUSY:
			WriteProtokoll( hWnd, (LPSTR) "Server busy", TRUE );
			break;

		case CONNECT_STATE_OK:
			/* Verbindungaufbau OK */
			hData = GetProp( hWnd, "hLocal" );
			lpData = (LPLOCAL) GlobalLock( hData );

			wsprintf( szMessage, "Verb: %s", lpData->szNumber );
			WriteProtokoll( hWnd, (LPSTR) szMessage, TRUE );

			DDETransAktSend( hWnd, hConv, (LPSTR) lpData->szFile );

			GlobalUnlock (hData);
			break;

		case CONNECT_STATE_REFUSE:
		case CONNECT_STATE_NOMEM:
		case CONNECT_STATE_NET_FAILURE:
		case CONNECT_STATE_ILL_REF:
		case CONNECT_STATE_NO_SERVICE:
		case CONNECT_STATE_WRONG_CID:
		case CONNECT_STATE_USED_CID:
		case CONNECT_STATE_NO_FREE_CHAN:
		case CONNECT_STATE_UNKNOWN_FAC:
		case CONNECT_STATE_FAC_REJECT:
		case CONNECT_STATE_CALL_BARRED:
		case CONNECT_STATE_ALL_CHAN_BUSY:
		case CONNECT_STATE_NEG_GBG:
		case CONNECT_STATE_CHECK_FAILED:
		case CONNECT_STATE_NO_REV_CHARGE:
		case CONNECT_STATE_RES1:
		case CONNECT_STATE_RES2:
		case CONNECT_STATE_WRONG_ADR:
		case CONNECT_STATE_CHANGED_NUMBER:
		case CONNECT_STATE_OUT_OF_ORDER:
		case CONNECT_STATE_NO_ANSWER:
		case CONNECT_STATE_USER_BUSY:
		case CONNECT_STATE_BARRED:
		case CONNECT_STATE_CALL_REJ:
		case CONNECT_STATE_NO_CAPACITY:
		case CONNECT_STATE_NET_REJ:
		case CONNECT_STATE_LOKAL_ERR:
		case CONNECT_STATE_REMOTE_ERR:
		case CONNECT_STATE_REMOTE_SUSP:
		case CONNECT_STATE_REMOTE_NOT_SUSP:
		case CONNECT_STATE_DISC_USER_INFO:
		case CONNECT_STATE_TIMEOUT:
		case CONNECT_STATE_SYNTAX_ERROR:
		case CONNECT_STATE_UNKNOWN_ERR:
		default:
			wsprintf( szMessage, "Verbindungsfehler: %X", lpConnectState->wState );
			WriteProtokoll( hWnd, (LPSTR) szMessage, TRUE );
			break;
	}
}


/****************************************************************************/
/* SaveStateProc: Gibt das Ergebnis des Verbindungsanforderung aus		*/
/*				 und startet die Sendeaktion							*/
/****************************************************************************/
void SaveStateProc( HWND				hWnd,
					HCONV				hConv,
					save_state_t far	*lpSaveState )
{
	char		szMessage[ MAX_TEXT ];

	HANDLE		hData;
	LPLOCAL		lpData;

	switch (lpSaveState->wState)
	{
		case SAVE_STATE_OK:
			hData = GetProp( hWnd, "hLocal" );
			lpData = (LPLOCAL) GlobalLock( hData );
			wsprintf( szMessage, "Send: %s", lpData->szFile );
			GlobalUnlock (hData);

			WriteProtokoll( hWnd, (LPSTR) szMessage, TRUE );
			DDETransAktDisconn( hWnd, hConv );

			break;

		case SAVE_STATE_FILE_EXISTS:
			WriteProtokoll( hWnd, (LPSTR) "Datei existiert", TRUE );
			DDETransAktDisconn( hWnd, hConv );
			break;

		case SAVE_STATE_ACCESS_VIOLATION:
			WriteProtokoll( hWnd, (LPSTR) "kein Zugriff", TRUE );
			DDETransAktDisconn( hWnd, hConv );
			break;

		case SAVE_STATE_IN_PROGRESS: /* wird Uebertragen */
			break;

		case SAVE_STATE_SERVER_BUSY:
		case SAVE_STATE_DIR_FULL:
		case SAVE_STATE_ILL_FILENAME:
		case SAVE_STATE_FILE_NOT_FOUND:
		case SAVE_STATE_NOMEM:
		case SAVE_STATE_REMOTE_ABORT:
		default:
			wsprintf( szMessage, "Sendefehler: %X", lpSaveState->wState );
			WriteProtokoll( hWnd, (LPSTR) szMessage, TRUE );
			break;
	}
}


/****************************************************************************/
/* DisconnStateProc: Gibt das Ergebnis des Verbindungsabbaus aus			*/
/****************************************************************************/
void DisconnStateProc( HWND						hWnd,
					   disconnect_state_t far	*lpDisconnState )
{
	char		szMessage[ MAX_TEXT ];

	HANDLE		hData;
	LPLOCAL		lpData;

	switch (lpDisconnState->wState)
	{
		case DISCONNECT_STATE_OK:
			hData = GetProp( hWnd, "hLocal" );
			lpData = (LPLOCAL) GlobalLock( hData );
			wsprintf( szMessage, "Disconn: %s", lpData->szNumber );
			GlobalUnlock (hData);
			WriteProtokoll( hWnd, (LPSTR) szMessage, TRUE );
			break;

		case DISCONNECT_STATE_ABORT:
		case DISCONNECT_STATE_NOMEM:
		case DISCONNECT_STATE_SERVER_BUSY:
		default:
			wsprintf( szMessage, "Abbaufehler: %X", lpDisconnState->wState );
			WriteProtokoll( hWnd, (LPSTR) szMessage, TRUE );
			break;
	}
}

/****************************************************************************/
/* ServerStateProc: Nur fuer eventuelle Reaktionen auf den Serverstatus		*/
/****************************************************************************/
void ServerStateProc( HWND					hWnd,
					  server_state_t far	*lpServerState )
{
	hWnd = hWnd;

	switch (lpServerState->ubState)
	{
		case SERVER_STATE_IDLE:
		case SERVER_STATE_ACCESS:
		case SERVER_STATE_FILE_RCV:
			break;

		default:
			break;
	}
}

/****************************************************************************/
/* Listen: Setzt die Applikation auf "Empfang"								*/
/* return: Erfolg oder nicht Erfolg 										*/
/****************************************************************************/
BOOL Listen( HWND hWnd, HCONV hConv )
{
	listen_req_t	ListenReq;
	HSZ 			hszItem;
	HDDEDATA		hDdeDataOut;

	hszItem = DdeCreateStringHandle( dwDdeInst,
									 SZDDE_ITEM_LISTEN_REQ,
									 CP_WINANSI);

	ListenReq.wFlag = ( M_LISTEN_CHECK_NUMBER );
	lstrcpy ((LPSTR)ListenReq.szTelNumberFileName, (LPSTR) "" );

	hDdeDataOut = DdeClientTransaction( (listen_req_t far *)&ListenReq,
										sizeof (listen_req_t),
										hConv,
										hszItem,
										aFormats[C_LISTEN_REQ].atom,
										XTYP_POKE,
										TIMEOUT_ASYNC,
										NULL);
	if ( !hDdeDataOut )
	{
		ErrorProtokoll( hWnd, dwDdeInst );
		return FALSE;
	}
	return TRUE;
}
