/* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   %       Copyright (C) 1992, by WATCOM Systems Inc. All rights     %
   %       reserved. No part of this software may be reproduced      %
   %       in any form or by any means - graphic, electronic or      %
   %       mechanical, including photocopying, recording, taping     %
   %       or information storage and retrieval systems - except     %
   %       with the written permission of WATCOM Systems Inc.        %
   %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  
   Date		By		Reason
   ----		--		------
   23-nov-93	Brent Williams	initial implementation
*/
#include <windows.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <ddeml.h>
#include <assert.h>
#include "weditcw.h"
#include "wedit.h"

static	HCONV	    hConv = 0;
static	DWORD	    idInstance = 0L;
static	HINSTANCE   hInstance = NULL;
static	BOOL	    bDdemlInitialized = FALSE;
static	BOOL	    bConnected = FALSE;
static	BOOL	    bAppSpawned = FALSE;
static	FARPROC	    lpDdeProc;

void doReset( void )
{
    // reset for another connect
    hConv = NULL;
    bDdemlInitialized = FALSE;
    bConnected = FALSE;
    bAppSpawned = FALSE;
    FreeProcInstance( lpDdeProc );
}

HDDEDATA __export FAR PASCAL DdeCallback( UINT wType, UINT wFmt, HCONV hConv,
		    HSZ hsz1, HSZ hsz2, HDDEDATA hData, DWORD dwData1,
		    DWORD dwData2 )
{
    wFmt = wFmt;
    hConv = hConv;
    hData = hData;
    dwData1 = dwData1;
    dwData2 = dwData2;

    switch( wType ) {
	case XTYP_DISCONNECT:
	    // user may have killed vi, or maybe this is a response to
	    // our ddeuninitialize.  either way, reset for next connect
	    if( bConnected ) {
		doReset();
	    }
	    return( NULL );
    }
    return( NULL );
} 

int extern __export FAR PASCAL EDITConnect( void )
{
#ifdef __WINDOWS__
    char    *szProg = "CW.EXE";
#elif __NT__
    char    *szProg = "CW32.EXE";
#else
    #error Unsupport operating system.
#endif

    char    *szService = "Codewright";
    char    *szTopic = "CurrentBuffer";
    HSZ	    hszService, hszTopic;
    int	    rc = 0;

    if( bConnected ) {
	return( TRUE );
    }

    // initialize our idInstance in ddeml
    if( !bDdemlInitialized ) {
	lpDdeProc = MakeProcInstance( (FARPROC) DdeCallback, hInstance );
	if( DdeInitialize( &idInstance, (PFNCALLBACK) lpDdeProc,
			   APPCMD_CLIENTONLY, 0L ) != DMLERR_NO_ERROR ) {
	    return( FALSE );
	}
	bDdemlInitialized = TRUE;
    }

    // get handles to access strings
    hszService = DdeCreateStringHandle( idInstance, szService, CP_WINANSI );
    hszTopic= DdeCreateStringHandle( idInstance, szTopic, CP_WINANSI );

    // attempt connection
    hConv = DdeConnect( idInstance, hszService, hszTopic,
    			(PCONVCONTEXT) NULL );
    if( hConv == 0 ) {
	// run editor (magically grabs focus)
	// DEBUGGERS NOTE: if debugging through here, give cw a moment
	//  		   to load up as it won't respond to our
	//		   DDEConnect right away
	rc = WinExec( szProg, SW_RESTORE );
	if( rc >= 32 ) {
	    // editor is up - try again (otherwise give up)
	    hConv = DdeConnect( idInstance, hszService, hszTopic,
				(PCONVCONTEXT) NULL );
	}
    }

    DdeFreeStringHandle( idInstance, hszService );
    DdeFreeStringHandle( idInstance, hszTopic );
    
    if( hConv == 0 ) {
	return( FALSE );
    }

    bConnected = TRUE;

    if( rc > 32 ) {
	// we spawned & connected ok
	bAppSpawned = TRUE;
    }

    return( TRUE );
}

BOOL doExecute( char *szCommand )
{
    HDDEDATA	hddeData;

    hddeData = DdeClientTransaction( szCommand, strlen( szCommand ) + 1,
    				     hConv, NULL, CF_TEXT, XTYP_EXECUTE,
				     5000, NULL );
    return( hddeData != 0 );
}

int extern __export FAR PASCAL EDITFile( LPSTR szFile, LPSTR szHelpFile )
{
    char	szCommand[ 128 ];
    BOOL	rc;

    if( !bConnected ) {
	return( FALSE );
    }

    if( strlen( szFile ) > 115 ) {
	return( FALSE );
    }
    sprintf( szCommand, "BufEditFile %s", szFile );
    rc = doExecute( szCommand );

    if( szHelpFile == NULL || strlen( szHelpFile ) > 109 ) {
	return( rc );
    }
    sprintf( szCommand, "CWHelpDefaultName %s", szHelpFile );
    rc |= doExecute( szCommand );

    return( rc );
}

int extern __export FAR PASCAL EDITLocateError( long lRow, int iCol,
				    int iLen, int idResource, LPSTR szErrmsg )
{
    char	szCommand[ 100 ];
    int		len;
    BOOL	rc;

    if( !bConnected ) {
	return( FALSE );
    }

    sprintf( szCommand, "MovAbs %ld %d", lRow, iCol );
    rc = doExecute( szCommand );
#if 0
    rc |= doExecute( "MarkBeginSel SELECTION_EXCLUSIVE" );
    sprintf( szCommand, "MovRight %d", iLen );
    rc |= doExecute( szCommand );
    rc |= doExecute( "MarkEndSel" );
#endif

    if( szErrmsg ) {
	sprintf( szCommand, "MsgError %.*s",
		 sizeof( szCommand ) - 10, szErrmsg );
	rc |= doExecute( szCommand );
    }

    if( idResource != 0 ) {
	idResource += 20000;
	sprintf( szCommand, "CWHelp %d HELP_CONTEXT", idResource );
	rc |= doExecute( szCommand );
    }

    return( rc );
}

int extern __export FAR PASCAL EDITLocate( long lRow, int iCol, int iLen )
{
    return( EDITLocateError( lRow, iCol, iLen, 0, NULL ) );
}

int extern __export FAR PASCAL EDITShowWindow( show_method iCmdShow )
{
    char	szCommand[ 80 ];
    BOOL	rc;
    HDDEDATA	hData;
    HSZ		hszItem;
    DWORD	cbDataLen;
    char	*szData;
    DWORD	dwFlags;
    
    if( !bConnected ) {
	return( FALSE );
    }

    // ensure iCmdShow is SW_RESTORE or SW_MINIMIZE only
    if( iCmdShow == EDIT_RESTORE || iCmdShow == EDIT_SHOWNORMAL ) {
	iCmdShow = EDIT_RESTORE;
    } else if( iCmdShow != EDIT_MINIMIZE ) {
	return( FALSE );
    }

    // get system flags
    rc = doExecute( "SysQFlags" );
    if( rc == FALSE ) {
	return( rc );
    }
    hszItem = DdeCreateStringHandle( idInstance, "ExecReturn", CP_WINANSI );
    hData = DdeClientTransaction( NULL, 0, hConv, hszItem, CF_TEXT,
    			          XTYP_REQUEST, 5000, NULL );
    DdeFreeStringHandle( idInstance, hszItem );
    if( hData == 0 ) {
	return( FALSE );
    }

    szData = DdeAccessData( hData, &cbDataLen );
    dwFlags = atol( szData );
    DdeUnaccessData( hData );

    // change minimize bit of system flags
    if( iCmdShow == EDIT_MINIMIZE ) {
	dwFlags |= SYSFLAG_WIN_MINIMIZED;
    } else {
	dwFlags &= ~SYSFLAG_WIN_MINIMIZED;
    }

    // store results
    sprintf( szCommand, "SysSetFlags %ld", dwFlags );
    return( doExecute( szCommand ) );
}

BOOL CALLBACK EnumWnd( HWND hwnd, LPARAM lParam )
{
    char    *szWantClass = CW_CLASS;
    char    szThisClass[ sizeof( CW_CLASS ) ];

    if( GetClassName( hwnd, szThisClass, sizeof( szThisClass ) ) + 1 !=
	    sizeof( szThisClass ) ) {
	// if not correct length, definitely no match
	return( TRUE );
    }
    if( strcmp( szThisClass, szWantClass ) == 0 ) {
	// gotcha - store handle & stop enumeration
	*( (HWND *)lParam ) = hwnd;
	return( FALSE );
    }
    return( TRUE );
}

int extern __export FAR PASCAL EDITDisconnect( void )
{
    DWORD	idTransaction;
    WNDENUMPROC lpEnumWnd;
    HWND	hwndCodewright = NULL;


    if( !bConnected ) {
	return( TRUE );
    }

    // kill our dde connection
    DdeUninitialize( idInstance );
    idInstance = 0;

    if( bAppSpawned ) {
	// look for a window with class name szClassName
	lpEnumWnd = (FONTENUMPROC) MakeProcInstance( (FARPROC) EnumWnd,
						     hInstance );
	EnumWindows( lpEnumWnd, (LPARAM)&hwndCodewright );
	FreeProcInstance( (FARPROC) lpEnumWnd );
    
	if( hwndCodewright != NULL ) {
	    // found a window called CodeWright - make a half-hearted
	    // attempt at killing it (user could possibly Cancel)
	    SendMessage( hwndCodewright, WM_CLOSE, 0, 0L );
	}
    }

    // fry rest of our flags, all ready for next connect
    doReset();

    return( TRUE );
}

int FAR PASCAL LibMain( HANDLE hInst, WORD wDataSeg, WORD wHeapSize,
			LPSTR lpszCmdLine )
{
    wDataSeg = wDataSeg;
    wHeapSize = wHeapSize;
    lpszCmdLine = lpszCmdLine;

    hInstance = hInst;

    return( 1 );
}

